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.
130 lines
4.3 KiB
JavaScript
130 lines
4.3 KiB
JavaScript
import { anchorIsValid } from '../doc/anchors.js';
|
|
import { isPair, isAlias, isNode, isScalar, isCollection } from '../nodes/identity.js';
|
|
import { stringifyComment } from './stringifyComment.js';
|
|
import { stringifyString } from './stringifyString.js';
|
|
|
|
function createStringifyContext(doc, options) {
|
|
const opt = Object.assign({
|
|
blockQuote: true,
|
|
commentString: stringifyComment,
|
|
defaultKeyType: null,
|
|
defaultStringType: 'PLAIN',
|
|
directives: null,
|
|
doubleQuotedAsJSON: false,
|
|
doubleQuotedMinMultiLineLength: 40,
|
|
falseStr: 'false',
|
|
flowCollectionPadding: true,
|
|
indentSeq: true,
|
|
lineWidth: 80,
|
|
minContentWidth: 20,
|
|
nullStr: 'null',
|
|
simpleKeys: false,
|
|
singleQuote: null,
|
|
trailingComma: false,
|
|
trueStr: 'true',
|
|
verifyAliasOrder: true
|
|
}, doc.schema.toStringOptions, options);
|
|
let inFlow;
|
|
switch (opt.collectionStyle) {
|
|
case 'block':
|
|
inFlow = false;
|
|
break;
|
|
case 'flow':
|
|
inFlow = true;
|
|
break;
|
|
default:
|
|
inFlow = null;
|
|
}
|
|
return {
|
|
anchors: new Set(),
|
|
doc,
|
|
flowCollectionPadding: opt.flowCollectionPadding ? ' ' : '',
|
|
indent: '',
|
|
indentStep: typeof opt.indent === 'number' ? ' '.repeat(opt.indent) : ' ',
|
|
inFlow,
|
|
options: opt
|
|
};
|
|
}
|
|
function getTagObject(tags, item) {
|
|
if (item.tag) {
|
|
const match = tags.filter(t => t.tag === item.tag);
|
|
if (match.length > 0)
|
|
return match.find(t => t.format === item.format) ?? match[0];
|
|
}
|
|
let tagObj = undefined;
|
|
let obj;
|
|
if (isScalar(item)) {
|
|
obj = item.value;
|
|
let match = tags.filter(t => t.identify?.(obj));
|
|
if (match.length > 1) {
|
|
const testMatch = match.filter(t => t.test);
|
|
if (testMatch.length > 0)
|
|
match = testMatch;
|
|
}
|
|
tagObj =
|
|
match.find(t => t.format === item.format) ?? match.find(t => !t.format);
|
|
}
|
|
else {
|
|
obj = item;
|
|
tagObj = tags.find(t => t.nodeClass && obj instanceof t.nodeClass);
|
|
}
|
|
if (!tagObj) {
|
|
const name = obj?.constructor?.name ?? (obj === null ? 'null' : typeof obj);
|
|
throw new Error(`Tag not resolved for ${name} value`);
|
|
}
|
|
return tagObj;
|
|
}
|
|
// needs to be called before value stringifier to allow for circular anchor refs
|
|
function stringifyProps(node, tagObj, { anchors, doc }) {
|
|
if (!doc.directives)
|
|
return '';
|
|
const props = [];
|
|
const anchor = (isScalar(node) || isCollection(node)) && node.anchor;
|
|
if (anchor && anchorIsValid(anchor)) {
|
|
anchors.add(anchor);
|
|
props.push(`&${anchor}`);
|
|
}
|
|
const tag = node.tag ?? (tagObj.default ? null : tagObj.tag);
|
|
if (tag)
|
|
props.push(doc.directives.tagString(tag));
|
|
return props.join(' ');
|
|
}
|
|
function stringify(item, ctx, onComment, onChompKeep) {
|
|
if (isPair(item))
|
|
return item.toString(ctx, onComment, onChompKeep);
|
|
if (isAlias(item)) {
|
|
if (ctx.doc.directives)
|
|
return item.toString(ctx);
|
|
if (ctx.resolvedAliases?.has(item)) {
|
|
throw new TypeError(`Cannot stringify circular structure without alias nodes`);
|
|
}
|
|
else {
|
|
if (ctx.resolvedAliases)
|
|
ctx.resolvedAliases.add(item);
|
|
else
|
|
ctx.resolvedAliases = new Set([item]);
|
|
item = item.resolve(ctx.doc);
|
|
}
|
|
}
|
|
let tagObj = undefined;
|
|
const node = isNode(item)
|
|
? item
|
|
: ctx.doc.createNode(item, { onTagObj: o => (tagObj = o) });
|
|
tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node));
|
|
const props = stringifyProps(node, tagObj, ctx);
|
|
if (props.length > 0)
|
|
ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1;
|
|
const str = typeof tagObj.stringify === 'function'
|
|
? tagObj.stringify(node, ctx, onComment, onChompKeep)
|
|
: isScalar(node)
|
|
? stringifyString(node, ctx, onComment, onChompKeep)
|
|
: node.toString(ctx, onComment, onChompKeep);
|
|
if (!props)
|
|
return str;
|
|
return isScalar(node) || str[0] === '{' || str[0] === '['
|
|
? `${props} ${str}`
|
|
: `${props}\n${ctx.indent}${str}`;
|
|
}
|
|
|
|
export { createStringifyContext, stringify };
|