@volter/world-core 2.0.37 → 3.0.0
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/README.md +4 -5
- package/app-route.cjs +12 -6
- package/app-route.d.cts +1 -1
- package/dist/app-route.cjs +12 -6
- package/dist/app-route.d.cts +1 -1
- package/dist/generated/pack-facts.json +1410 -3069
- package/dist/inject.cjs +64 -9
- package/dist/pack-facts.cjs +44 -0
- package/dist/src/actions.d.ts +3 -3
- package/dist/src/actions.js +22 -16
- package/dist/src/ancestry.d.ts +14 -2
- package/dist/src/ancestry.js +92 -2
- package/dist/src/anthropic-wire.d.ts +39 -0
- package/dist/src/anthropic-wire.js +136 -0
- package/dist/src/bytes.d.ts +7 -0
- package/dist/src/bytes.js +35 -0
- package/dist/src/changeset.d.ts +1 -1
- package/dist/src/changeset.js +0 -0
- package/dist/src/clickhouse/index.d.ts +3 -0
- package/dist/src/clickhouse/index.js +6 -0
- package/dist/src/clickhouse/sql.d.ts +233 -0
- package/dist/src/clickhouse/sql.js +4329 -0
- package/dist/src/clickhouse/types.d.ts +18 -0
- package/dist/src/clickhouse/types.js +47 -0
- package/dist/src/clickhouse/values.d.ts +146 -0
- package/dist/src/clickhouse/values.js +858 -0
- package/dist/src/client-bundle.js +2 -3
- package/dist/src/cors.d.ts +15 -0
- package/dist/src/cors.js +31 -0
- package/dist/src/derived-core.d.ts +487 -24
- package/dist/src/derived-core.js +788 -144
- package/dist/src/derived-real.d.ts +13 -0
- package/dist/src/derived-real.js +518 -0
- package/dist/src/derived.d.ts +35 -1
- package/dist/src/derived.js +61 -9
- package/dist/src/emit.js +1 -2
- package/dist/src/events.d.ts +206 -0
- package/dist/src/events.js +341 -0
- package/dist/src/executor.d.ts +3 -0
- package/dist/src/executor.js +19 -2
- package/dist/src/file-response.d.ts +6 -0
- package/dist/src/file-response.js +30 -0
- package/dist/src/fork.js +3 -2
- package/dist/src/git/history.d.ts +7 -0
- package/dist/src/git/history.js +24 -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 +28 -0
- package/dist/src/git/lfs.js +66 -0
- package/dist/src/git/objects.js +3 -8
- package/dist/src/git/smart-http.d.ts +3 -1
- package/dist/src/git/smart-http.js +67 -6
- package/dist/src/graphql-wire.d.ts +29 -0
- package/dist/src/graphql-wire.js +101 -0
- package/dist/src/grpc-wire.d.ts +67 -0
- package/dist/src/grpc-wire.js +170 -0
- package/dist/src/h2.d.ts +40 -0
- package/dist/src/h2.js +656 -0
- package/dist/src/head.d.ts +32 -3
- package/dist/src/head.js +161 -40
- package/dist/src/history.d.ts +1 -1
- package/dist/src/history.js +6 -6
- package/dist/src/hpack.json +1 -0
- package/dist/src/index.d.ts +64 -75
- package/dist/src/index.js +58 -101
- package/dist/src/log.js +28 -19
- package/dist/src/machines.d.ts +50 -0
- package/dist/src/machines.js +151 -0
- package/dist/src/managed-database.d.ts +86 -0
- package/dist/src/managed-database.js +283 -0
- package/dist/src/multipart.d.ts +11 -0
- package/dist/src/multipart.js +51 -0
- package/dist/src/observe.d.ts +15 -5
- package/dist/src/observe.js +23 -9
- package/dist/src/openai-wire.d.ts +108 -0
- package/dist/src/openai-wire.js +337 -0
- package/dist/src/pack-assets.d.ts +3 -4
- package/dist/src/pack-assets.js +15 -10
- package/dist/src/pack-fetch.d.ts +77 -0
- package/dist/src/pack-fetch.js +449 -0
- package/dist/src/pack-paths.d.ts +12 -0
- package/dist/src/pack-paths.js +86 -0
- package/dist/src/packRegistry.d.ts +69 -162
- package/dist/src/packRegistry.js +55 -20
- package/dist/src/people.d.ts +13 -0
- package/dist/src/people.js +18 -0
- package/dist/src/placeholder-image.d.ts +5 -0
- package/dist/src/placeholder-image.js +114 -0
- package/dist/src/protobuf.d.ts +28 -0
- package/dist/src/protobuf.js +332 -0
- package/dist/src/redis/engine.js +1 -1
- package/dist/src/request-scope.d.ts +1 -1
- package/dist/src/request-scope.js +6 -4
- package/dist/src/resource-blob.d.ts +5 -0
- package/dist/src/resource-blob.js +11 -0
- package/dist/src/runtime.d.ts +85 -0
- package/dist/src/runtime.js +104 -0
- package/dist/src/s3/wire.d.ts +60 -0
- package/dist/src/s3/wire.js +157 -0
- package/dist/src/scenario.d.ts +3 -0
- package/dist/src/scenario.js +2 -0
- package/dist/src/schema-sample.d.ts +1 -0
- package/dist/src/schema-sample.js +21 -0
- package/dist/src/sealed-box.d.ts +14 -0
- package/dist/src/sealed-box.js +225 -0
- package/dist/src/serve-http.d.ts +14 -0
- package/dist/src/serve-http.js +27 -3
- package/dist/src/serve.d.ts +6 -0
- package/dist/src/serve.js +69 -14
- package/dist/src/signing.d.ts +135 -0
- package/dist/src/signing.js +222 -0
- package/dist/src/sigv4.d.ts +48 -0
- package/dist/src/sigv4.js +167 -0
- package/dist/src/smtp.d.ts +16 -0
- package/dist/src/smtp.js +72 -0
- package/dist/src/sockets.d.ts +51 -0
- package/dist/src/sockets.js +90 -0
- package/dist/src/state-system.d.ts +1 -0
- package/dist/src/state-system.js +1 -1
- package/dist/src/storage.d.ts +1 -1
- package/dist/src/storage.js +3 -3
- package/dist/src/trace-context.js +1 -1
- package/dist/src/twin-fetch.d.ts +0 -7
- package/dist/src/twin-fetch.js +0 -14
- package/dist/src/vendor-call.d.ts +6 -0
- package/dist/src/vendor-call.js +41 -0
- package/dist/src/world-store.js +1 -1
- package/dist/vendor-hosts.cjs +36 -125
- package/dist/vendor-hosts.d.cts +8 -0
- package/generated/pack-facts.json +1410 -3069
- package/inject.cjs +64 -9
- package/pack-facts.cjs +44 -0
- package/package.json +17 -3
- package/src/actions.ts +23 -16
- package/src/ancestry.ts +74 -2
- package/src/anthropic-wire.ts +137 -0
- package/src/bytes.ts +42 -0
- package/src/changeset.ts +5 -5
- package/src/clickhouse/index.ts +6 -0
- package/src/clickhouse/sql.ts +3059 -0
- package/src/clickhouse/types.ts +44 -0
- package/src/clickhouse/values.ts +697 -0
- package/src/client-bundle.ts +2 -3
- package/src/cors.ts +34 -0
- package/src/derived-core.ts +1013 -146
- package/src/derived-real.ts +434 -0
- package/src/derived.ts +73 -3
- package/src/emit.ts +1 -2
- package/src/events.ts +449 -0
- package/src/executor.ts +24 -2
- package/src/file-response.ts +27 -0
- package/src/fork.ts +3 -2
- package/src/git/history.ts +19 -0
- package/src/git/index.ts +1 -0
- package/src/git/lfs.ts +67 -0
- package/src/git/objects.ts +3 -5
- package/src/git/smart-http.ts +56 -6
- package/src/graphql-wire.ts +106 -0
- package/src/grpc-wire.ts +159 -0
- package/src/h2.ts +627 -0
- package/src/head.ts +132 -41
- package/src/history.ts +6 -6
- package/src/hpack.json +1 -0
- package/src/index.ts +82 -329
- package/src/log.ts +27 -18
- package/src/machines.ts +151 -0
- package/src/managed-database.ts +299 -0
- package/src/multipart.ts +51 -0
- package/src/observe.ts +31 -15
- package/src/openai-wire.ts +371 -0
- package/src/pack-assets.ts +15 -11
- package/src/pack-fetch.ts +458 -0
- package/src/pack-paths.ts +72 -0
- package/src/packRegistry.ts +79 -167
- package/src/people.ts +31 -0
- package/src/placeholder-image.ts +88 -0
- package/src/protobuf.ts +251 -0
- package/src/redis/engine.ts +1 -1
- package/src/request-scope.ts +8 -4
- package/src/resource-blob.ts +13 -0
- package/src/runtime.ts +344 -0
- package/src/s3/wire.ts +172 -0
- package/src/scenario.ts +4 -0
- package/src/schema-sample.ts +24 -0
- package/src/sealed-box.ts +182 -0
- package/src/serve-http.ts +31 -3
- package/src/serve.ts +58 -14
- package/src/signing.ts +231 -0
- package/src/sigv4.ts +158 -0
- package/src/smtp.ts +76 -0
- package/src/sockets.ts +140 -0
- package/src/state-system.ts +2 -2
- package/src/storage.ts +3 -3
- package/src/trace-context.ts +1 -1
- package/src/twin-fetch.ts +0 -20
- package/src/vendor-call.ts +41 -0
- package/src/world-store.ts +1 -1
- package/vendor-hosts.cjs +36 -125
- package/vendor-hosts.d.cts +8 -0
- package/dist/src/mirror-shell.d.ts +0 -2
- package/dist/src/mirror-shell.js +0 -13
- package/dist/src/v1-removed.d.ts +0 -159
- package/dist/src/v1-removed.js +0 -124
- package/src/mirror-shell.ts +0 -15
- package/src/v1-removed.ts +0 -172
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type RateBudgetDeclaration } from './rateBudget.js';
|
|
2
2
|
import type { TwinAuthStrategy } from './executor.js';
|
|
3
3
|
import { type ReferenceDeclaration } from './references.js';
|
|
4
|
+
import type { DerivedManifest } from './derived-core.js';
|
|
5
|
+
import type { DerivedSurface } from './derived.js';
|
|
4
6
|
import type { TwinEmitter } from './emit.js';
|
|
5
7
|
import { type StateSystemAdapters } from './state-system.js';
|
|
6
8
|
/**
|
|
@@ -10,13 +12,13 @@ import { type StateSystemAdapters } from './state-system.js';
|
|
|
10
12
|
* `browserRouting`, and no entry in the injector's `VENDOR_HOSTS` host map is possible (the
|
|
11
13
|
* injector patches http/fetch and never sees the traffic). Such a pack is wired into a world
|
|
12
14
|
* through app-read host/port env instead, and this value is what tells a reader that "no injector
|
|
13
|
-
* entry" is STRUCTURAL rather than a missing wiring point.
|
|
14
|
-
* is the first;
|
|
15
|
+
* entry" is STRUCTURAL rather than a missing wiring point. `@volter/twin-smtp` (a line protocol)
|
|
16
|
+
* is the first; `@volter/twin-temporal` (gRPC: HTTP/2 framing its clients drive themselves) the
|
|
15
17
|
* second.
|
|
16
18
|
*
|
|
17
19
|
* Deliberately the transport CLASS, not the protocol name: naming the wire protocol would put a
|
|
18
20
|
* vendor id in the kernel the moment a pack is named after its protocol, which is exactly what
|
|
19
|
-
*
|
|
21
|
+
* A2, "the kernel does not branch on vendor identity" (`scripts/architecture-auto.ts`), forbids. The
|
|
20
22
|
* specific protocol belongs on the pack's own `specSource`/`description`.
|
|
21
23
|
*/
|
|
22
24
|
export type PackTransport = 'rest' | 'graphql' | 'web-api' | 'raw-tcp';
|
|
@@ -24,7 +26,7 @@ export type PackTransport = 'rest' | 'graphql' | 'web-api' | 'raw-tcp';
|
|
|
24
26
|
* The pack's SERVE-FAMILY — the axis the invariant matrix keys strictness off, DECLARED
|
|
25
27
|
* because it is the pack's own claim about what kind of thing it is (a heuristic over file
|
|
26
28
|
* shapes would be the loose-scan disease). Orthogonal facts stay derived: transport is its
|
|
27
|
-
* own field,
|
|
29
|
+
* own field, screens are the manifest's, webhooks are the events module.
|
|
28
30
|
* 'crud' — stateful resource CRUD behind the vendor's API (the default family;
|
|
29
31
|
* includes ingestion→grouping packs — the ingest door is a trait).
|
|
30
32
|
* 'generative' — model-shaped surface serving deterministic stubs/scenarios; "cannot
|
|
@@ -37,16 +39,16 @@ export type PackTransport = 'rest' | 'graphql' | 'web-api' | 'raw-tcp';
|
|
|
37
39
|
*/
|
|
38
40
|
/** THE PLATFORM PROTOCOL VERSION (runtime contract R16): `major.minor`. The major names the
|
|
39
41
|
* descriptor shape, the factory signature, the doors and the adapter contract a package is
|
|
40
|
-
* written against
|
|
41
|
-
* change bumps minor. A package declares the major
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
export declare const PROTOCOL_VERSION = "
|
|
42
|
+
* written against, and the build form it is made in (the derived pack); a change that removes or
|
|
43
|
+
* alters a required shape bumps it, an additive change bumps minor. A package declares the major
|
|
44
|
+
* it targets (`protocol`), and the kernel serves its own major and no other: a package that
|
|
45
|
+
* declares another, or none, is refused. */
|
|
46
|
+
export declare const PROTOCOL_VERSION = "3.0";
|
|
45
47
|
export declare const PROTOCOL_MAJOR: number;
|
|
46
|
-
/** Where a package's declared protocol stands against this kernel
|
|
48
|
+
/** Where a package's declared protocol stands against this kernel: `current`, or `refused`. */
|
|
47
49
|
export declare function protocolStanding(declared: string | undefined): {
|
|
48
50
|
major: number;
|
|
49
|
-
standing: 'current' | '
|
|
51
|
+
standing: 'current' | 'refused';
|
|
50
52
|
};
|
|
51
53
|
export declare const PACK_ARCHETYPES: readonly ["crud", "generative", "signed-protocol", "engine-control", "proxy"];
|
|
52
54
|
export type PackArchetype = (typeof PACK_ARCHETYPES)[number];
|
|
@@ -123,8 +125,7 @@ export declare function pullOnSchedule<T>(vendor: PullVendor, pull: () => Promis
|
|
|
123
125
|
* hostname — regional families like `^s3[.-][a-z0-9-]+\.amazonaws\.com$`). `pathPattern` (a
|
|
124
126
|
* RegExp source over the pathname) splits a host shared between packs. `key` names the routing
|
|
125
127
|
* identity the rule belongs to — the `<KEY>_TWIN_URL` env stem — when it is not the pack's own
|
|
126
|
-
* vendor id (aws's
|
|
127
|
-
* `googleauth` token exchange); a key belongs to ONE pack. `exclude: true` carves a host out of
|
|
128
|
+
* vendor id (aws's one twin answers under s3 and secretsmanager); a key belongs to ONE pack. `exclude: true` carves a host out of
|
|
128
129
|
* the key's includes (`.upstash.io` minus `*-vector.upstash.io`): a key matches when any include
|
|
129
130
|
* matches and no exclude does. Validated by scripts/pack-facts.ts: keys are `[a-z0-9-]+`, a
|
|
130
131
|
* foreign key is never another pack's vendor id, belongs to one pack, and must appear in the
|
|
@@ -148,186 +149,95 @@ export type RoundTripWrite = {
|
|
|
148
149
|
body?: unknown;
|
|
149
150
|
headers?: Record<string, string>;
|
|
150
151
|
};
|
|
152
|
+
/** A pack's descriptor: docs/contributing/architecture.md, "The descriptor". */
|
|
151
153
|
export type TwinPack = {
|
|
152
|
-
/** vendor id / service, e.g. 'stripe'. */
|
|
153
154
|
vendor: string;
|
|
154
155
|
transport: PackTransport;
|
|
155
|
-
/** Optional native frontend of the same state owner, selected explicitly on the pack CLI. */
|
|
156
|
-
nativeTransport?: {
|
|
157
|
-
protocol: string;
|
|
158
|
-
flag: string;
|
|
159
|
-
upstreamEnv: string;
|
|
160
|
-
};
|
|
161
|
-
/** The serve-family (see PackArchetype). Every pack declares one. */
|
|
162
156
|
archetype?: PackArchetype;
|
|
163
|
-
/** The platform protocol MAJOR this package targets (R16), e.g. '1'. Undeclared reads as the
|
|
164
|
-
* previous major (deprecated), or as the current major (recorded as assumed) while it is the first;
|
|
165
|
-
* the scaffolder declares it. */
|
|
166
157
|
protocol?: string;
|
|
167
|
-
/** Pack-shipped assets (R20): files or directories under the pack dir the serve path reads through
|
|
168
|
-
* the pack-asset seam (`getActivePackAssets`), never off the host. Declared so the deploy step
|
|
169
|
-
* can place them behind the workerd assets binding. Paths relative to the pack dir. */
|
|
170
158
|
assets?: string[];
|
|
171
|
-
/** R2, resource level: declared subject types the twin genuinely serves but no replay can create —
|
|
172
|
-
* born only of the vendor's own catalog, scheduler, account or a pull. Each names WHY. The cell
|
|
173
|
-
* stays debt (adopted, never faked), but legibly: the runner separates these from open gaps. */
|
|
174
159
|
resourcesUnreachable?: Record<string, string>;
|
|
175
|
-
/** subject types the twin serves, e.g. ['customer','charge','payment_intent']. */
|
|
176
160
|
resources: string[];
|
|
177
|
-
/** the `world-<vendor>` operator bin, if any. */
|
|
178
161
|
bin?: string;
|
|
179
|
-
/** conformance field map { object: { field: type } } — vendored or derived from the spec. */
|
|
180
162
|
conformanceFields?: Record<string, Record<string, string>>;
|
|
181
|
-
/** where the exact surface came from (a spec path) — provenance for re-derivation. */
|
|
182
163
|
specSource?: string;
|
|
183
|
-
/** one-line human description. */
|
|
184
164
|
description?: string;
|
|
185
|
-
/** How this vendor's BROWSER SDK addresses its API — used by the zero-edit dev proxy so
|
|
186
|
-
* the kernel proxy stays vendor-agnostic (it forwards/rewrites by these values, never by a
|
|
187
|
-
* hardcoded vendor table). `apiPathPrefix`: the same-origin path the browser SDK calls
|
|
188
|
-
* (e.g. Stripe.js → '/v1/'). `loaderHost`: the absolute API host to strip from the loaded
|
|
189
|
-
* SDK so its calls become same-origin (e.g. 'https://api.stripe.com'). Omit for vendors
|
|
190
|
-
* with no browser SDK. */
|
|
191
165
|
browserRouting?: {
|
|
192
166
|
apiPathPrefix: string;
|
|
193
167
|
loaderHost?: string;
|
|
194
168
|
};
|
|
195
|
-
/** How often this vendor's REAL API may be pulled. Omitted ⇒ `'on-demand'` (see `PullPosture`).
|
|
196
|
-
* Declare `'continuous'` only for a vendor whose published limits genuinely tolerate a
|
|
197
|
-
* scheduled sync, and say why in `pullPostureReason`. */
|
|
198
169
|
pullPosture?: PullPosture;
|
|
199
|
-
/** Why this posture — the limits that justify it. Required in review for `'continuous'`, since
|
|
200
|
-
* that is the claim that can cost a lockout if it's wrong. */
|
|
201
170
|
pullPostureReason?: string;
|
|
202
|
-
/**
|
|
203
|
-
* The vendor's CLIENT-SIDE RATE BUDGET — the ceiling, window and per-endpoint weights the pack's
|
|
204
|
-
* guarded connector enforces before a live call goes out. Vendor knowledge as DATA, exactly like
|
|
205
|
-
* `browserRouting`: the mechanism is the kernel's (`rateBudget.ts`), the numbers are the pack's.
|
|
206
|
-
*
|
|
207
|
-
* `registerPack` forwards this to `declareRateBudget`, so registering a pack arms its budget.
|
|
208
|
-
* A pack that omits it is NOT unlimited — any budget built for that vendor falls back to
|
|
209
|
-
* `DEFAULT_RATE_BUDGET` (see its docstring). `pullPosture` says "do not SCHEDULE this vendor";
|
|
210
|
-
* this says "and here is the ceiling on an EXPLICIT pull". They are complementary, not
|
|
211
|
-
* substitutes — the posture guards cadence seams, the budget guards the call itself.
|
|
212
|
-
*/
|
|
213
171
|
rateBudget?: RateBudgetDeclaration;
|
|
214
|
-
/**
|
|
215
|
-
* The pack's DELIVER support (`emit` — see emit.ts): how to synthesize this vendor's
|
|
216
|
-
* signed event/webhook deliveries from current twin state. Vendor knowledge on the
|
|
217
|
-
* descriptor, like `browserRouting`; the kernel engine (`emitTwinEvent`) is generic.
|
|
218
|
-
* The operator surface is the pack's own bin (`world-<vendor> emit`); a consumer that
|
|
219
|
-
* registered the pack can also drive it via `volter-twin emit <vendor>`.
|
|
220
|
-
*/
|
|
221
172
|
emitter?: TwinEmitter;
|
|
222
|
-
/**
|
|
223
|
-
* SERVE FACTORY OVERRIDE — normally DERIVED, declared only under ambiguity. The colocated
|
|
224
|
-
* host (world-runtime/src/host.ts) mounts a pack by its `create<Name>TwinServer` factory
|
|
225
|
-
* export (`({port,root,readOnly}) => {port,stop}`); `scripts/pack-facts.ts` reads that
|
|
226
|
-
* export's name off the module, so a pack with exactly ONE such export declares nothing.
|
|
227
|
-
* A pack exporting SEVERAL factories declares here which one `cli.ts serve` would have
|
|
228
|
-
* booted — the judgment the exports alone cannot reveal (linear serves its DERIVED server
|
|
229
|
-
* by default; the hand-written one is behind a flag). Must name a function export of the
|
|
230
|
-
* pack's index module matching /^create\w*TwinServer$/.
|
|
231
|
-
*/
|
|
232
173
|
serveExport?: string;
|
|
233
|
-
/**
|
|
234
|
-
* ADOPTION — how application repos betray that they talk to this vendor, so
|
|
235
|
-
* `volter-world covers`/`init` can attribute the signal to this pack. Vendor knowledge as
|
|
236
|
-
* DATA, same doctrine as `browserRouting`/`rateBudget`: the detector mechanism lives in
|
|
237
|
-
* world-runtime; the names live here. Absorbs the central `SDK_TWINS` / `SDK_SCOPE_VENDORS` /
|
|
238
|
-
* `ENV_STEM_VENDORS` / `VENDOR_WORLD_IDS` maps (the one wiring point NO gate enforced —
|
|
239
|
-
* the class that let `@planetscale/database` escape, twin#255).
|
|
240
|
-
*/
|
|
241
174
|
adoption?: {
|
|
242
|
-
/** every official npm client of the API surface this pack models, e.g. ['stripe']. */
|
|
243
175
|
sdks?: string[];
|
|
244
|
-
/** PyPI distribution names (PEP 503 normalized: lowercase, `-`) the vendor's Python SDKs
|
|
245
|
-
* ship under — the Python half of adoption discovery and coverage (`covers`). */
|
|
246
176
|
pypi?: string[];
|
|
247
|
-
/** npm scope prefixes whose members all belong to this vendor, e.g. ['@upstash/']. */
|
|
248
177
|
scopes?: string[];
|
|
249
|
-
/** credential-env-var stems, e.g. ['STRIPE'] for STRIPE_SECRET_KEY et al. */
|
|
250
178
|
envStems?: string[];
|
|
251
|
-
|
|
179
|
+
configFiles?: Array<{
|
|
180
|
+
file: string;
|
|
181
|
+
usage: 'application' | 'build' | 'deployment';
|
|
182
|
+
when?: string;
|
|
183
|
+
}>;
|
|
252
184
|
worldIds?: string[];
|
|
253
|
-
/** Vendor-facing tool packages whose calls belong to supporting workflow rather than the
|
|
254
|
-
* application itself. The use is saved and selected by World discovery policy. */
|
|
255
185
|
tools?: Array<{
|
|
256
186
|
package: string;
|
|
257
187
|
usage: 'build' | 'deployment';
|
|
258
188
|
}>;
|
|
259
189
|
};
|
|
260
|
-
/**
|
|
261
|
-
* INTERCEPTION — the vendor hosts whose traffic the injector must route to this twin,
|
|
262
|
-
* as serializable data (the committed `inject.cjs` table is GENERATED from these — it must
|
|
263
|
-
* stay dependency-free preloaded CJS, so it consumes compiled output, never imports packs).
|
|
264
|
-
* Exactly one of `hosts` or `hostsNone` per pack once migration completes: silence is not a
|
|
265
|
-
* ruling. `pathPattern` (a RegExp source string, applied to the URL pathname) splits shared
|
|
266
|
-
* hosts (the youtube/googleauth case). Absorbed `VENDOR_HOSTS`'s per-pack keys + the retired NO_INJECTOR_ENTRY
|
|
267
|
-
* allowlist (hostsNone IS the ruling now).
|
|
268
|
-
*/
|
|
269
190
|
hosts?: HostRule[];
|
|
270
|
-
/** Why this pack deliberately has NO injector entry (explicit-endpoint wiring only). */
|
|
271
191
|
hostsNone?: string;
|
|
272
|
-
/**
|
|
273
|
-
* HOSTS A TWIN CLAIMS WHILE IT RUNS — names a person makes answer to this vendor (a custom domain connected to a
|
|
274
|
-
* bucket), which no descriptor can list. The twin lists the ones it answers now at its own `door` (GET, answering
|
|
275
|
-
* `{ "hosts": [...] }`), and the World routes them to it as DNS would the vendor's; a host a descriptor's `hosts`
|
|
276
|
-
* names is never taken from another pack this way.
|
|
277
|
-
*/
|
|
278
192
|
hostsClaimed?: {
|
|
279
193
|
door: string;
|
|
280
194
|
note: string;
|
|
281
195
|
};
|
|
282
|
-
/**
|
|
283
|
-
* WORLD WIRING — the env var the vendor's own SDK documents for overriding its base URL,
|
|
284
|
-
* which `volter-world init` injects pointing at the twin. `endpointEnvNone` declares the
|
|
285
|
-
* deliberate absence WITH its reason (inventing a var the app never reads would make
|
|
286
|
-
* `covers` report coverage while traffic still reaches the real vendor — the exact lie the
|
|
287
|
-
* proof exists to catch). Absorbs init.ts's `APP_READ_ENDPOINT_ENV` map, where these
|
|
288
|
-
* reasons lived as comments. Exactly one of the two once migration completes.
|
|
289
|
-
*/
|
|
290
196
|
endpointEnv?: {
|
|
291
197
|
name: string;
|
|
292
198
|
templates?: Record<string, string>;
|
|
293
199
|
note: string;
|
|
294
200
|
};
|
|
295
|
-
/** Why this pack deliberately injects no endpoint env (see `endpointEnv`). */
|
|
296
201
|
endpointEnvNone?: string;
|
|
297
|
-
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
202
|
+
/** The door that issues the application the credentials the twin will accept (a key made on the vendor's dashboard,
|
|
203
|
+
* and whatever names it: a database's URL, a project's id), and the env names it fills, each from a field of the
|
|
204
|
+
* answer: `init` writes `$issue:<service>` for each, and `up`, once this twin is running, POSTs the body to the door
|
|
205
|
+
* and sets each name to its field. A twin that holds its keys (refusing any it
|
|
206
|
+
* did not issue) declares one, so a World's applications hold keys it issued rather than fixtures it refuses. */
|
|
207
|
+
credentialDoor?: {
|
|
208
|
+
path: string;
|
|
209
|
+
body: Record<string, unknown>;
|
|
210
|
+
fill: Record<string, string>;
|
|
211
|
+
};
|
|
212
|
+
/** Another pack of this vendor issues the API's credentials; architecture, descriptor World wiring. */
|
|
213
|
+
credentialIssuer?: string;
|
|
305
214
|
prismaAdapter?: {
|
|
306
215
|
adapter: string;
|
|
307
216
|
export: string;
|
|
308
217
|
urlEnv: string;
|
|
309
218
|
};
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
* the spawn path, the factory's `database` option in the colocated host. A World whose database is not its own
|
|
316
|
-
* managed infrastructure (none declared, or a URL outside it) binds nothing, and the twin refuses its data plane.
|
|
317
|
-
*/
|
|
219
|
+
managedService?: {
|
|
220
|
+
kind: 'redis' | 'mongodb';
|
|
221
|
+
scheme: string;
|
|
222
|
+
note: string;
|
|
223
|
+
};
|
|
318
224
|
managedDatabase?: {
|
|
319
225
|
kind: 'postgres';
|
|
320
226
|
arg: string;
|
|
321
227
|
note: string;
|
|
322
228
|
};
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
229
|
+
sidePorts?: Array<{
|
|
230
|
+
name: string;
|
|
231
|
+
option: string;
|
|
232
|
+
flag: string;
|
|
233
|
+
path?: string;
|
|
234
|
+
note: string;
|
|
235
|
+
}>;
|
|
326
236
|
stateSystem?: StateSystemAdapters;
|
|
327
|
-
/**
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
237
|
+
/** The pack has no vendor-backed half, and why (copied from the manifest by packOf). */
|
|
238
|
+
vendorBacked?: {
|
|
239
|
+
none: string;
|
|
240
|
+
};
|
|
331
241
|
refresh?: {
|
|
332
242
|
every?: string;
|
|
333
243
|
webhook?: boolean;
|
|
@@ -335,51 +245,48 @@ export type TwinPack = {
|
|
|
335
245
|
atMost: string;
|
|
336
246
|
};
|
|
337
247
|
};
|
|
338
|
-
/** PROTOCOL 2 — a minimal write on the vendor's wire the kernel can send blind (or a short
|
|
339
|
-
* sequence whose LAST write creates something new every time): the branch round-trip
|
|
340
|
-
* (docs/contributing/architecture.md#evidence) sends it, checkpoints, branches, sends it again on the branch, and
|
|
341
|
-
* proves the tree contract with no pack code. */
|
|
342
248
|
roundTrip?: RoundTripWrite | RoundTripWrite[];
|
|
343
|
-
/** PROTOCOL 2 — which fields of which subject types hold another subject's id (docs/contributing/architecture.md
|
|
344
|
-
* #alias-aware-lookup-at-the-request-boundary): the kernel resolves them through an adopted vendor id, in the tree and at the perform. */
|
|
345
249
|
references?: ReferenceDeclaration[];
|
|
346
|
-
/** PROTOCOL 2 — with `references`: a write on the wire that references the `roundTrip` write's subject;
|
|
347
|
-
* `{{field}}` in its path or body is the referenced subject's field. The round-trip gate sends it
|
|
348
|
-
* after simulating the parent's adoption and checks the reference followed. */
|
|
349
250
|
referenceTrip?: RoundTripWrite;
|
|
350
|
-
/** PROTOCOL 2 — shape parity (roadmap 4c): the origin the parity gate hands the refresh adapter when it
|
|
351
|
-
* refreshes the twin from itself (`http://twin` + whatever path the adapter reads its scope from), and
|
|
352
|
-
* `'held'` once the write handler and the refresh adapter store the same shape (the gate then asserts). */
|
|
353
251
|
parityOrigin?: string;
|
|
354
252
|
shapeParity?: 'held';
|
|
355
|
-
/** PROTOCOL 2 — a refresh that cannot be exercised for effect when the parity gate refreshes the twin
|
|
356
|
-
* from itself, and the refusal it answers with instead: `refusal` is text its thrown error carries,
|
|
357
|
-
* `reason` why (an identity pull the twin's own wire refuses without a consent-minted token; a pulled
|
|
358
|
-
* id inside the twin's reserved local namespace). The gate runs the refresh and passes it only on
|
|
359
|
-
* that refusal; any other error fails. */
|
|
360
253
|
refreshRefusal?: {
|
|
361
254
|
refusal: string;
|
|
362
255
|
reason: string;
|
|
363
256
|
};
|
|
364
|
-
/** PROTOCOL 2 — HOW THIS VENDOR AUTHENTICATES, so the kernel executor can apply the sealed
|
|
365
|
-
* credential the way the vendor actually reads it (docs/concepts/the-model.md#the-rules, rule 5). Absent means header
|
|
366
|
-
* replacement, which is what `credential.headers` has always done — declare this ONLY for a
|
|
367
|
-
* vendor that reads its key from the query string or wants a signature computed per request. */
|
|
368
257
|
auth?: TwinAuthStrategy;
|
|
369
|
-
/** PROTOCOL 2 — the ENGINE beside the tree, when the pack's state has a second half that is not a
|
|
370
|
-
* projection (a git plane, S3 bytes, a SQL engine): the module that owns every write outside the
|
|
371
|
-
* world store. The tree references engine objects by hash; the gate keeps such writes there. */
|
|
372
258
|
engine?: {
|
|
373
259
|
module: string;
|
|
374
260
|
note?: string;
|
|
375
261
|
};
|
|
376
262
|
};
|
|
263
|
+
/** The descriptor a pack declares as data in its manifest (docs/contributing/architecture.md, "The descriptor"): the
|
|
264
|
+
* pack's fields without the ones that are code or that the derived core supplies (the state system and refresh, round
|
|
265
|
+
* trips and references, parity, a rate budget, the emitter, conformance fields, the engine key, pull posture). */
|
|
266
|
+
export type PackDescriptor = Omit<TwinPack, 'stateSystem' | 'refresh' | 'refreshRefusal' | 'roundTrip' | 'references' | 'referenceTrip' | 'parityOrigin' | 'shapeParity' | 'rateBudget' | 'emitter' | 'conformanceFields' | 'engine' | 'pullPosture' | 'pullPostureReason'>;
|
|
377
267
|
/** Register (or replace) a pack descriptor. Returns it.
|
|
378
268
|
*
|
|
379
269
|
* Side effect, deliberately: a `rateBudget` declaration on the descriptor is ARMED here, so
|
|
380
270
|
* registering a pack is enough to give its vendor the ceiling it declared. `declareRateBudget`
|
|
381
271
|
* is idempotent for an identical declaration and REFUSES a widening one, so re-registering is
|
|
382
272
|
* safe and "register a fatter pack descriptor to buy a bigger budget" is not a move. */
|
|
273
|
+
/** A derived pack's descriptor, from its manifest (`manifest.descriptor`, named by the manifest's `vendor`). Given the
|
|
274
|
+
* pack's generated surface (a vendor of lanes: each lane's manifest and surface), the kernel derives what a pack never
|
|
275
|
+
* writes by hand (the real-system adapters, "Where it lives"): its state system (perform, refresh, ingest), the
|
|
276
|
+
* references its subjects hold (from the manifest's `embeds` and each `parent.field` naming its parent by id) and its
|
|
277
|
+
* rate budget (the manifest's). A descriptor that declares any of them by hand is refused. */
|
|
278
|
+
export declare function packOf(manifest: {
|
|
279
|
+
vendor: string;
|
|
280
|
+
descriptor?: Omit<TwinPack, 'vendor'>;
|
|
281
|
+
} & Partial<Pick<DerivedManifest, 'rateBudget' | 'vendorBacked'>>, surface?: DerivedSurface | Record<string, {
|
|
282
|
+
manifest: DerivedManifest;
|
|
283
|
+
surface: DerivedSurface;
|
|
284
|
+
}>,
|
|
285
|
+
/** the lanes beside a pack that has an API of its own */
|
|
286
|
+
beside?: Record<string, {
|
|
287
|
+
manifest: DerivedManifest;
|
|
288
|
+
surface: DerivedSurface;
|
|
289
|
+
}>): TwinPack;
|
|
383
290
|
export declare function registerPack(pack: TwinPack): TwinPack;
|
|
384
291
|
export declare function getPack(vendor: string): TwinPack | undefined;
|
|
385
292
|
/** All registered packs, sorted by vendor (deterministic). */
|
package/dist/src/packRegistry.js
CHANGED
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
// This is an optional convenience, NOT a kernel that defines the twin: a pack can
|
|
9
9
|
// ignore the registry entirely. Pure + deterministic.
|
|
10
10
|
import { declareRateBudget } from "./rateBudget.js";
|
|
11
|
-
import { registerReferences } from "./references.js";
|
|
11
|
+
import { referenceField, registerReferences } from "./references.js";
|
|
12
|
+
import { deriveStateSystem, deriveVendorStateSystem } from "./derived-real.js";
|
|
12
13
|
import { registerAuthStrategy, registerStateSystem } from "./state-system.js";
|
|
13
14
|
/**
|
|
14
15
|
* The pack's SERVE-FAMILY — the axis the invariant matrix keys strictness off, DECLARED
|
|
15
16
|
* because it is the pack's own claim about what kind of thing it is (a heuristic over file
|
|
16
17
|
* shapes would be the loose-scan disease). Orthogonal facts stay derived: transport is its
|
|
17
|
-
* own field,
|
|
18
|
+
* own field, screens are the manifest's, webhooks are the events module.
|
|
18
19
|
* 'crud' — stateful resource CRUD behind the vendor's API (the default family;
|
|
19
20
|
* includes ingestion→grouping packs — the ingest door is a trait).
|
|
20
21
|
* 'generative' — model-shaped surface serving deterministic stubs/scenarios; "cannot
|
|
@@ -27,26 +28,16 @@ import { registerAuthStrategy, registerStateSystem } from "./state-system.js";
|
|
|
27
28
|
*/
|
|
28
29
|
/** THE PLATFORM PROTOCOL VERSION (runtime contract R16): `major.minor`. The major names the
|
|
29
30
|
* descriptor shape, the factory signature, the doors and the adapter contract a package is
|
|
30
|
-
* written against
|
|
31
|
-
* change bumps minor. A package declares the major
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
export const PROTOCOL_VERSION = '
|
|
31
|
+
* written against, and the build form it is made in (the derived pack); a change that removes or
|
|
32
|
+
* alters a required shape bumps it, an additive change bumps minor. A package declares the major
|
|
33
|
+
* it targets (`protocol`), and the kernel serves its own major and no other: a package that
|
|
34
|
+
* declares another, or none, is refused. */
|
|
35
|
+
export const PROTOCOL_VERSION = '3.0';
|
|
35
36
|
export const PROTOCOL_MAJOR = Number(PROTOCOL_VERSION.split('.')[0]);
|
|
36
|
-
/** Where a package's declared protocol stands against this kernel
|
|
37
|
+
/** Where a package's declared protocol stands against this kernel: `current`, or `refused`. */
|
|
37
38
|
export function protocolStanding(declared) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (declared === undefined)
|
|
41
|
-
return PROTOCOL_MAJOR > 1 ? { major: PROTOCOL_MAJOR - 1, standing: 'deprecated' } : { major: PROTOCOL_MAJOR, standing: 'assumed' };
|
|
42
|
-
const major = Number(String(declared).split('.')[0]);
|
|
43
|
-
if (!Number.isInteger(major) || major < 1)
|
|
44
|
-
return { major: Number.NaN, standing: 'refused' };
|
|
45
|
-
if (major === PROTOCOL_MAJOR)
|
|
46
|
-
return { major, standing: 'current' };
|
|
47
|
-
if (major === PROTOCOL_MAJOR - 1)
|
|
48
|
-
return { major, standing: 'deprecated' };
|
|
49
|
-
return { major, standing: 'refused' };
|
|
39
|
+
const major = declared === undefined ? Number.NaN : Number(String(declared).split('.')[0]);
|
|
40
|
+
return major === PROTOCOL_MAJOR ? { major, standing: 'current' } : { major, standing: 'refused' };
|
|
50
41
|
}
|
|
51
42
|
export const PACK_ARCHETYPES = ['crud', 'generative', 'signed-protocol', 'engine-control', 'proxy'];
|
|
52
43
|
/** The posture to assume for a pack that doesn't declare one. Conservative on purpose. */
|
|
@@ -112,9 +103,53 @@ const registry = new Map();
|
|
|
112
103
|
* registering a pack is enough to give its vendor the ceiling it declared. `declareRateBudget`
|
|
113
104
|
* is idempotent for an identical declaration and REFUSES a widening one, so re-registering is
|
|
114
105
|
* safe and "register a fatter pack descriptor to buy a bigger budget" is not a move. */
|
|
106
|
+
/** A derived pack's descriptor, from its manifest (`manifest.descriptor`, named by the manifest's `vendor`). Given the
|
|
107
|
+
* pack's generated surface (a vendor of lanes: each lane's manifest and surface), the kernel derives what a pack never
|
|
108
|
+
* writes by hand (the real-system adapters, "Where it lives"): its state system (perform, refresh, ingest), the
|
|
109
|
+
* references its subjects hold (from the manifest's `embeds` and each `parent.field` naming its parent by id) and its
|
|
110
|
+
* rate budget (the manifest's). A descriptor that declares any of them by hand is refused. */
|
|
111
|
+
export function packOf(manifest, surface,
|
|
112
|
+
/** the lanes beside a pack that has an API of its own */
|
|
113
|
+
beside = {}) {
|
|
114
|
+
if (!manifest.descriptor)
|
|
115
|
+
throw new Error(`${manifest.vendor}: the manifest declares no descriptor (architecture, "The descriptor")`);
|
|
116
|
+
const hand = ['stateSystem', 'references', 'rateBudget'].filter((k) => manifest.descriptor[k] !== undefined);
|
|
117
|
+
if (hand.length)
|
|
118
|
+
throw new Error(`${manifest.vendor}: the descriptor declares ${hand.join(', ')} by hand; the kernel derives them from the manifest (architecture, "The real-system adapters")`);
|
|
119
|
+
const pack = { vendor: manifest.vendor, ...manifest.descriptor, ...(manifest.rateBudget ? { rateBudget: manifest.rateBudget } : {}) };
|
|
120
|
+
// a pack with no vendor-backed half says why, and derives no adapters
|
|
121
|
+
if (manifest.vendorBacked?.none)
|
|
122
|
+
return { ...pack, vendorBacked: { none: manifest.vendorBacked.none } };
|
|
123
|
+
if (!surface)
|
|
124
|
+
return pack;
|
|
125
|
+
const lanes = 'operations' in surface ? undefined : surface;
|
|
126
|
+
const units = lanes ? Object.values(lanes).map((l) => l.manifest) : [manifest, ...Object.values(beside).map((l) => l.manifest)];
|
|
127
|
+
const references = units.flatMap(derivedReferences);
|
|
128
|
+
return {
|
|
129
|
+
...pack,
|
|
130
|
+
stateSystem: lanes ? deriveVendorStateSystem(manifest, lanes) : deriveStateSystem(manifest, surface, beside),
|
|
131
|
+
...(references.length ? { references } : {}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/** The references a derived manifest's subjects hold: each `embeds` field naming another resource, and each child's
|
|
135
|
+
* `parent.field` when it holds the parent's id (a `value` template joins several parameters, so it holds no one id). */
|
|
136
|
+
function derivedReferences(m) {
|
|
137
|
+
const stored = (r) => m.resources[r]?.storedAs ?? r;
|
|
138
|
+
const out = [];
|
|
139
|
+
for (const [resource, decl] of Object.entries(m.resources)) {
|
|
140
|
+
for (const [field, to] of Object.entries(decl.embeds ?? {}))
|
|
141
|
+
if (m.resources[to])
|
|
142
|
+
out.push(referenceField(stored(resource), field, stored(to)));
|
|
143
|
+
if (decl.parent && !decl.parent.value && !decl.parent.where && m.resources[decl.parent.resource])
|
|
144
|
+
out.push(referenceField(stored(resource), decl.parent.field, stored(decl.parent.resource)));
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
115
148
|
export function registerPack(pack) {
|
|
116
149
|
if (!/^[a-z0-9-]+$/.test(pack.vendor))
|
|
117
150
|
throw new Error(`invalid pack vendor id: ${pack.vendor}`);
|
|
151
|
+
if (protocolStanding(pack.protocol).standing !== 'current')
|
|
152
|
+
throw new Error(`${pack.vendor}: protocol ${pack.protocol ?? '(missing)'} refused; this kernel serves ${PROTOCOL_VERSION}`);
|
|
118
153
|
if (pack.rateBudget)
|
|
119
154
|
declareRateBudget(pack.vendor, pack.rateBudget);
|
|
120
155
|
if (pack.stateSystem)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HandlerContext } from './derived-core.js';
|
|
2
|
+
/** A person a vendor's pages know: their email, and whatever the vendor's sign-up keeps beside it. */
|
|
3
|
+
export type Person = Record<string, unknown> & {
|
|
4
|
+
email: string;
|
|
5
|
+
};
|
|
6
|
+
/** A password as a World keeps it: its hash, salted by whose it is. */
|
|
7
|
+
export declare const passwordHash: (email: string, password: string) => string;
|
|
8
|
+
/** A person, their password's hash and the fields the vendor's sign-up keeps, recorded. */
|
|
9
|
+
export declare function recordPerson(ctx: HandlerContext, email: string, password: string, fields?: Record<string, unknown>): Promise<Person>;
|
|
10
|
+
/** The person with this email, or undefined. */
|
|
11
|
+
export declare const personOf: (ctx: HandlerContext, email: string) => Person | undefined;
|
|
12
|
+
/** The person whose email and password these are, or undefined. */
|
|
13
|
+
export declare function personWith(ctx: HandlerContext, email: string, password: string): Person | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { digest } from "./signing.js";
|
|
2
|
+
const PERSON = '_person';
|
|
3
|
+
/** A password as a World keeps it: its hash, salted by whose it is. */
|
|
4
|
+
export const passwordHash = (email, password) => digest('sha256', `password:${email.toLowerCase()}:${password}`);
|
|
5
|
+
/** A person, their password's hash and the fields the vendor's sign-up keeps, recorded. */
|
|
6
|
+
export async function recordPerson(ctx, email, password, fields = {}) {
|
|
7
|
+
const lower = email.toLowerCase();
|
|
8
|
+
const person = { ...fields, email: lower, password_sha256: passwordHash(lower, password) };
|
|
9
|
+
await ctx.record(PERSON, person, `person:${lower}`);
|
|
10
|
+
return person;
|
|
11
|
+
}
|
|
12
|
+
/** The person with this email, or undefined. */
|
|
13
|
+
export const personOf = (ctx, email) => ctx.rowsRaw(PERSON).find((p) => p.email === email.trim().toLowerCase());
|
|
14
|
+
/** The person whose email and password these are, or undefined. */
|
|
15
|
+
export function personWith(ctx, email, password) {
|
|
16
|
+
const person = personOf(ctx, email);
|
|
17
|
+
return person && person.password_sha256 === passwordHash(person.email, password) ? person : undefined;
|
|
18
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// A placeholder image (architecture, "A placeholder image is a kernel library too"): a real PNG of the size asked for, in
|
|
2
|
+
// one flat colour drawn from a seed, labeled in a tEXt chunk as the twin's, for generative packs that run no model.
|
|
3
|
+
// Importing it does nothing.
|
|
4
|
+
//
|
|
5
|
+
// The image data is a zlib stream (RFC 1950) of one fixed-Huffman deflate block (RFC 1951 §3.2.6): each row is the Sub
|
|
6
|
+
// filter (PNG §9.2, type 1), its first pixel as literals and the rest zeros, the zeros as a literal and then copies of
|
|
7
|
+
// distance 1. A flat image of any size is a few kilobytes.
|
|
8
|
+
import { sha256 } from "./signing.js";
|
|
9
|
+
const SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
10
|
+
const CRC = Array.from({ length: 256 }, (_, n) => { let c = n; for (let k = 0; k < 8; k++)
|
|
11
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; return c >>> 0; });
|
|
12
|
+
const crc32 = (b) => { let c = 0xffffffff; for (const x of b)
|
|
13
|
+
c = CRC[(c ^ x) & 0xff] ^ (c >>> 8); return (c ^ 0xffffffff) >>> 0; };
|
|
14
|
+
function chunk(type, data) {
|
|
15
|
+
const out = new Uint8Array(12 + data.length);
|
|
16
|
+
const v = new DataView(out.buffer);
|
|
17
|
+
v.setUint32(0, data.length);
|
|
18
|
+
out.set([...type].map((c) => c.charCodeAt(0)), 4);
|
|
19
|
+
out.set(data, 8);
|
|
20
|
+
v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
/** Deflate's bits, least significant first; a Huffman code is written most significant bit first. */
|
|
24
|
+
class Bits {
|
|
25
|
+
out = [];
|
|
26
|
+
acc = 0;
|
|
27
|
+
n = 0;
|
|
28
|
+
put(value, count) { for (let i = 0; i < count; i++) {
|
|
29
|
+
this.acc |= ((value >> i) & 1) << this.n;
|
|
30
|
+
if (++this.n === 8) {
|
|
31
|
+
this.out.push(this.acc);
|
|
32
|
+
this.acc = 0;
|
|
33
|
+
this.n = 0;
|
|
34
|
+
}
|
|
35
|
+
} }
|
|
36
|
+
code(code, length) { for (let i = length - 1; i >= 0; i--)
|
|
37
|
+
this.put((code >> i) & 1, 1); }
|
|
38
|
+
bytes() { return this.n ? [...this.out, this.acc] : this.out; }
|
|
39
|
+
}
|
|
40
|
+
/** A literal byte in the fixed code: 0–143 are 8 bits from 0x30, 144–255 are 9 bits from 0x190. */
|
|
41
|
+
const literal = (w, b) => (b < 144 ? w.code(0x30 + b, 8) : w.code(0x190 + b - 144, 9));
|
|
42
|
+
/** A copy of `length` (3–258) bytes at distance 1: length codes 257–285 (7 bits from 0 for 256–279, 8 bits from 0xc0 for
|
|
43
|
+
* 280–287) with their extra bits, then distance code 0 (5 bits, distance 1). */
|
|
44
|
+
const LENGTH_BASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
|
|
45
|
+
const LENGTH_EXTRA = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
|
|
46
|
+
function copy(w, length) {
|
|
47
|
+
let i = LENGTH_BASE.length - 1;
|
|
48
|
+
while (LENGTH_BASE[i] > length)
|
|
49
|
+
i--;
|
|
50
|
+
const symbol = 257 + i;
|
|
51
|
+
if (symbol < 280)
|
|
52
|
+
w.code(symbol - 256, 7);
|
|
53
|
+
else
|
|
54
|
+
w.code(0xc0 + symbol - 280, 8);
|
|
55
|
+
w.put(length - LENGTH_BASE[i], LENGTH_EXTRA[i]);
|
|
56
|
+
w.code(0, 5);
|
|
57
|
+
}
|
|
58
|
+
function flatIdat(width, height, rgb) {
|
|
59
|
+
const w = new Bits();
|
|
60
|
+
w.put(1, 1);
|
|
61
|
+
w.put(1, 2); // the last block, fixed Huffman
|
|
62
|
+
const zeros = (width - 1) * 3;
|
|
63
|
+
for (let y = 0; y < height; y++) {
|
|
64
|
+
literal(w, 1);
|
|
65
|
+
for (const c of rgb)
|
|
66
|
+
literal(w, c);
|
|
67
|
+
if (zeros > 0) {
|
|
68
|
+
literal(w, 0);
|
|
69
|
+
let left = zeros - 1;
|
|
70
|
+
while (left > 0) {
|
|
71
|
+
const n = left > 258 ? (left - 258 < 3 ? left - 3 : 258) : left;
|
|
72
|
+
if (n < 3) {
|
|
73
|
+
for (let k = 0; k < n; k++)
|
|
74
|
+
literal(w, 0);
|
|
75
|
+
left -= n;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
copy(w, n);
|
|
79
|
+
left -= n;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
w.code(0, 7); // end of block (256)
|
|
84
|
+
// Adler-32 of the raw rows: a zero byte leaves a as it is and adds a to b
|
|
85
|
+
let a = 1;
|
|
86
|
+
let b = 0;
|
|
87
|
+
for (let y = 0; y < height; y++) {
|
|
88
|
+
for (const byte of [1, ...rgb]) {
|
|
89
|
+
a = (a + byte) % 65521;
|
|
90
|
+
b = (b + a) % 65521;
|
|
91
|
+
}
|
|
92
|
+
b = (b + a * zeros) % 65521;
|
|
93
|
+
}
|
|
94
|
+
return Uint8Array.from([0x78, 0x01, ...w.bytes(), (b >> 8) & 0xff, b & 0xff, (a >> 8) & 0xff, a & 0xff]);
|
|
95
|
+
}
|
|
96
|
+
/** A real PNG of `size`, one flat colour drawn from `seed`, its tEXt Comment `[twin-stub] <label>`. */
|
|
97
|
+
export function placeholderPng(size, seed, label = `placeholder image, no model is run: ${seed}`) {
|
|
98
|
+
const [r, g, bl] = Buffer.from(sha256(seed), 'hex');
|
|
99
|
+
const ihdr = new Uint8Array(13);
|
|
100
|
+
const v = new DataView(ihdr.buffer);
|
|
101
|
+
v.setUint32(0, size.width);
|
|
102
|
+
v.setUint32(4, size.height);
|
|
103
|
+
ihdr.set([8, 2, 0, 0, 0], 8); // 8-bit RGB, no interlace
|
|
104
|
+
// tEXt is Latin-1 without NUL: a label's other characters (a prompt's) are written as `?`
|
|
105
|
+
const text = Uint8Array.from(`Comment\0[twin-stub] ${label.replace(/[^\x20-\x7e\xa0-\xff]/g, '?')}`, (c) => c.charCodeAt(0));
|
|
106
|
+
const parts = [Uint8Array.from(SIGNATURE), chunk('IHDR', ihdr), chunk('tEXt', text), chunk('IDAT', flatIdat(size.width, size.height, [r, g, bl])), chunk('IEND', new Uint8Array(0))];
|
|
107
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
108
|
+
let at = 0;
|
|
109
|
+
for (const p of parts) {
|
|
110
|
+
out.set(p, at);
|
|
111
|
+
at += p.length;
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|