@volter/world-core 3.0.38 → 3.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app-route.cjs +26 -25
- package/app-route.d.cts +1 -0
- package/dist/app-route.cjs +26 -25
- package/dist/app-route.d.cts +1 -0
- package/dist/generated/pack-facts.json +409 -352
- package/dist/inject.cjs +474 -95
- package/dist/network-policy.cjs +110 -13
- package/dist/network-policy.d.cts +8 -3
- package/dist/src/actions.js +2 -1
- package/dist/src/anthropic-wire.d.ts +2 -0
- package/dist/src/anthropic-wire.js +5 -0
- package/dist/src/attestation.d.ts +6 -0
- package/dist/src/attestation.js +27 -0
- package/dist/src/bytes.d.ts +3 -0
- package/dist/src/bytes.js +17 -0
- package/dist/src/changeset.js +2 -1
- package/dist/src/clickhouse/sql.js +73 -14
- package/dist/src/derived-core.d.ts +210 -32
- package/dist/src/derived-core.js +510 -244
- package/dist/src/derived-real.d.ts +1 -1
- package/dist/src/derived-real.js +704 -93
- package/dist/src/derived.d.ts +18 -2
- package/dist/src/derived.js +21 -2
- package/dist/src/events.d.ts +28 -45
- package/dist/src/events.js +40 -58
- package/dist/src/exact-json.d.ts +10 -0
- package/dist/src/exact-json.js +98 -0
- package/dist/src/executor.d.ts +17 -0
- package/dist/src/executor.js +91 -30
- package/dist/src/file-response.js +1 -1
- package/dist/src/form.d.ts +8 -0
- package/dist/src/form.js +76 -0
- package/dist/src/git/format.d.ts +16 -0
- package/dist/src/git/format.js +110 -0
- package/dist/src/git/index.d.ts +1 -0
- package/dist/src/git/index.js +1 -0
- package/dist/src/git/lfs.d.ts +1 -1
- package/dist/src/git/lfs.js +4 -3
- package/dist/src/graphql-wire.d.ts +6 -0
- package/dist/src/graphql-wire.js +6 -3
- package/dist/src/grpc-wire.js +2 -1
- package/dist/src/head.d.ts +1 -0
- package/dist/src/head.js +15 -4
- package/dist/src/held-reads.d.ts +6 -0
- package/dist/src/held-reads.js +60 -0
- package/dist/src/history.js +5 -4
- package/dist/src/host-port.d.ts +5 -0
- package/dist/src/host-port.js +14 -0
- package/dist/src/index.d.ts +11 -3
- package/dist/src/index.js +9 -4
- package/dist/src/log.js +12 -7
- package/dist/src/machines.d.ts +81 -5
- package/dist/src/machines.js +125 -9
- package/dist/src/managed-database.d.ts +15 -11
- package/dist/src/managed-database.js +38 -37
- package/dist/src/multipart.d.ts +21 -3
- package/dist/src/multipart.js +112 -38
- package/dist/src/observe.d.ts +3 -0
- package/dist/src/observe.js +6 -2
- package/dist/src/openai-wire.d.ts +16 -0
- package/dist/src/openai-wire.js +43 -12
- package/dist/src/pack-fetch.d.ts +4 -6
- package/dist/src/pack-fetch.js +189 -57
- package/dist/src/packRegistry.d.ts +7 -1
- package/dist/src/packRegistry.js +20 -3
- package/dist/src/private-transport.d.ts +1 -0
- package/dist/src/private-transport.js +11 -0
- package/dist/src/protobuf.js +10 -4
- package/dist/src/rateBudget.d.ts +8 -1
- package/dist/src/rateBudget.js +1 -0
- package/dist/src/redis/engine.d.ts +14 -5
- package/dist/src/redis/engine.js +271 -72
- package/dist/src/redis/frames.d.ts +17 -0
- package/dist/src/redis/frames.js +123 -0
- package/dist/src/redis/index.d.ts +6 -0
- package/dist/src/redis/index.js +6 -0
- package/dist/src/redis/json.d.ts +2 -0
- package/dist/src/redis/json.js +14 -0
- package/dist/src/redis/lua-libs.d.ts +4 -0
- package/dist/src/redis/lua-libs.js +173 -0
- package/dist/src/redis/lua.d.ts +1 -0
- package/dist/src/redis/lua.js +36 -0
- package/dist/src/redis/member-storage.d.ts +5 -0
- package/dist/src/redis/member-storage.js +75 -0
- package/dist/src/redis/resp2.d.ts +4 -0
- package/dist/src/redis/resp2.js +29 -0
- package/dist/src/redis/stream.d.ts +14 -0
- package/dist/src/redis/stream.js +335 -0
- package/dist/src/remote-execute.d.ts +2 -2
- package/dist/src/request-body.d.ts +11 -0
- package/dist/src/request-body.js +11 -0
- package/dist/src/runtime.d.ts +6 -5
- package/dist/src/runtime.js +5 -5
- package/dist/src/s3/wire.d.ts +4 -4
- package/dist/src/s3/wire.js +5 -3
- package/dist/src/scenario.d.ts +22 -0
- package/dist/src/scenario.js +35 -31
- package/dist/src/serve-http.d.ts +2 -1
- package/dist/src/serve-http.js +10 -1
- package/dist/src/serve.js +7 -4
- package/dist/src/signing.d.ts +23 -5
- package/dist/src/signing.js +74 -11
- package/dist/src/sigv4.d.ts +3 -1
- package/dist/src/sigv4.js +4 -2
- package/dist/src/smtp.js +2 -1
- package/dist/src/sockets.js +8 -3
- package/dist/src/state-system.d.ts +3 -0
- package/dist/src/storage.js +5 -2
- package/dist/src/twin-fetch.d.ts +3 -1
- package/dist/src/twin-fetch.js +3 -2
- package/dist/src/vendor-call.d.ts +10 -3
- package/dist/src/vendor-call.js +58 -14
- package/dist/test-fixtures/attestation.SOURCE.md +1 -0
- package/dist/test-fixtures/attestation.json +50 -0
- package/generated/pack-facts.json +409 -352
- package/inject.cjs +474 -95
- package/network-policy.cjs +110 -13
- package/network-policy.d.cts +8 -3
- package/package.json +11 -4
- package/src/actions.ts +2 -1
- package/src/anthropic-wire.ts +6 -0
- package/src/attestation.ts +23 -0
- package/src/bytes.ts +21 -0
- package/src/changeset.ts +2 -1
- package/src/clickhouse/sql.ts +50 -16
- package/src/derived-core.ts +573 -249
- package/src/derived-real.ts +552 -42
- package/src/derived.ts +23 -4
- package/src/events.ts +54 -85
- package/src/exact-json.ts +71 -0
- package/src/executor.ts +86 -27
- package/src/file-response.ts +1 -1
- package/src/form.ts +66 -0
- package/src/git/format.ts +89 -0
- package/src/git/index.ts +1 -0
- package/src/git/lfs.ts +5 -4
- package/src/graphql-wire.ts +8 -3
- package/src/grpc-wire.ts +2 -1
- package/src/head.ts +15 -5
- package/src/held-reads.ts +54 -0
- package/src/history.ts +5 -4
- package/src/host-port.ts +9 -0
- package/src/index.ts +10 -4
- package/src/log.ts +11 -7
- package/src/machines.ts +135 -13
- package/src/managed-database.ts +48 -40
- package/src/multipart.ts +110 -35
- package/src/observe.ts +6 -3
- package/src/openai-wire.ts +45 -14
- package/src/pack-fetch.ts +173 -50
- package/src/packRegistry.ts +27 -4
- package/src/private-transport.ts +8 -0
- package/src/protobuf.ts +8 -4
- package/src/rateBudget.ts +6 -1
- package/src/redis/engine.ts +168 -63
- package/src/redis/frames.ts +53 -0
- package/src/redis/index.ts +6 -0
- package/src/redis/json.ts +13 -0
- package/src/redis/lua-libs.ts +62 -0
- package/src/redis/lua.ts +20 -0
- package/src/redis/member-storage.ts +58 -0
- package/src/redis/resp2.ts +25 -0
- package/src/redis/stream.ts +102 -0
- package/src/remote-execute.ts +2 -2
- package/src/request-body.ts +12 -0
- package/src/runtime.ts +6 -5
- package/src/s3/wire.ts +8 -5
- package/src/scenario.ts +34 -13
- package/src/serve-http.ts +11 -2
- package/src/serve.ts +4 -3
- package/src/signing.ts +71 -17
- package/src/sigv4.ts +5 -3
- package/src/smtp.ts +3 -2
- package/src/sockets.ts +8 -3
- package/src/state-system.ts +3 -0
- package/src/storage.ts +5 -2
- package/src/twin-fetch.ts +4 -3
- package/src/vendor-call.ts +59 -12
- package/test-fixtures/attestation.json +50 -0
package/src/file-response.ts
CHANGED
|
@@ -33,7 +33,7 @@ export function bytesResponse(request: Request, bytes: Uint8Array, contentType:
|
|
|
33
33
|
const range = /^bytes=(\d*)-(\d*)$/.exec((request.headers.get('range') ?? '').trim());
|
|
34
34
|
let start = 0;
|
|
35
35
|
let end = total - 1;
|
|
36
|
-
const valid = range !== null && (range[1] !== '' || range[2] !== '') && !(range[1] !== '' && range[2] !== '' &&
|
|
36
|
+
const valid = range !== null && (range[1] !== '' || range[2] !== '') && !(range[1] !== '' && range[2] !== '' && BigInt(range[1]) > BigInt(range[2]));
|
|
37
37
|
if (valid) {
|
|
38
38
|
if (range[1] === '') { start = Math.max(0, total - Number(range[2])); } else {
|
|
39
39
|
start = Number(range[1]);
|
package/src/form.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { exactInteger } from './exact-json.ts';
|
|
2
|
+
// Side-effect-free URL-encoded form reading, shared by requests and published examples.
|
|
3
|
+
|
|
4
|
+
/** A field set as the object's own, whatever its name (`__proto__`, `constructor` and `prototype` are data a caller
|
|
5
|
+
* may name: Stripe's metadata keys are the caller's), never through what the object inherits. */
|
|
6
|
+
export function ownField(node: Record<string, unknown> | unknown[], key: string, value: unknown): void {
|
|
7
|
+
Object.defineProperty(node, key, { value, writable: true, enumerable: true, configurable: true });
|
|
8
|
+
}
|
|
9
|
+
/** A field the object holds as its own, never one it inherits (`constructor` is every object's). */
|
|
10
|
+
const ownValue = (node: Record<string, unknown> | unknown[], key: string): unknown => (Object.hasOwn(node, key) ? (node as Record<string, unknown>)[key] : undefined);
|
|
11
|
+
|
|
12
|
+
/** A form body's bracket notation (`a[b][0][c]=v`, `expand[]=x`) as nested objects and arrays. A form carries only
|
|
13
|
+
* text: a field the operation's spec types as a number or a boolean (`scalars`, by bracket path, written by
|
|
14
|
+
* world-tooling's formScalarsOf) is read as one when its text is that literal; every other field stays the text sent
|
|
15
|
+
* (`metadata[order]=007`, `name=2024`). */
|
|
16
|
+
export function parseBracketForm(text: string, scalars?: Readonly<Record<string, string | undefined>>): Record<string, unknown> {
|
|
17
|
+
const out: Record<string, unknown> = {};
|
|
18
|
+
for (const [rawKey, raw] of new URLSearchParams(text)) {
|
|
19
|
+
const parts = rawKey.replace(/\]/g, '').split('[');
|
|
20
|
+
const kind = scalars && scalarAt(scalars, parts);
|
|
21
|
+
const value: unknown = kind === 'boolean' ? (raw === 'true' ? true : raw === 'false' ? false : raw)
|
|
22
|
+
: kind === 'integer' ? (/^-?\d+$/.test(raw) ? exactInteger(raw) : raw)
|
|
23
|
+
: kind === 'number' ? (/^-?\d+$/.test(raw) ? exactInteger(raw) : /^-?\d+\.\d+$/.test(raw) ? Number(raw) : raw)
|
|
24
|
+
: raw;
|
|
25
|
+
// every field is the node's own, read and written as such: a key naming an object's machinery (`__proto__[x]`,
|
|
26
|
+
// `constructor[prototype][x]`) is a field of that name, never a walk into what every object inherits
|
|
27
|
+
let node: Record<string, unknown> | unknown[] = out;
|
|
28
|
+
for (const [i, key] of parts.entries()) {
|
|
29
|
+
// an array holds its items by index (`a[]`, `a[0]`): a named key under one (`a[]=1&a[length]=-1`) has nowhere to
|
|
30
|
+
// go, as a key under text has none (`a=1&a[b]=2`): the first shape stands
|
|
31
|
+
if (Array.isArray(node) && key !== '' && !/^\d+$/.test(key)) break;
|
|
32
|
+
if (i === parts.length - 1) {
|
|
33
|
+
if (key === '') {
|
|
34
|
+
if (Array.isArray(node)) node.push(value);
|
|
35
|
+
} else {
|
|
36
|
+
const held = ownValue(node, key);
|
|
37
|
+
if (held === undefined) ownField(node, key, value);
|
|
38
|
+
else if (Array.isArray(held)) held.push(value);
|
|
39
|
+
else ownField(node, key, [held, value]);
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
const next = parts[i + 1]!;
|
|
44
|
+
if (key === '') break;
|
|
45
|
+
let child = ownValue(node, key);
|
|
46
|
+
if (child === undefined) { child = next === '' || /^\d+$/.test(next) ? [] : {}; ownField(node, key, child); }
|
|
47
|
+
if (child === null || typeof child !== 'object') break;
|
|
48
|
+
node = child as Record<string, unknown> | unknown[];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The type `scalars` gives a form key's parts: a segment is a property name, `[]` an array's item (an index or empty),
|
|
55
|
+
* `*` a map's key. */
|
|
56
|
+
function scalarAt(scalars: Readonly<Record<string, string | undefined>>, parts: string[]): string | undefined {
|
|
57
|
+
let paths = [''];
|
|
58
|
+
for (const [i, part] of parts.entries()) {
|
|
59
|
+
const segs = part === '' || /^\d+$/.test(part) ? ['[]', '*'] : [part, '*'];
|
|
60
|
+
paths = paths.flatMap((p) => segs.map((seg) => (i === 0 ? seg : `${p}.${seg}`)));
|
|
61
|
+
if (paths.length > 64) paths = paths.filter((p) => Object.keys(scalars).some((k) => k === p || k.startsWith(`${p}.`)));
|
|
62
|
+
}
|
|
63
|
+
for (const p of paths) if (scalars[p]) return scalars[p];
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Git text representations over the object store (architecture, The engine slot).
|
|
2
|
+
import { decodeCommit, type GitObjectStore, type Ident } from './objects.ts';
|
|
3
|
+
import { diffTrees, lineDiff, unifiedPatch, type TreeChange } from './history.ts';
|
|
4
|
+
|
|
5
|
+
const decoder = new TextDecoder();
|
|
6
|
+
const binary = (bytes: Uint8Array): boolean => bytes.slice(0, 8000).includes(0);
|
|
7
|
+
const pathName = (path: string): string => /[\s"\\\x00-\x1f]/.test(path) ? JSON.stringify(path) : path;
|
|
8
|
+
async function bytesOf(store: GitObjectStore, sha?: string, mode?: string): Promise<Uint8Array> {
|
|
9
|
+
if (!sha) return new Uint8Array();
|
|
10
|
+
if (mode === '160000') return new TextEncoder().encode(`Subproject commit ${sha}\n`);
|
|
11
|
+
return (await store.read(sha))?.payload ?? new Uint8Array();
|
|
12
|
+
}
|
|
13
|
+
async function fileDiff(store: GitObjectStore, change: TreeChange): Promise<{ text: string; additions: number; deletions: number }> {
|
|
14
|
+
const oldBytes = await bytesOf(store, change.oldSha, change.oldMode);
|
|
15
|
+
const newBytes = await bytesOf(store, change.newSha, change.newMode);
|
|
16
|
+
const left = pathName(`a/${change.path}`), right = pathName(`b/${change.path}`);
|
|
17
|
+
let text = `diff --git ${left} ${right}\n`;
|
|
18
|
+
if (!change.oldSha) text += `new file mode ${change.newMode}\n`;
|
|
19
|
+
else if (!change.newSha) text += `deleted file mode ${change.oldMode}\n`;
|
|
20
|
+
else if (change.oldMode !== change.newMode) text += `old mode ${change.oldMode}\nnew mode ${change.newMode}\n`;
|
|
21
|
+
if (change.oldSha === change.newSha) return { text, additions: 0, deletions: 0 };
|
|
22
|
+
text += `index ${(change.oldSha ?? '0000000').slice(0, 7)}..${(change.newSha ?? '0000000').slice(0, 7)}${change.oldSha && change.newSha && change.oldMode === change.newMode ? ` ${change.newMode}` : ''}\n`;
|
|
23
|
+
if (binary(oldBytes) || binary(newBytes)) return { text: `${text}Binary files ${change.oldSha ? left : '/dev/null'} and ${change.newSha ? right : '/dev/null'} differ\n`, additions: 0, deletions: 0 };
|
|
24
|
+
const oldText = decoder.decode(oldBytes), newText = decoder.decode(newBytes);
|
|
25
|
+
const edits = lineDiff(oldText, newText);
|
|
26
|
+
text += `--- ${change.oldSha ? left : '/dev/null'}\n+++ ${change.newSha ? right : '/dev/null'}\n${unifiedPatch(oldText, newText)}\n`;
|
|
27
|
+
return { text, additions: edits.filter((edit) => edit.kind === '+').length, deletions: edits.filter((edit) => edit.kind === '-').length };
|
|
28
|
+
}
|
|
29
|
+
/** A complete unified diff between two trees or commits, with null as the empty preimage. */
|
|
30
|
+
export async function diffText(store: GitObjectStore, from: string | null, to: string): Promise<string> {
|
|
31
|
+
const changes = await diffTrees(store, from, to);
|
|
32
|
+
return (await Promise.all(changes.map((change) => fileDiff(store, change)))).map((file) => file.text).join('');
|
|
33
|
+
}
|
|
34
|
+
function mailDate(author: Ident): string {
|
|
35
|
+
const sign = author.tz.startsWith('-') ? -1 : 1;
|
|
36
|
+
const offset = sign * (Number(author.tz.slice(1, 3)) * 60 + Number(author.tz.slice(3, 5)));
|
|
37
|
+
const date = new Date((author.time + offset * 60) * 1000);
|
|
38
|
+
const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
39
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
40
|
+
const pad = (n: number): string => String(n).padStart(2, '0');
|
|
41
|
+
return `${weekdays[date.getUTCDay()]}, ${date.getUTCDate()} ${months[date.getUTCMonth()]} ${date.getUTCFullYear()} ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())} ${author.tz}`;
|
|
42
|
+
}
|
|
43
|
+
/** One commit as an email patch; series metadata numbers the subject for a comparison's commits. */
|
|
44
|
+
export async function formatPatch(store: GitObjectStore, sha: string, series?: { number: number; total: number }): Promise<string | null> {
|
|
45
|
+
const object = await store.read(sha);
|
|
46
|
+
if (!object || object.type !== 'commit') return null;
|
|
47
|
+
const commit = decodeCommit(object.payload);
|
|
48
|
+
const changes = await diffTrees(store, commit.parents[0] ?? null, sha);
|
|
49
|
+
const files = await Promise.all(changes.map((change) => fileDiff(store, change)));
|
|
50
|
+
const width = Math.max(0, ...changes.map((change) => change.path.length));
|
|
51
|
+
const stats = changes.map((change, i) => {
|
|
52
|
+
const file = files[i]!;
|
|
53
|
+
return ` ${change.path.padEnd(width)} | ${file.additions + file.deletions} ${'+'.repeat(file.additions)}${'-'.repeat(file.deletions)}\n`;
|
|
54
|
+
}).join('');
|
|
55
|
+
const additions = files.reduce((sum, file) => sum + file.additions, 0), deletions = files.reduce((sum, file) => sum + file.deletions, 0);
|
|
56
|
+
const summary = `${changes.length} file${changes.length === 1 ? '' : 's'} changed${additions ? `, ${additions} insertion${additions === 1 ? '' : 's'}(+)` : ''}${deletions ? `, ${deletions} deletion${deletions === 1 ? '' : 's'}(-)` : ''}`;
|
|
57
|
+
const [title = '', ...rest] = commit.message.trimEnd().split('\n');
|
|
58
|
+
const body = rest.join('\n').trim();
|
|
59
|
+
const numbered = series && series.total > 1 ? ` ${series.number}/${series.total}` : '';
|
|
60
|
+
return `From ${sha} Mon Sep 17 00:00:00 2001\nFrom: ${commit.author.name} <${commit.author.email}>\nDate: ${mailDate(commit.author)}\nSubject: [PATCH${numbered}] ${title}\n\n${body ? `${body}\n\n` : ''}---\n${stats} ${summary}\n\n${files.map((file) => file.text).join('')}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Submodule paths and URLs declared by a .gitmodules file (git configuration syntax). */
|
|
64
|
+
export function parseSubmodules(text: string): Array<{ name: string; path: string; url: string }> {
|
|
65
|
+
const modules: Array<{ name: string; path: string; url: string }> = [];
|
|
66
|
+
let current: { name: string; path: string; url: string } | undefined;
|
|
67
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
68
|
+
const line = raw.trim();
|
|
69
|
+
const section = /^\[submodule\s+"((?:[^"\\]|\\.)*)"\]\s*(?:[#;].*)?$/.exec(line);
|
|
70
|
+
if (section) { current = { name: section[1]!.replace(/\\(.)/g, '$1'), path: '', url: '' }; modules.push(current); continue; }
|
|
71
|
+
if (line.startsWith('[')) { current = undefined; continue; }
|
|
72
|
+
const field = /^(path|url)\s*=\s*(.*)$/i.exec(line);
|
|
73
|
+
if (!current || !field) continue;
|
|
74
|
+
const value = field[2]!.replace(/\s+[#;].*$/, '').trim();
|
|
75
|
+
current[field[1]!.toLowerCase() as 'path' | 'url'] = value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1).replace(/\\(.)/g, '$1') : value;
|
|
76
|
+
}
|
|
77
|
+
return modules.filter((module) => module.path && module.url);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A repository-relative symbolic-link target; targets escaping the repository have no local path. */
|
|
81
|
+
export function symlinkPath(path: string, target: string): string | undefined {
|
|
82
|
+
if (target.startsWith('/')) return undefined;
|
|
83
|
+
const segments = path.split('/').slice(0, -1);
|
|
84
|
+
for (const part of target.split('/')) {
|
|
85
|
+
if (!part || part === '.') continue;
|
|
86
|
+
if (part === '..') { if (!segments.length) return undefined; segments.pop(); } else segments.push(part);
|
|
87
|
+
}
|
|
88
|
+
return segments.join('/');
|
|
89
|
+
}
|
package/src/git/index.ts
CHANGED
package/src/git/lfs.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { exactInteger } from '../exact-json.ts';
|
|
1
2
|
// GIT LFS (architecture, "The tree contract": Git LFS is the git library's too): the batch API git-lfs speaks
|
|
2
3
|
// (https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md, the basic transfer), a pointer file read
|
|
3
4
|
// (https://github.com/git-lfs/git-lfs/blob/main/docs/spec.md), and an upload accepted only when its bytes hash to the
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
// Importing it does nothing.
|
|
7
8
|
|
|
8
9
|
export const LFS_TYPE = 'application/vnd.git-lfs+json';
|
|
9
|
-
export type LfsObject = { oid: string; size: number };
|
|
10
|
+
export type LfsObject = { oid: string; size: number | string };
|
|
10
11
|
/** Where a pack keeps its objects: by oid. */
|
|
11
12
|
export type LfsStore = { get(oid: string): Promise<Uint8Array | null>; put(oid: string, bytes: Uint8Array): Promise<void> };
|
|
12
13
|
|
|
@@ -20,7 +21,7 @@ export function parseLfsPointer(file: Uint8Array | string): LfsObject | null {
|
|
|
20
21
|
if (!text.startsWith('version https://git-lfs.github.com/spec/')) return null;
|
|
21
22
|
const oid = /^oid sha256:([0-9a-f]{64})$/m.exec(text)?.[1];
|
|
22
23
|
const size = /^size (\d+)$/m.exec(text)?.[1];
|
|
23
|
-
return oid && size ? { oid, size:
|
|
24
|
+
return oid && size ? { oid, size: exactInteger(size) } : null;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/** A pointer file's text for an object. */
|
|
@@ -44,9 +45,9 @@ export async function lfsBatch(body: unknown, opts: { store: LfsStore; href: (op
|
|
|
44
45
|
const operation = b.operation;
|
|
45
46
|
const objects = [];
|
|
46
47
|
for (const raw of b.objects as Array<{ oid?: unknown; size?: unknown }>) {
|
|
47
|
-
const o = { oid: String(raw?.oid ?? ''), size: Number(raw?.size) };
|
|
48
|
+
const o = { oid: String(raw?.oid ?? ''), size: /^-?\d+$/.test(String(raw?.size)) ? exactInteger(String(raw?.size)) : Number(raw?.size) };
|
|
48
49
|
// source: https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md "The object is invalid (for example, an invalid oid or size)."
|
|
49
|
-
if (!OID.test(o.oid) || !Number.isSafeInteger(o.size) || o.size < 0) { objects.push({ ...o, error: { code: 422, message: 'Invalid object' } }); continue; }
|
|
50
|
+
if (!OID.test(o.oid) || !Number.isSafeInteger(o.size) || Number(o.size) < 0) { objects.push({ ...o, error: { code: 422, message: 'Invalid object' } }); continue; }
|
|
50
51
|
const held = await opts.store.get(o.oid);
|
|
51
52
|
if (operation === 'upload') {
|
|
52
53
|
objects.push(held ? { ...o, authenticated: true } : { ...o, authenticated: true, actions: { upload: { href: opts.href('upload', o), ...(opts.header ? { header: opts.header } : {}), expires_in: 3600 } } });
|
package/src/graphql-wire.ts
CHANGED
|
@@ -16,6 +16,8 @@ export type GraphqlResolver<C = unknown> = (source: Record<string, unknown>, arg
|
|
|
16
16
|
|
|
17
17
|
/** What a pack's `semantics/graphql.ts` exports as `graphql`. */
|
|
18
18
|
export type GraphqlPart<C = unknown> = {
|
|
19
|
+
/** The root operations derived from the SDL, for dispatch ownership and gap reporting. */
|
|
20
|
+
operations?: ReadonlyArray<{ id: string; kind: 'query' | 'mutation'; field: string }>;
|
|
19
21
|
/** resolvers by `Type.field` (`Query.repository`, `PullRequest.state`) */
|
|
20
22
|
resolvers: Record<string, GraphqlResolver<C>>;
|
|
21
23
|
/** per type, a GraphQL field name → the object's field that holds it (`nameWithOwner` → `full_name`) */
|
|
@@ -63,8 +65,11 @@ export function graphqlEndpoint<C>(options: {
|
|
|
63
65
|
}): (request: Request) => Promise<Response> {
|
|
64
66
|
let schema: import('graphql').GraphQLSchema | undefined;
|
|
65
67
|
const { resolvers, aliases, stored } = options.part;
|
|
68
|
+
// introspection (`__typename`, `__type`, `__schema` and the `__Type`/`__Field`/… meta types) is the vendor's schema
|
|
69
|
+
// describing itself, answered by graphql-js from the SDL: modeled as the schema is (gh asks `__type(name:"PullRequest")`
|
|
70
|
+
// before it merges)
|
|
66
71
|
const modeled = (type: string, field: string): boolean =>
|
|
67
|
-
field
|
|
72
|
+
field.startsWith('__') || type.startsWith('__') || `${type}.${field}` in resolvers || !!aliases?.[type]?.[field] || !!stored?.[type]?.includes(field) || !!stored?.[type]?.includes(snake(field));
|
|
68
73
|
return async (request) => {
|
|
69
74
|
const g = await graphqlLib();
|
|
70
75
|
schema ??= g.buildSchema(options.sdl, { assumeValidSDL: true });
|
|
@@ -87,10 +92,10 @@ export function graphqlEndpoint<C>(options: {
|
|
|
87
92
|
const ctx = await options.context(request);
|
|
88
93
|
const result = await g.execute({
|
|
89
94
|
schema, document, variableValues: payload.variables ?? {}, ...(payload.operationName ? { operationName: payload.operationName } : {}), contextValue: ctx,
|
|
90
|
-
fieldResolver: (source, args, context, info) => {
|
|
95
|
+
fieldResolver: async (source, args, context, info) => {
|
|
91
96
|
const own = resolvers[`${info.parentType.name}.${info.fieldName}`];
|
|
92
97
|
const obj = (source ?? {}) as Record<string, unknown>;
|
|
93
|
-
let value = own ? own(obj, args as Record<string, unknown>, context as C) : (obj[aliases?.[info.parentType.name]?.[info.fieldName] ?? info.fieldName] ?? obj[snake(info.fieldName)] ?? null);
|
|
98
|
+
let value = own ? await own(obj, args as Record<string, unknown>, context as C) : (obj[aliases?.[info.parentType.name]?.[info.fieldName] ?? info.fieldName] ?? obj[snake(info.fieldName)] ?? null);
|
|
94
99
|
const named = g.getNamedType(info.returnType);
|
|
95
100
|
if (Array.isArray(value) && g.isObjectType(named) && named.name.endsWith('Connection')) value = connection(value, args as Record<string, unknown>);
|
|
96
101
|
if (typeof value === 'string' && g.isEnumType(named)) value = named.getValues().find((v) => v.name.toLowerCase() === String(value).toLowerCase())?.name ?? value;
|
package/src/grpc-wire.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseExactJson } from './exact-json.ts';
|
|
1
2
|
// THE gRPC WIRE (architecture, "Other wires": gRPC): unary gRPC calls over the kernel's HTTP/2 server (./h2.ts), each
|
|
2
3
|
// request's one length-prefixed message decoded by the proto unit's schema (./protobuf.ts) and handed to the pack's
|
|
3
4
|
// handler for its operation, whose JSON answer is encoded back with the status trailers
|
|
@@ -137,7 +138,7 @@ export function grpcOverFetch(fetch: (request: Request) => Promise<Response>): G
|
|
|
137
138
|
const res = await fetch(new Request(`http://twin${call.path}`, { method: 'POST', headers: { ...call.metadata, 'content-type': 'application/json' }, body: JSON.stringify(call.request), signal: call.signal }));
|
|
138
139
|
const text = await res.text();
|
|
139
140
|
let body: unknown;
|
|
140
|
-
try { body = text ?
|
|
141
|
+
try { body = text ? parseExactJson(text) : {}; } catch { body = undefined; }
|
|
141
142
|
if (res.ok) return (body ?? {}) as Record<string, unknown>;
|
|
142
143
|
const message = (body as { error?: { message?: unknown }; message?: unknown } | undefined)?.error?.message ?? (body as { message?: unknown } | undefined)?.message ?? text;
|
|
143
144
|
throw new GrpcError(codeOf(res.status, body), String(message).slice(0, 500));
|
package/src/head.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type PushOutcome = {
|
|
|
31
31
|
/** the group's entry the id is for, when it is not the group's first (a cascade writes children first) */
|
|
32
32
|
actionId?: string;
|
|
33
33
|
/** the group's other entries the answer named an id for (a resource's `companions`) */
|
|
34
|
-
also?: Array<{ actionId: string; externalId: string }>;
|
|
34
|
+
also?: Array<{ actionId: string; externalId: string; data?: Record<string, unknown> }>;
|
|
35
35
|
};
|
|
36
36
|
/** What a perform is handed beside the executor: a resolver from a local id to the vendor's (an
|
|
37
37
|
* entry authored against `twin-1` crosses against `REAL-42` once that subject's landing adopted it). */
|
|
@@ -377,6 +377,14 @@ async function performNow(opts: Parameters<typeof performEntries>[0]): Promise<D
|
|
|
377
377
|
let stopped = false;
|
|
378
378
|
for (const group of groupsOf(pick(settling.offered))) {
|
|
379
379
|
if (stopped) { report.skipped.push(...group.map((a) => a.id)); continue; }
|
|
380
|
+
// a write of a unit the pack sends nowhere (`vendorBacked.none` beside vendor-backed lanes) is the World's alone
|
|
381
|
+
const unsent = adapters.unsent?.(group[0]!.input as Record<string, unknown> | undefined);
|
|
382
|
+
if (unsent) {
|
|
383
|
+
const at = opts.at ?? worldNow();
|
|
384
|
+
for (const action of group) settle(action, at, { status: 'landed', reason: unsent });
|
|
385
|
+
report.worldOnly.push(...group.map((a) => a.id));
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
380
388
|
if (named(group[0]!).some((v) => refusedIds.has(v))) {
|
|
381
389
|
const at = opts.at ?? worldNow();
|
|
382
390
|
for (const action of group) { settle(action, at, { status: 'landed', reason: 'names a subject the vendor refused: the World\'s alone' }); refusedIds.add(action.subject.id); }
|
|
@@ -402,12 +410,14 @@ async function performNow(opts: Parameters<typeof performEntries>[0]): Promise<D
|
|
|
402
410
|
// the vendor's answer, when the adapter returned one as fields, is what the primary entry's landed copy holds
|
|
403
411
|
const answered = outcome.data !== null && typeof outcome.data === 'object' && !Array.isArray(outcome.data) ? (outcome.data as Record<string, unknown>) : {};
|
|
404
412
|
const primary = resolved.find((a) => a.id === outcome.actionId) ?? resolved[0]!;
|
|
405
|
-
const also = new Map((outcome.also ?? []).map((a) => [a.actionId, a
|
|
413
|
+
const also = new Map((outcome.also ?? []).map((a) => [a.actionId, a]));
|
|
406
414
|
for (const action of resolved) {
|
|
407
|
-
const
|
|
408
|
-
|
|
415
|
+
const answeredId = action === primary ? outcome.externalId : also.get(action.id)?.externalId;
|
|
416
|
+
const externalId = answeredId || aliases.get(`${action.subject.type}:${action.subject.id}`);
|
|
417
|
+
settle(action, at, { status: 'deployed', ...(action === primary && outcome.url ? { url: outcome.url } : {}) }, { ...action.fields, ...(action === primary ? answered : also.get(action.id)?.data ?? {}) }, externalId || undefined);
|
|
418
|
+
const data = action === primary ? outcome.data : also.get(action.id)?.data;
|
|
409
419
|
report.pushed += 1;
|
|
410
|
-
report.deployed.push({ actionId: action.id, at, ...(externalId ? { externalId } : {}), ...(
|
|
420
|
+
report.deployed.push({ actionId: action.id, at, ...(externalId ? { externalId } : {}), ...(data !== undefined ? { data } : {}) });
|
|
411
421
|
}
|
|
412
422
|
} catch (error) {
|
|
413
423
|
// a write the vendor refuses (a 4xx: it can never be sent as it is) settles the group refused, as a check's
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// A read held until the World writes (architecture, "Other wires": a read held until the World writes): HTTP long
|
|
2
|
+
// polling, as Telegram's getUpdates is, where a client asks again at once on an empty answer. A handler that finds
|
|
3
|
+
// nothing to answer awaits `ctx.awaitWrite(types, ms)`: it settles true when a write of one of the stored types lands in
|
|
4
|
+
// the same World (the derived core's one write path tells it, as it tells the open sockets), false when the time runs
|
|
5
|
+
// out or the client hangs up. The wait is the connection's, never World state: the handler reads the tree again and
|
|
6
|
+
// answers what it holds. Nothing here runs at module scope beyond the empty registry.
|
|
7
|
+
|
|
8
|
+
type Waiter = { types: ReadonlySet<string>; settle: (wrote: boolean) => void };
|
|
9
|
+
const waiting = new Map<string, Set<Waiter>>();
|
|
10
|
+
const keyOf = (service: string, root: string | undefined): string => `${service}\0${root ?? ''}`;
|
|
11
|
+
|
|
12
|
+
/** Tell every read held in this World that a subject of `storedType` was written. */
|
|
13
|
+
export function heldReadsWrite(service: string, root: string | undefined, storedType: string): void {
|
|
14
|
+
const held = waiting.get(keyOf(service, root));
|
|
15
|
+
if (!held?.size) return;
|
|
16
|
+
for (const w of [...held]) if (w.types.has(storedType)) w.settle(true);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Wait until a write of one of `storedTypes` lands in this World (true), or `ms` pass or `signal` aborts (false). */
|
|
20
|
+
export function awaitWrite(service: string, root: string | undefined, storedTypes: ReadonlyArray<string>, ms: number, signal?: AbortSignal): Promise<boolean> {
|
|
21
|
+
if (!(ms > 0) || signal?.aborted) return Promise.resolve(false);
|
|
22
|
+
const key = keyOf(service, root);
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
let held = waiting.get(key);
|
|
25
|
+
if (!held) { held = new Set(); waiting.set(key, held); }
|
|
26
|
+
const set = held;
|
|
27
|
+
const waiter: Waiter = {
|
|
28
|
+
types: new Set(storedTypes),
|
|
29
|
+
settle: (wrote) => {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
signal?.removeEventListener('abort', onAbort);
|
|
32
|
+
set.delete(waiter);
|
|
33
|
+
if (!set.size) waiting.delete(key);
|
|
34
|
+
resolve(wrote);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const onAbort = (): void => waiter.settle(false);
|
|
38
|
+
const timer = setTimeout(() => waiter.settle(false), ms);
|
|
39
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
40
|
+
set.add(waiter);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Hold a read until its condition holds, within the caller's timeout. The kernel owns elapsed time. */
|
|
45
|
+
export async function awaitCondition(service: string, root: string | undefined, storedTypes: ReadonlyArray<string>, ms: number, pollMs: number, condition: () => Promise<boolean>, signal?: AbortSignal): Promise<boolean> {
|
|
46
|
+
if (!(pollMs > 0)) throw new Error('a held condition requires a positive observation cadence');
|
|
47
|
+
const deadline = Date.now() + Math.max(0, ms);
|
|
48
|
+
for (;;) {
|
|
49
|
+
if (await condition()) return true;
|
|
50
|
+
const remaining = deadline - Date.now();
|
|
51
|
+
if (remaining <= 0 || signal?.aborted) return false;
|
|
52
|
+
await awaitWrite(service, root, storedTypes, Math.min(remaining, pollMs), signal);
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/history.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseExactJson } from './exact-json.ts';
|
|
1
2
|
// Immutable views over shared append-only segments. A position is meaningful within a view.
|
|
2
3
|
import { createHash } from 'node:crypto';
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -34,7 +35,7 @@ const viewPath = (directory: string, id: string) => {
|
|
|
34
35
|
return join(directory, 'views', `${id}.json`);
|
|
35
36
|
};
|
|
36
37
|
function rows(directory: string, file: Segment['file']): Entry[] {
|
|
37
|
-
return getActiveWorldStore().readLines(join(directory, file)).filter(line => line.trim()).map(line => toEntry(
|
|
38
|
+
return getActiveWorldStore().readLines(join(directory, file)).filter(line => line.trim()).map(line => toEntry(parseExactJson(line)));
|
|
38
39
|
}
|
|
39
40
|
function sliceLayout(layout: HistoryLayout, at: number): HistoryLayout {
|
|
40
41
|
const base = layout.base ? historyLength(layout.base) : 0;
|
|
@@ -43,7 +44,7 @@ function sliceLayout(layout: HistoryLayout, at: number): HistoryLayout {
|
|
|
43
44
|
export function readHistoryView(directory: string, id: string): HistoryView {
|
|
44
45
|
const raw = getActiveWorldStore().read(viewPath(directory, id));
|
|
45
46
|
if (raw === null) throw new Error(`Missing retained history view ${id}`);
|
|
46
|
-
const { dependencies: _dependencies, ...view } =
|
|
47
|
+
const { dependencies: _dependencies, ...view } = parseExactJson(raw) as HistoryView & { dependencies?: unknown };
|
|
47
48
|
if (view.version !== 1 || historyDigest({ ...view, dependencies: _dependencies }) !== id) throw new Error(`Invalid retained history view ${id}`);
|
|
48
49
|
historyLength(view.layout);
|
|
49
50
|
if (view.origin) validateHistoryOrigin(view.origin, view.layout);
|
|
@@ -99,7 +100,7 @@ export function inheritedHistoryKey(service: string, root?: string): string {
|
|
|
99
100
|
let raw: string | null; let at: string;
|
|
100
101
|
try { at = viewPath(directory, parent.view); raw = store.read(at); } catch { return 'invalid-view'; }
|
|
101
102
|
if (raw === null) return 'missing-view';
|
|
102
|
-
const view =
|
|
103
|
+
const view = parseExactJson(raw) as HistoryView;
|
|
103
104
|
const directories = new Set<string>([view.owner?.directory ?? '', ...(view.segments ?? []).map((sp) => sp.directory)]);
|
|
104
105
|
if (!isUrlParent(parent.at)) directories.add(worldPaths(service, parent.at).dir);
|
|
105
106
|
const files = [...new Set((view.segments ?? []).map((sp) => join(sp.directory, sp.file)))].sort();
|
|
@@ -133,7 +134,7 @@ export function captureHistory(service: string, root?: string): { view: string;
|
|
|
133
134
|
}
|
|
134
135
|
export function originHead(service: string, root?: string): { view: string; remoteView: string } | null {
|
|
135
136
|
const raw = getActiveWorldStore().read(join(worldPaths(service, root).dir, 'origin-head.json'));
|
|
136
|
-
return raw === null ? null :
|
|
137
|
+
return raw === null ? null : parseExactJson(raw);
|
|
137
138
|
}
|
|
138
139
|
/** A complete remote view shares cached rows by content, never by mutable offsets or entry ID. */
|
|
139
140
|
export function publishOriginHistory(service: string, root: string, remoteView: string, layout: HistoryLayout, entries: Entry[]): { view: string; appended: number } {
|
package/src/host-port.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Read a Host authority with the platform URL parser; bracketed IPv6 retains its address. */
|
|
2
|
+
export function readHostPort(authority: string): { host: string; port: string } | undefined {
|
|
3
|
+
try {
|
|
4
|
+
if (!authority || /[\s/\\?#@]/.test(authority)) return undefined;
|
|
5
|
+
const url = new URL(`http://${authority}`);
|
|
6
|
+
if (url.username || url.password) return undefined;
|
|
7
|
+
return { host: url.hostname.toLowerCase(), port: url.port };
|
|
8
|
+
} catch { return undefined; }
|
|
9
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,9 @@ export type { PackDescriptor, PackTransport } from './packRegistry.ts';
|
|
|
16
16
|
export const packOf = (
|
|
17
17
|
manifest: { vendor: string; descriptor?: Omit<PackDescriptor, 'vendor'> },
|
|
18
18
|
surface?: DerivedSurface | Record<string, { manifest: DerivedManifest; surface: DerivedSurface }>,
|
|
19
|
-
|
|
19
|
+
/** the lanes beside a pack that has an API of its own (Clerk's `fapi`, PlanetScale's `api`) */
|
|
20
|
+
beside?: Record<string, { manifest: DerivedManifest; surface: DerivedSurface }>,
|
|
21
|
+
): PackDescriptor => packOfAny(manifest, surface, beside);
|
|
20
22
|
/** The pack registered, so the World finds it. */
|
|
21
23
|
export const registerPack = (pack: PackDescriptor): PackDescriptor => registerAny(pack);
|
|
22
24
|
|
|
@@ -27,7 +29,7 @@ export { verifyEvent } from './events.ts';
|
|
|
27
29
|
export { deriveStateSystem, deriveVendorStateSystem } from './derived-real.ts';
|
|
28
30
|
export type { IngestDecl } from './derived-core.ts';
|
|
29
31
|
export type { SocketDecl, SocketEngine, SocketSession } from './sockets.ts';
|
|
30
|
-
export { machinePool, type ExecSpec, type MachinePool, type MachineProcess, type MachineRun, type MachineSpec } from './machines.ts';
|
|
32
|
+
export { machinePool, isMachineCapabilityGap, type ExecSpec, type MachinePool, type MachineProcess, type MachineRun, type MachineSpec, type MachineStatus, type MachineFiles, type MachineFileStat } from './machines.ts';
|
|
31
33
|
export type { CorsDecl } from './cors.ts';
|
|
32
34
|
|
|
33
35
|
// ── the handler contract ───────────────────────────────────────────────────────────────────────
|
|
@@ -51,6 +53,7 @@ export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER } from './request-scope.ts'
|
|
|
51
53
|
// ── the serve factory ──────────────────────────────────────────────────────────────────────────
|
|
52
54
|
export { serveHttp, serveStream, type HttpHandler, type HttpServer, type ServeHttpOptions } from './serve-http.ts';
|
|
53
55
|
export type { TwinStream, TwinStreamConnection, TwinStreamSink } from './twin-fetch.ts';
|
|
56
|
+
export { privateTransportKey } from './private-transport.ts';
|
|
54
57
|
// where a page links a hostname its twin answers, inside the World that shows it
|
|
55
58
|
export { siteLabel, siteUrlOf, twinSiteUrl, twinVendorUrl } from './twin-fetch.ts';
|
|
56
59
|
export { statefulTwinManifest, twinManifest } from './scenario.ts';
|
|
@@ -58,7 +61,7 @@ export { assetContentType, packAsset } from './pack-assets.ts';
|
|
|
58
61
|
export { bytesResponse, contentTypeOf, fileResponse } from './file-response.ts';
|
|
59
62
|
|
|
60
63
|
// ── the kernel's deterministic helpers ─────────────────────────────────────────────────────────
|
|
61
|
-
export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from './signing.ts';
|
|
64
|
+
export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, base62From, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from './signing.ts';
|
|
62
65
|
export type { HandlerCrypto, Jwk, Jwks, JwtVerdict, SigningKey } from './signing.ts';
|
|
63
66
|
export { blobDigest } from './blob-store.ts';
|
|
64
67
|
// compressed bytes a wire carries (a SARIF upload), over web streams
|
|
@@ -69,7 +72,7 @@ export type { TwinResource } from './serve.ts';
|
|
|
69
72
|
// the people a vendor's pages sign in (a `_person` row with its password's hash)
|
|
70
73
|
export { passwordHash, personOf, personWith, recordPerson, type Person } from './people.ts';
|
|
71
74
|
// the World's managed Postgres a handler reaches as `ctx.engine` (managed-database.ts), and a mail's route (`ctx.mail`)
|
|
72
|
-
export { EngineDatabaseError, EngineUnavailableError, quoteIdent
|
|
75
|
+
export { EngineDatabaseError, EngineUnavailableError, quoteIdent } from './managed-database.ts';
|
|
73
76
|
export type { Batch as EngineBatch, ManagedDatabase, Statement as EngineStatement, StatementResult as EngineStatementResult } from './managed-database.ts';
|
|
74
77
|
export type { Mail, SmtpRoute } from './smtp.ts';
|
|
75
78
|
// S3's wire (contract "S3's wire is a kernel library too") and the git plane (contract "The git plane is a kernel library")
|
|
@@ -90,6 +93,9 @@ export * as git from './git/index.ts';
|
|
|
90
93
|
export { parseScenarioDocument, scenarioFaultResult, ScenarioEngine, ScenarioError } from './scenario.ts';
|
|
91
94
|
export type { PackScenarioAdapter, ScenarioDecision, ScenarioDocument, ScenarioExtractorSpec, ScenarioFault, ScenarioFaultResult, ScenarioFeatures, ScenarioHandler, ScenarioMatcher, ScenarioMissRecord, ScenarioStatus } from './scenario.ts';
|
|
92
95
|
|
|
96
|
+
export { verifyAttestationBundle } from './attestation.ts';
|
|
97
|
+
|
|
93
98
|
export { browserSessionToken, startBrowserSession, endBrowserSession } from './browser-session.ts';
|
|
94
99
|
|
|
100
|
+
export { readHostPort } from './host-port.ts';
|
|
95
101
|
export { assertBrowserName, browserOrigin, validateBrowserState, validateBrowserStorageState, validateBrowserContext, browserAcceptLanguage, cookieMatchesHost, type BrowserCookie, type BrowserState, type BrowserStorageState, type BrowserContext, type BrowserIndexedDB } from './browser-state.ts';
|
package/src/log.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseExactJson } from './exact-json.ts';
|
|
1
2
|
// THE LOG (docs/concepts/the-model.md). One kind of entry, one log per
|
|
2
3
|
// branch, and a branch is a parent plus a position in the parent's log:
|
|
3
4
|
//
|
|
@@ -116,7 +117,7 @@ const parsedRows = new WeakMap<object, Map<string, ParsedLog>>();
|
|
|
116
117
|
function parseLines<T>(path: string, lines: string[], offset: number, out: T[]): void {
|
|
117
118
|
for (const [i, line] of lines.entries()) {
|
|
118
119
|
if (!line.trim()) continue;
|
|
119
|
-
try { out.push(
|
|
120
|
+
try { out.push(parseExactJson(line) as T); } catch (error) { throw new Error(`${path}:${offset + i + 1}: invalid JSONL row: ${(error as Error).message}`); }
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
|
|
@@ -173,7 +174,7 @@ function readRows<T>(path: string): T[] {
|
|
|
173
174
|
export function readBranchMeta(service: string, root?: string): BranchMeta | null {
|
|
174
175
|
const raw = getActiveWorldStore().read(branchMetaPath(service, root));
|
|
175
176
|
if (raw === null) return null;
|
|
176
|
-
const meta =
|
|
177
|
+
const meta = parseExactJson(raw) as BranchMeta & { parent?: { root?: string; parentCount?: number; branchCount?: number } };
|
|
177
178
|
// a pointer written before positions were one number: the parent's whole log is parent then branch
|
|
178
179
|
if (meta.parent && meta.parent.at === undefined && meta.parent.root !== undefined) meta.parent = { at: meta.parent.root, position: (meta.parent.parentCount ?? 0) + (meta.parent.branchCount ?? 0) };
|
|
179
180
|
return meta as BranchMeta;
|
|
@@ -324,7 +325,7 @@ export function landedIds(parent: Entry[]): Set<string> {
|
|
|
324
325
|
* the vendor's subject onto the landed one, nor send a caller who addresses the vendor's id there. */
|
|
325
326
|
export function aliasesFrom(parent: Entry[]): Map<string, string> {
|
|
326
327
|
const vendorHeld = new Set<string>();
|
|
327
|
-
for (const e of parent) if (e.batch !== undefined || e.operation === 'observed' || e.event !== undefined || e.landsId !== undefined) vendorHeld.add(`${e.subject.type}:${e.subject.id}`);
|
|
328
|
+
for (const e of parent) if (e.batch !== undefined || e.operation === 'observed' || e.event !== undefined || (e.landsId !== undefined && e.receipt?.status === 'deployed')) vendorHeld.add(`${e.subject.type}:${e.subject.id}`);
|
|
328
329
|
const out = new Map<string, string>();
|
|
329
330
|
for (const e of parent) {
|
|
330
331
|
if (!e.aliasOf || e.aliasOf === e.subject.id) continue;
|
|
@@ -397,7 +398,7 @@ export function treeResources(tree: Tree): TwinResource[] {
|
|
|
397
398
|
return [...tree.values()].map((s) => {
|
|
398
399
|
const out: Record<string, unknown> = { id: s.id, type: s.type, updatedAt: s.updatedAt };
|
|
399
400
|
const own: Record<string, unknown> = {};
|
|
400
|
-
for (const [k, v] of Object.entries(s.fields)) { if (!META.has(k)) out[k] = v; else if (v !== undefined
|
|
401
|
+
for (const [k, v] of Object.entries(s.fields)) { if (!META.has(k)) out[k] = v; else if (v !== undefined) own[k] = v; }
|
|
401
402
|
if (Object.keys(own).length) Object.defineProperty(out, OWN, { value: own, enumerable: false });
|
|
402
403
|
return out as TwinResource;
|
|
403
404
|
});
|
|
@@ -416,7 +417,10 @@ export function ownFields(r: TwinResource): Record<string, unknown> {
|
|
|
416
417
|
const { id: _id, type: _type, updatedAt: _at, ...rest } = r;
|
|
417
418
|
const own = ((r as unknown as Record<symbol, unknown>)[OWN] ?? {}) as { id?: unknown; type?: unknown; updatedAt?: unknown };
|
|
418
419
|
// restored first: a vendor's object leads with its id, and a reader's grep may count on the order
|
|
419
|
-
|
|
420
|
+
const out = { ...(own.id !== undefined ? { id: own.id } : {}), ...(own.type !== undefined ? { type: own.type } : {}), ...rest, ...(own.updatedAt !== undefined ? { updatedAt: own.updatedAt } : {}) };
|
|
421
|
+
// the own record rides along unseen, so an own view taken of an own view is the same view (never one that drops them)
|
|
422
|
+
if ((r as unknown as Record<symbol, unknown>)[OWN] !== undefined) Object.defineProperty(out, OWN, { value: own, enumerable: false });
|
|
423
|
+
return out;
|
|
420
424
|
}
|
|
421
425
|
|
|
422
426
|
/** `facts`, when present, is the parent and branch logs' size and version with branch.json at the cut
|
|
@@ -598,7 +602,7 @@ function readTreeMapFull(service: string, root?: string, opts: { until?: string;
|
|
|
598
602
|
const facts = opts.until === undefined ? `${parentKeyOf(service, root)}#${statKey(branchLogPath(service, root))}` : null;
|
|
599
603
|
if (facts !== null) {
|
|
600
604
|
const held = getActiveWorldStore().read(checkpointPath(service, root));
|
|
601
|
-
const cut = held === null ? null :
|
|
605
|
+
const cut = held === null ? null : parseExactJson(held) as Checkpoint;
|
|
602
606
|
if (cut?.facts === facts) return new Map(cut.subjects.map(s => [`${s.type}:${s.id}`, { ...s, fields: { ...s.fields } }]));
|
|
603
607
|
}
|
|
604
608
|
const inherited = inheritedHistory(service, root);
|
|
@@ -610,7 +614,7 @@ function readTreeMapFull(service: string, root?: string, opts: { until?: string;
|
|
|
610
614
|
const branch = until < 0 ? branchAll : branchAll.slice(0, until);
|
|
611
615
|
const parentDigest = hashFieldValue({ inherited, own }); const branchDigest = hashFieldValue(branch);
|
|
612
616
|
const raw = opts.until === undefined ? getActiveWorldStore().read(checkpointPath(service, root)) : null;
|
|
613
|
-
const cp = raw === null ? null :
|
|
617
|
+
const cp = raw === null ? null : parseExactJson(raw) as Checkpoint;
|
|
614
618
|
if (cp?.parentDigest === parentDigest && cp.branchDigest === branchDigest) return new Map(cp.subjects.map(s => [`${s.type}:${s.id}`, { ...s, fields: { ...s.fields } }]));
|
|
615
619
|
const layout: HistoryLayout = { ...(inherited ? { base: inherited.layout } : {}), parent: own.length, branch: branch.length };
|
|
616
620
|
const incremental = cp?.parentDigest === parentDigest && cp.branchCount <= branch.length && hashFieldValue(branch.slice(0, cp.branchCount)) === cp.branchDigest && !branch.slice(cp.branchCount).some(e => e.op === 'revert');
|