@volter/world-core 2.0.0 → 2.0.2

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.
Files changed (66) hide show
  1. package/app-route.cjs +95 -6
  2. package/app-route.d.cts +2 -1
  3. package/dist/app-route.cjs +95 -6
  4. package/dist/app-route.d.cts +2 -1
  5. package/dist/generated/pack-facts.json +127 -8
  6. package/dist/inject.cjs +46 -1
  7. package/dist/src/actions.d.ts +27 -0
  8. package/dist/src/actions.js +62 -4
  9. package/dist/src/blob-store.d.ts +3 -0
  10. package/dist/src/blob-store.js +15 -1
  11. package/dist/src/client-bundle.d.ts +4 -0
  12. package/dist/src/client-bundle.js +13 -2
  13. package/dist/src/derived-core.d.ts +6 -0
  14. package/dist/src/derived-core.js +17 -3
  15. package/dist/src/derived.js +5 -2
  16. package/dist/src/git/refs.js +5 -3
  17. package/dist/src/head.js +12 -2
  18. package/dist/src/index.d.ts +8 -5
  19. package/dist/src/index.js +9 -5
  20. package/dist/src/log.d.ts +2 -0
  21. package/dist/src/packRegistry.d.ts +8 -6
  22. package/dist/src/redis/engine.d.ts +308 -0
  23. package/dist/src/redis/engine.js +1663 -0
  24. package/dist/src/redis/index.d.ts +2 -0
  25. package/dist/src/redis/index.js +6 -0
  26. package/dist/src/redis/lua.d.ts +153 -0
  27. package/dist/src/redis/lua.js +1373 -0
  28. package/dist/src/request-scope.d.ts +28 -0
  29. package/dist/src/request-scope.js +70 -0
  30. package/dist/src/storage.d.ts +22 -1
  31. package/dist/src/storage.js +57 -1
  32. package/dist/src/trace-context.d.ts +31 -0
  33. package/dist/src/trace-context.js +78 -0
  34. package/dist/src/twin-fetch.d.ts +16 -0
  35. package/dist/src/twin-fetch.js +66 -3
  36. package/dist/src/world-clock.d.ts +2 -2
  37. package/dist/src/world-clock.js +18 -17
  38. package/dist/src/world-store.js +9 -0
  39. package/dist/vendor-hosts.cjs +2 -2
  40. package/dist/world-clock.cjs +40 -0
  41. package/dist/world-clock.d.cts +4 -0
  42. package/generated/pack-facts.json +127 -8
  43. package/inject.cjs +46 -1
  44. package/package.json +11 -1
  45. package/src/actions.ts +69 -4
  46. package/src/blob-store.ts +15 -1
  47. package/src/client-bundle.ts +15 -2
  48. package/src/derived-core.ts +17 -3
  49. package/src/derived.ts +5 -2
  50. package/src/git/refs.ts +5 -3
  51. package/src/head.ts +11 -2
  52. package/src/index.ts +11 -3
  53. package/src/log.ts +2 -0
  54. package/src/packRegistry.ts +8 -6
  55. package/src/redis/engine.ts +1468 -0
  56. package/src/redis/index.ts +6 -0
  57. package/src/redis/lua.ts +1250 -0
  58. package/src/request-scope.ts +74 -0
  59. package/src/storage.ts +54 -2
  60. package/src/trace-context.ts +87 -0
  61. package/src/twin-fetch.ts +69 -3
  62. package/src/world-clock.ts +19 -16
  63. package/src/world-store.ts +9 -0
  64. package/vendor-hosts.cjs +2 -2
  65. package/world-clock.cjs +40 -0
  66. package/world-clock.d.cts +4 -0
package/src/derived.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  // existing fetch, which keeps serving everything not yet moved (and every route outside the spec, such
6
6
  // as the `/twin` door). `owners` says, per operation, which of the two serves it: the count a pack's
7
7
  // move is measured by. Workerd-clean: no fs, no clock, nothing at import.
8
+ import { runWithRequestTrace } from './trace-context.ts';
8
9
 
9
10
  /** The slice of a generated surface the dispatch reads. */
10
11
  export type DerivedOperation = {
@@ -141,7 +142,9 @@ export function createDerivedFetch(options: DerivedFetchOptions): DerivedFetch {
141
142
  const routes = compileSurface(options.surface);
142
143
  const fallback = (request: Request, operation: DerivedOperation | undefined, reason: string): Promise<Response> =>
143
144
  options.legacy ? options.legacy(request) : Promise.resolve(options.gap(request, operation, reason));
144
- const fetch = (async (request: Request): Promise<Response> => {
145
+ // a valid incoming W3C traceparent scopes the dispatch, so every entry it appends records it (trace-context.ts)
146
+ const fetch = ((request: Request): Promise<Response> => runWithRequestTrace(request, () => dispatch(request))) as DerivedFetch;
147
+ const dispatch = async (request: Request): Promise<Response> => {
145
148
  const url = new URL(request.url);
146
149
  const matched = options.anyMethod
147
150
  ? [...routes.keys()].map((m) => matchOperation(routes, m, url.pathname, url.searchParams, request.headers)).find(Boolean)
@@ -167,7 +170,7 @@ export function createDerivedFetch(options: DerivedFetchOptions): DerivedFetch {
167
170
  }
168
171
  }
169
172
  return fallback(request, matched.operation, 'no handler');
170
- }) as DerivedFetch;
173
+ };
171
174
  fetch.owners = () =>
172
175
  Object.fromEntries(
173
176
  options.surface.operations.map((o) => [o.id, handlers[o.id] ? 'handler' : options.core?.owns(o) ? 'core' : options.legacy ? 'legacy' : 'gap'] as const),
package/src/git/refs.ts CHANGED
@@ -3,15 +3,17 @@
3
3
  // state a snapshot carries, a projection can read, and (through the action log the plane keeps
4
4
  // beside it) a push policy can gate. No packed-refs: the store lists directories.
5
5
  import type { WorldStore } from '../world-store.ts';
6
+ import { refuseReadOnlyWrite } from '../request-scope.ts';
6
7
 
7
8
  export class GitRefs {
8
9
  constructor(private readonly store: WorldStore, private readonly prefix: string) {}
9
10
  private path(name: string): string { return `${this.prefix}/${name}`; }
10
11
  get(name: string): string | null { const v = this.store.read(this.path(name)); return v === null ? null : v.trim(); }
11
- set(name: string, sha: string): void { this.store.write(this.path(name), `${sha}\n`); }
12
- delete(name: string): void { if (this.store.exists(this.path(name))) this.store.remove(this.path(name)); }
12
+ // a read-only request moves no ref (request-scope.ts): refused before the store is touched
13
+ set(name: string, sha: string): void { refuseReadOnlyWrite('git'); this.store.write(this.path(name), `${sha}\n`); }
14
+ delete(name: string): void { refuseReadOnlyWrite('git'); if (this.store.exists(this.path(name))) this.store.remove(this.path(name)); }
13
15
  head(): string { const v = this.store.read(this.path('HEAD')); return v === null ? 'refs/heads/main' : v.trim().replace(/^ref: /, ''); }
14
- setHead(target: string): void { this.store.write(this.path('HEAD'), `ref: ${target}\n`); }
16
+ setHead(target: string): void { refuseReadOnlyWrite('git'); this.store.write(this.path('HEAD'), `ref: ${target}\n`); }
15
17
  /** Run `fn` holding the repository's ref lock (the world store's exclusive lock). */
16
18
  withLock<T>(fn: () => T): T { return this.store.withLock(this.path('refs.lock'), fn); }
17
19
  /** Every `refs/...` name with its sha, sorted by name. */
package/src/head.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  // record: a `deployed` copy is never performed again, a `failed` one is retried.
10
10
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
11
11
  import { homedir } from 'node:os';
12
- import { basename, dirname, join, resolve } from 'node:path';
12
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
13
13
  import { confirmAction, isTwinBookkeeping, resolveSubjectId, revertAction, type TwinAction } from './actions.ts';
14
14
  import { openSealedCredential, sealCredential, type CredentialPayload, type SealedCredential } from './credential.ts';
15
15
  import { buildRemoteExecute, validateRemoteOrigin, type CredentialCustody } from './executor.ts';
@@ -116,11 +116,20 @@ export async function loadChecks(worldRoot: string): Promise<Check[]> {
116
116
  * loader that runs the check in an isolate of its own, with no network and nothing of the World's. */
117
117
  export type CheckLoader = (path: string, name: string) => Promise<Check>;
118
118
  const importCheck: CheckLoader = async (path, name) => {
119
- const mod = (await import(path)) as { check?: Check; default?: Check };
119
+ // Node's ESM loader takes an absolute path only as a file URL (on Windows `C:\…` reads as the scheme `c:`); Bun takes either
120
+ const mod = (await import(isAbsolute(path) ? fileUrlOf(path) : path)) as { check?: Check; default?: Check };
120
121
  const check = mod.check ?? mod.default;
121
122
  if (!check || typeof check.run !== 'function') throw new Error(`${path} must export a check { name, run }`);
122
123
  return { name: check.name ?? name, run: check.run };
123
124
  };
125
+ /** An absolute path's file URL, built here: `node:url` is not in the browser bundles that also reach this module. */
126
+ const fileUrlOf = (path: string): string => {
127
+ // a long path (`\\?\C:\…`) is the drive path it names; a UNC path (`\\server\share\…`) puts the server in the URL's host
128
+ const long = /^\\\\\?\\(?:UNC\\)?/i.exec(path); const unc = /^\\\\\?\\UNC\\/i.test(path) || (!long && /^\\\\[^\\?]/.test(path));
129
+ const rest = (long ? path.slice(long[0].length) : unc ? path.slice(2) : path).replace(/\\/g, '/');
130
+ const encoded = rest.split('/').map((seg) => encodeURIComponent(seg).replace(/%3A/g, ':')).join('/');
131
+ return unc ? `file://${encoded}` : `file://${rest.startsWith('/') ? '' : '/'}${encoded}`;
132
+ };
124
133
  let checkLoader: CheckLoader = importCheck;
125
134
  export function setCheckLoader(loader: CheckLoader | null): void { checkLoader = loader ?? importCheck; }
126
135
  /** One check file through the host's loader: what placing a check validates with. */
package/src/index.ts CHANGED
@@ -15,10 +15,13 @@ export type { PackTransport, PullPosture, PullTrigger, PullVendor, RoundTripWrit
15
15
  export { parseScenarioDocument, scenarioFaultResult, ScenarioEngine, ScenarioError, statefulTwinManifest, twinManifest } from './scenario.ts';
16
16
  export { WORLD_CLOCK_ENV, worldNow } from './world-clock.ts';
17
17
  export { WORLD_ENV_NAMES_ENV, worldEnvValue } from './world-env.ts';
18
- export { createTwinFetchFromHandler, TWIN_PREFIX_HEADER, twinPublicBase } from './twin-fetch.ts';
18
+ export { createTwinFetchFromHandler, TWIN_PREFIX_HEADER, TWIN_REQUEST_SCOPES, twinPublicBase, withRequestScopes } from './twin-fetch.ts';
19
+ export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER, ReadOnlyRequestError, runAsReadOnlyRequest, runAsVendorMove, writesRefused } from './request-scope.ts';
19
20
  export { compileSurface, createDerivedFetch, matchOperation } from './derived.ts';
21
+ export { currentTraceparent, deliveryTraceHeaders, parseTraceparent, runWithRequestTrace, runWithTraceparent, TRACEPARENT_HEADER, traceparentForDelivery, validTraceparent } from './trace-context.ts';
22
+ export type { Traceparent } from './trace-context.ts';
20
23
  export type { DerivedCall, DerivedCoreOutcome, DerivedFetch, DerivedFetchOptions, DerivedHandler, DerivedOperation, DerivedOwner, DerivedSurface } from './derived.ts';
21
- export { bindSemantics, coreFor, crossCutting, observeTransitions, resourcesOfType as twinResourcesOfType, semanticsContext, stateOf, transitionFor, parseBracketForm, readParams, render as renderDerived, serveCore, sse, vendorError } from './derived-core.ts';
24
+ export { bindSemantics, coreFor, crossCutting, derivedRequestScopes, observeTransitions, resourcesOfType as twinResourcesOfType, semanticsContext, stateOf, transitionFor, parseBracketForm, readParams, render as renderDerived, serveCore, sse, vendorError } from './derived-core.ts';
22
25
  export type { Actor, CoreScope, DerivedManifest, ErrorSpec, FieldRule, ResourceDecl, ScreenDecl, Semantics, SemanticsContext, StateField, Transition, TransitionObserver } from './derived-core.ts';
23
26
  export type { RemoteExecute, RemoteExecuteRequest, RemoteExecuteResponse } from './remote-execute.ts';
24
27
  export type { TwinFetchAdapterConfig, TwinFetchHandlerRequest, TwinFetchHandlerResult, TwinStream, TwinStreamConnection, TwinStreamSink } from './twin-fetch.ts';
@@ -111,6 +114,9 @@ export {
111
114
  withFileLock,
112
115
  worldPaths,
113
116
  worldStateRoot,
117
+ // Where another pack's store is in a World (architecture A3), and the variable the runtime names its data directory in.
118
+ ownerStoreRoots,
119
+ WORLD_DATA_ENV,
114
120
  } from './storage.ts';
115
121
  export type {
116
122
  AppendEventResult,
@@ -190,6 +196,8 @@ export {
190
196
  isTwinBookkeeping,
191
197
  pendingTransactionCommits,
192
198
  projectResources,
199
+ projectOwnerResources,
200
+ OwnerStoreAmbiguousError,
193
201
  revertAction,
194
202
  TwinActionPreconditionError, subjectAliases, resolveSubjectId, runWithCorrelationId, currentCorrelationId } from './actions.ts';
195
203
  export type {
@@ -308,7 +316,7 @@ export { referenceField, registerReferences, packReferences, resolveReferences,
308
316
  export { serveHttp, nodeBuiltin, WORLD_BOOT_PATH, type HttpServer, type ServeHttpOptions, type HttpHandler } from './serve-http.ts';
309
317
  // two runtime-neutral helpers for a pack's serve path: a file as a Response, a mirror's client bundle
310
318
  export { fileResponse, contentTypeOf } from './file-response.ts';
311
- export { bundleClient } from './client-bundle.ts';
319
+ export { bundleClient, filePathOf } from './client-bundle.ts';
312
320
  // the brand's tokens and faces for a Volter page (the console, the site, the UI kit), fetched at build
313
321
  export { brandTokensResponse } from './brand-tokens.ts';
314
322
 
package/src/log.ts CHANGED
@@ -71,6 +71,8 @@ export type Entry = {
71
71
  /** a landed copy under the vendor's id: the local subject id it stands for */
72
72
  aliasOf?: string;
73
73
  receipt?: Receipt;
74
+ /** the W3C traceparent of the request that wrote this entry (trace-context.ts); never part of its identity */
75
+ traceparent?: string;
74
76
  /** a v1 observed row kept verbatim for `listEvents`; folds nothing unless `fields` was derived */
75
77
  event?: WorldServiceEvent;
76
78
  [extra: string]: unknown;
@@ -15,12 +15,14 @@ import { registerAuthStrategy, registerStateSystem, type StateSystemAdapters } f
15
15
 
16
16
  /**
17
17
  * How a pack's clients ADDRESS it. The first three are HTTP API styles; `raw-tcp` is the
18
- * RAW-PROTOCOL class — a pack whose clients speak a line protocol directly over a TCP socket
19
- * rather than HTTP, so none of the HTTP machinery applies: no `browserRouting`, and no entry in
20
- * the injector's `VENDOR_HOSTS` host map is possible (the injector patches http/fetch and never
21
- * sees the traffic). Such a pack is wired into a world through app-read host/port env instead,
22
- * and this value is what tells a reader that "no injector entry" is STRUCTURAL rather than a
23
- * missing wiring point. `packages/twin/smtp` is the first.
18
+ * RAW-PROTOCOL class — a pack whose clients speak their protocol directly over a TCP socket
19
+ * rather than through the HTTP/1 fetch machinery, so none of the HTTP machinery applies: no
20
+ * `browserRouting`, and no entry in the injector's `VENDOR_HOSTS` host map is possible (the
21
+ * injector patches http/fetch and never sees the traffic). Such a pack is wired into a world
22
+ * through app-read host/port env instead, and this value is what tells a reader that "no injector
23
+ * entry" is STRUCTURAL rather than a missing wiring point. `packages/twin/smtp` (a line protocol)
24
+ * is the first; `packages/twin/temporal` (gRPC: HTTP/2 framing its clients drive themselves) the
25
+ * second.
24
26
  *
25
27
  * Deliberately the transport CLASS, not the protocol name: naming the wire protocol would put a
26
28
  * vendor id in the kernel the moment a pack is named after its protocol, which is exactly what