Files
oikos/web/node_modules/eslint/lib/rules/utils/char-source.js
dtoro d4d99a7473
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more
phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper,
compose/web image) must move to its own repo first so doc/layout
rewrites land once on a backend-only tree.

Change:
- New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/
  (updateURL repointed to oikos-web releases), compose/, own CI (web +
  desktop jobs), own deploy script (CI-green gate, TOCTOU guard,
  version-tagged images, prune-to-3), own webhook receiver on :9798 +
  launchd unit, own compose project publishing the same 8091:80.
- Cutover executed on mac-mini in order: oikos stack's web service
  stopped+removed, oikos-web project brought up on 8091; outer Caddy
  untouched (targets the published port) — serving + Authentik flow +
  /wails 404 quirk verified post-cutover.
- Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI
  workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install
  targets, the compose web service, oikos-web from deploy.sh's fallback
  prune list; wails + go-keyring dropped from go.mod, vendor synced.
- README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now
  point at the new repo; mbse + mascot design docs carry a path note.

Risk: production SPA serving depends on the new pipeline now; rollback
is versioned-image re-up of the old web service from a pre-split
checkout (port 8091). Desktop builds installed before the split still
check dtoro/oikos releases — one manual reinstall, noted in the
oikos-web release notes.

Verification: go vet, make test (race), make generate-check, golangci
(no new findings; baseline down 400→365); post-cutover curls —
localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
2026-08-15 22:27:52 +02:00

248 lines
5.8 KiB
JavaScript

/**
* @fileoverview Utility functions to locate the source text of each code unit in the value of a string literal or template token.
* @author Francesco Trotta
*/
"use strict";
/**
* Represents a code unit produced by the evaluation of a JavaScript common token like a string
* literal or template token.
*/
class CodeUnit {
constructor(start, source) {
this.start = start;
this.source = source;
}
get end() {
return this.start + this.length;
}
get length() {
return this.source.length;
}
}
/**
* An object used to keep track of the position in a source text where the next characters will be read.
*/
class TextReader {
constructor(source) {
this.source = source;
this.pos = 0;
}
/**
* Advances the reading position of the specified number of characters.
* @param {number} length Number of characters to advance.
* @returns {void}
*/
advance(length) {
this.pos += length;
}
/**
* Reads characters from the source.
* @param {number} [offset=0] The offset where reading starts, relative to the current position.
* @param {number} [length=1] Number of characters to read.
* @returns {string} A substring of source characters.
*/
read(offset = 0, length = 1) {
const start = offset + this.pos;
return this.source.slice(start, start + length);
}
}
const SIMPLE_ESCAPE_SEQUENCES = {
__proto__: null,
b: "\b",
f: "\f",
n: "\n",
r: "\r",
t: "\t",
v: "\v",
};
/**
* Reads a hex escape sequence.
* @param {TextReader} reader The reader should be positioned on the first hexadecimal digit.
* @param {number} length The number of hexadecimal digits.
* @returns {string} A code unit.
*/
function readHexSequence(reader, length) {
const str = reader.read(0, length);
const charCode = parseInt(str, 16);
reader.advance(length);
return String.fromCharCode(charCode);
}
/**
* Reads a Unicode escape sequence.
* @param {TextReader} reader The reader should be positioned after the "u".
* @returns {string} A code unit.
*/
function readUnicodeSequence(reader) {
const regExp = /\{(?<hexDigits>[\dA-F]+)\}/iuy;
regExp.lastIndex = reader.pos;
const match = regExp.exec(reader.source);
if (match) {
const codePoint = parseInt(match.groups.hexDigits, 16);
reader.pos = regExp.lastIndex;
return String.fromCodePoint(codePoint);
}
return readHexSequence(reader, 4);
}
/**
* Reads an octal escape sequence.
* @param {TextReader} reader The reader should be positioned after the first octal digit.
* @param {number} maxLength The maximum number of octal digits.
* @returns {string} A code unit.
*/
function readOctalSequence(reader, maxLength) {
const [octalStr] = reader.read(-1, maxLength).match(/^[0-7]+/u);
reader.advance(octalStr.length - 1);
const octal = parseInt(octalStr, 8);
return String.fromCharCode(octal);
}
/**
* Reads an escape sequence or line continuation.
* @param {TextReader} reader The reader should be positioned on the backslash.
* @returns {string} A string of zero, one or two code units.
*/
function readEscapeSequenceOrLineContinuation(reader) {
const char = reader.read(1);
reader.advance(2);
const unitChar = SIMPLE_ESCAPE_SEQUENCES[char];
if (unitChar) {
return unitChar;
}
switch (char) {
case "x":
return readHexSequence(reader, 2);
case "u":
return readUnicodeSequence(reader);
case "\r":
if (reader.read() === "\n") {
reader.advance(1);
}
// fallthrough
case "\n":
case "\u2028":
case "\u2029":
return "";
case "0":
case "1":
case "2":
case "3":
return readOctalSequence(reader, 3);
case "4":
case "5":
case "6":
case "7":
return readOctalSequence(reader, 2);
default:
return char;
}
}
/**
* Reads an escape sequence or line continuation and generates the respective `CodeUnit` elements.
* @param {TextReader} reader The reader should be positioned on the backslash.
* @returns {Generator<CodeUnit>} Zero, one or two `CodeUnit` elements.
*/
function* mapEscapeSequenceOrLineContinuation(reader) {
const start = reader.pos;
const str = readEscapeSequenceOrLineContinuation(reader);
const end = reader.pos;
const source = reader.source.slice(start, end);
switch (str.length) {
case 0:
break;
case 1:
yield new CodeUnit(start, source);
break;
default:
yield new CodeUnit(start, source);
yield new CodeUnit(start, source);
break;
}
}
/**
* Parses a string literal.
* @param {string} source The string literal to parse, including the delimiting quotes.
* @returns {CodeUnit[]} A list of code units produced by the string literal.
*/
function parseStringLiteral(source) {
const reader = new TextReader(source);
const quote = reader.read();
reader.advance(1);
const codeUnits = [];
for (;;) {
const char = reader.read();
if (char === quote) {
break;
}
if (char === "\\") {
codeUnits.push(...mapEscapeSequenceOrLineContinuation(reader));
} else {
codeUnits.push(new CodeUnit(reader.pos, char));
reader.advance(1);
}
}
return codeUnits;
}
/**
* Parses a template token.
* @param {string} source The template token to parse, including the delimiting sequences `` ` ``, `${` and `}`.
* @returns {CodeUnit[]} A list of code units produced by the template token.
*/
function parseTemplateToken(source) {
const reader = new TextReader(source);
reader.advance(1);
const codeUnits = [];
for (;;) {
const char = reader.read();
if (char === "`" || (char === "$" && reader.read(1) === "{")) {
break;
}
if (char === "\\") {
codeUnits.push(...mapEscapeSequenceOrLineContinuation(reader));
} else {
let unitSource;
if (char === "\r" && reader.read(1) === "\n") {
unitSource = "\r\n";
} else {
unitSource = char;
}
codeUnits.push(new CodeUnit(reader.pos, unitSource));
reader.advance(unitSource.length);
}
}
return codeUnits;
}
module.exports = { parseStringLiteral, parseTemplateToken };