@pygmalionjs/pygmalion 0.9.0 → 0.11.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.
Files changed (37) hide show
  1. package/README.md +34 -0
  2. package/dist-lib/{CameraLayer-BdOko8XE.js → CameraLayer-BZ8HgrUG.js} +1 -1
  3. package/dist-lib/pygmalion.js +10813 -10529
  4. package/dist-lib/{runtime-Dxb-IaLN.js → runtime-Bxn8116i.js} +1825 -1798
  5. package/dist-lib/testing.js +15 -15
  6. package/dist-lib/types/binding/duplicateMetadata.d.ts +8 -0
  7. package/dist-lib/types/binding/index.d.ts +1 -0
  8. package/dist-lib/types/document/contracts.d.ts +10 -0
  9. package/dist-lib/types/document/prototype.d.ts +26 -5
  10. package/dist-lib/types/document/prototypeOccurrence.d.ts +18 -0
  11. package/dist-lib/types/lib.d.ts +6 -2
  12. package/dist-lib/types/token-library/valueResolver.d.ts +19 -0
  13. package/dist-lib/types/workspace/configuration.d.ts +8 -0
  14. package/dist-lib/types/workspace/edit/projection.d.ts +1 -1
  15. package/dist-lib/types/workspace/edit/prototypeProjection.d.ts +7 -0
  16. package/dist-lib/types/workspace/view/PrototypePlayer.d.ts +4 -3
  17. package/dist-lib/types/workspace/view/prototypePlayback.d.ts +2 -2
  18. package/host-runtime.d.ts +73 -0
  19. package/jsx-source-reconciler.d.ts +35 -0
  20. package/node/host-runtime.mjs +4 -0
  21. package/node/jsx-source-reconciler.mjs +155 -0
  22. package/node/revision-catalog-resolver.mjs +159 -0
  23. package/node/source-archive-runtime.mjs +63 -0
  24. package/node/source-module.mjs +17 -0
  25. package/node/source-service-inputs.mjs +81 -0
  26. package/node/source-service-plugin.mjs +57 -0
  27. package/node/source-service.mjs +4 -0
  28. package/node/token-source-service.mjs +151 -0
  29. package/node/workspace-draft-entry.mjs +52 -0
  30. package/node/workspace-draft-preview.mjs +3 -2
  31. package/node/workspace-draft-process.mjs +129 -0
  32. package/node/workspace-draft-runtime.mjs +67 -0
  33. package/node/workspace-source-session.mjs +187 -0
  34. package/package.json +40 -2
  35. package/source-module.d.ts +16 -0
  36. package/source-service.d.ts +92 -0
  37. package/workspace-draft-entry.d.ts +15 -0
@@ -1,5 +1,5 @@
1
- import { bV as l, T as u } from "./runtime-Dxb-IaLN.js";
2
- import { bi as w, cb as y, cc as E, cd as P, be as T, as as V } from "./runtime-Dxb-IaLN.js";
1
+ import { bW as l, T as u } from "./runtime-Bxn8116i.js";
2
+ import { bi as y, cc as b, cd as E, ce as P, be as T, as as $ } from "./runtime-Bxn8116i.js";
3
3
  const v = {
4
4
  mirror: ["status", "refresh", "refs"],
5
5
  session: ["open", "check", "previewVisual", "applyVisual", "revertVisual"],
@@ -9,11 +9,11 @@ const v = {
9
9
  storyboard: ["environment", "sourceUsage", "componentBranches"]
10
10
  };
11
11
  function g(m = {}, t = {}) {
12
- const n = [], d = t.origin ?? "", r = (e) => {
12
+ const n = [], d = t.origin ?? "", e = (r) => {
13
13
  var a;
14
14
  const i = {};
15
- for (const o of v[e]) {
16
- const s = `${e}.${String(o)}`, c = (a = m[e]) == null ? void 0 : a[o];
15
+ for (const o of v[r]) {
16
+ const s = `${r}.${String(o)}`, c = (a = m[r]) == null ? void 0 : a[o];
17
17
  i[String(o)] = (...p) => (n.push({ operation: s, request: p[0] }), c ? Promise.resolve(c(...p)) : Promise.reject(
18
18
  new u({
19
19
  operation: s,
@@ -27,21 +27,21 @@ function g(m = {}, t = {}) {
27
27
  return {
28
28
  calls: n,
29
29
  endpoints: l(t.endpoints),
30
- resolveUrl: (e) => `${d}${e}`,
31
- mirror: r("mirror"),
32
- session: r("session"),
33
- inspect: r("inspect"),
34
- qa: r("qa"),
35
- preview: r("preview"),
36
- storyboard: r("storyboard")
30
+ resolveUrl: (r) => `${d}${r}`,
31
+ mirror: e("mirror"),
32
+ session: e("session"),
33
+ inspect: e("inspect"),
34
+ qa: e("qa"),
35
+ preview: e("preview"),
36
+ storyboard: e("storyboard")
37
37
  };
38
38
  }
39
39
  export {
40
- w as EditorRuntimeProvider,
41
- y as EditorStore,
40
+ y as EditorRuntimeProvider,
41
+ b as EditorStore,
42
42
  E as FrozenRoutePreviewView,
43
43
  P as NodeModel,
44
44
  T as createEditorRuntime,
45
45
  g as createMemoryTransport,
46
- V as serializeNode
46
+ $ as serializeNode
47
47
  };
@@ -0,0 +1,8 @@
1
+ import type { DataCatalog } from './contracts.js';
2
+ import type { DocumentDuplicateMetadataRemapper } from '../document/contracts.js';
3
+ export interface DataBindingMetadataDuplicatorOptions {
4
+ /** Host-declared metadata containing no node references that require remapping. */
5
+ preservedMetadataKeys?: readonly string[];
6
+ }
7
+ /** Copies binding relationships while retaining only explicitly declared, reference-free metadata. */
8
+ export declare function createDataBindingMetadataDuplicator(catalog: DataCatalog, options?: DataBindingMetadataDuplicatorOptions): DocumentDuplicateMetadataRemapper;
@@ -4,3 +4,4 @@ export * from './validate.js';
4
4
  export * from './preview.js';
5
5
  export * from './source.js';
6
6
  export * from './documentCommands.js';
7
+ export * from './duplicateMetadata.js';
@@ -158,6 +158,16 @@ export interface ResolvedDocumentNode {
158
158
  }
159
159
  /** Optional presentation-time instance resolution; normal editing uses canonical definitions. */
160
160
  export interface DocumentResolveOptions {
161
+ /**
162
+ * Expand a source occurrence before resolving its component subtree. The
163
+ * continuation retains inherited overrides and cycle checks, skips this
164
+ * occurrence's expansion hook, and accepts callback overrides for its subtree.
165
+ * A definition root is resolved through its instance, not expanded separately.
166
+ */
167
+ expandNode?: (input: {
168
+ node: DocumentNode;
169
+ instancePath: readonly string[];
170
+ }, resolve: (options?: DocumentResolveOptions) => ResolvedDocumentNode) => readonly ResolvedDocumentNode[];
161
171
  resolveInstance?: (input: {
162
172
  node: DocumentNode;
163
173
  /** Outer-to-inner instance IDs. IDs remain opaque, including slashes. */
@@ -8,6 +8,8 @@
8
8
  * side effects a player must perform; time enters only through `now`.
9
9
  */
10
10
  import type { DesignDocument, DocumentDiagnostic, DocumentDuplicateMetadataRemapper, DocumentNode, DocumentResolveOptions, DocumentTransaction, ResolvedDocumentNode } from './contracts.js';
11
+ import type { PrototypeOccurrence, PrototypeRowPath } from './prototypeOccurrence.js';
12
+ export type { PrototypeOccurrence, PrototypeRowPath } from './prototypeOccurrence.js';
11
13
  export declare const PROTOTYPE_METADATA_KEY = "prototype";
12
14
  export declare const PROTOTYPE_INTERACTIONS_PROPERTY = "interactions";
13
15
  /** Upper bound for an authored transition in this player. */
@@ -252,14 +254,28 @@ export interface PrototypeSceneLayer {
252
254
  fill: string | null;
253
255
  node: DocumentNode;
254
256
  depth: number;
257
+ occurrence?: PrototypeOccurrence;
258
+ }
259
+ export interface PrototypeResolvedSceneNode extends Omit<ResolvedDocumentNode, 'children'> {
260
+ rowPath: PrototypeRowPath;
261
+ instancePath?: readonly string[];
262
+ children: PrototypeResolvedSceneNode[];
263
+ }
264
+ /** Supplies the same expanded scene used by the rendering surface, without owning authoring data. */
265
+ export interface PrototypeRuntimeContext {
266
+ resolveScene(document: DesignDocument, state: PrototypeState, rootId: string, sceneId: string): PrototypeResolvedSceneNode;
255
267
  }
256
268
  /** Stable occurrence key. Every ID is opaque; nested instances never share canonical state. */
257
- export declare function prototypeVariantKey(instancePath: readonly string[]): string;
269
+ export declare function prototypeVariantKey(instancePath: readonly string[], occurrence?: PrototypeOccurrence): string;
258
270
  /** Presentation-only variant resolution; neither the document nor shared definitions are changed. */
259
- export declare function prototypeResolveOptions(document: DesignDocument, variants: Readonly<Record<string, string>>): DocumentResolveOptions;
271
+ export declare function prototypeResolveOptions(document: DesignDocument, variants: Readonly<Record<string, string>>, occurrence?: PrototypeOccurrence): DocumentResolveOptions;
260
272
  /** Resolves runtime variants explicitly; invalid choices never silently render canonical state. */
261
273
  export declare function resolvePrototypeScene(document: DesignDocument, rootId: string, variants?: Readonly<Record<string, string>>): ResolvedDocumentNode;
262
- export declare function prototypeSceneLayers(document: DesignDocument, rootId: string, variants?: Readonly<Record<string, string>>): PrototypeSceneLayer[];
274
+ export declare function prototypeSceneLayers(document: DesignDocument, rootId: string, variants?: Readonly<Record<string, string>>, options?: {
275
+ state: PrototypeState;
276
+ sceneId?: string;
277
+ context?: PrototypeRuntimeContext;
278
+ }): PrototypeSceneLayer[];
263
279
  export interface PrototypeSmartAnimatePair {
264
280
  name: string;
265
281
  from: PrototypeSceneLayer;
@@ -301,6 +317,7 @@ export type PrototypeRevert = {
301
317
  export interface PrototypeHold {
302
318
  nodeId: string;
303
319
  trigger: 'hover' | 'press';
320
+ occurrence?: PrototypeOccurrence;
304
321
  /** Screen-space bounds of the hotspot when the hold began; a surface keeps it hit-testable across navigation. */
305
322
  bounds: {
306
323
  sceneId: string;
@@ -327,7 +344,7 @@ export interface PrototypeState {
327
344
  history: string[];
328
345
  overlays: PrototypeOverlayState[];
329
346
  variables: Record<string, PrototypeValue>;
330
- /** Runtime variant choices keyed by prototypeVariantKey(outer-to-inner instance path). */
347
+ /** Runtime variant choices keyed by the authored instance path and optional scene/row occurrence. */
331
348
  variants: Record<string, string>;
332
349
  scroll: Record<string, {
333
350
  x: number;
@@ -338,6 +355,7 @@ export interface PrototypeState {
338
355
  targetId: string;
339
356
  transition: PrototypeTransition;
340
357
  at: number;
358
+ occurrence?: PrototypeOccurrence;
341
359
  } | null;
342
360
  timers: PrototypeTimer[];
343
361
  holds: PrototypeHold[];
@@ -352,6 +370,7 @@ export type PrototypeEvent =
352
370
  {
353
371
  type: PrototypePointerEventType;
354
372
  path: readonly string[];
373
+ occurrence?: PrototypeOccurrence;
355
374
  } | {
356
375
  type: 'key';
357
376
  key: string;
@@ -397,6 +416,7 @@ export type PrototypeEffect = {
397
416
  sceneId: string;
398
417
  targetId: string;
399
418
  transition: PrototypeTransition;
419
+ occurrence?: PrototypeOccurrence;
400
420
  } | {
401
421
  type: 'open-url';
402
422
  url: string;
@@ -415,10 +435,11 @@ export interface PrototypeStartOptions {
415
435
  flowId?: string | null;
416
436
  startingNodeId?: string;
417
437
  now?: number;
438
+ context?: PrototypeRuntimeContext;
418
439
  }
419
440
  /** The initial state of a flow (or of an explicit starting screen); delay triggers on the screen are armed. */
420
441
  export declare function createPrototypeState(document: DesignDocument, options?: PrototypeStartOptions): PrototypeState;
421
442
  /** The earliest moment the state changes without input, or null when it is at rest. */
422
443
  export declare function prototypeNextDeadline(state: PrototypeState): number | null;
423
444
  /** One pure step: the event (or the passage of time) against a frozen document; time is `now`. */
424
- export declare function stepPrototype(document: DesignDocument, previous: PrototypeState, event: PrototypeEvent, now: number): PrototypeStepResult;
445
+ export declare function stepPrototype(document: DesignDocument, previous: PrototypeState, event: PrototypeEvent, now: number, runtime?: PrototypeRuntimeContext): PrototypeStepResult;
@@ -0,0 +1,18 @@
1
+ /** A stable row address, ordered from the outer repeat to the inner repeat. */
2
+ export type PrototypeRowPath = readonly {
3
+ repeatId: string;
4
+ key: string | number;
5
+ }[];
6
+ /** A rendered occurrence. Overlay scene IDs distinguish separate openings of one screen. */
7
+ export interface PrototypeOccurrence {
8
+ sceneId: string;
9
+ rowPath: PrototypeRowPath;
10
+ }
11
+ export declare function prototypeOccurrenceValid(value: unknown): value is PrototypeOccurrence;
12
+ export declare function prototypeRowPathPrefix(prefix: PrototypeRowPath, path: PrototypeRowPath): boolean;
13
+ export declare function prototypeOccurrenceEqual(left: PrototypeOccurrence | undefined, right: PrototypeOccurrence | undefined): boolean;
14
+ export declare function encodePrototypeVariantKey(instancePath: readonly string[], occurrence?: PrototypeOccurrence): string;
15
+ export declare function decodePrototypeVariantKey(key: string): {
16
+ instancePath: string[];
17
+ occurrence?: PrototypeOccurrence;
18
+ } | null;
@@ -10,6 +10,8 @@ export type { StylesPanelProps } from './workspace/edit/StylesPanel.js';
10
10
  export { TokenLibraryProvider, useTokenLibraryController, useHasTokenLibraryProvider } from './workspace/tokens/TokenLibraryContext.js';
11
11
  export { createTokenLibraryEngine, reopenTokenLibrary, validateTokenLibraryDocument, resolveTokenLibraryValue, tokenValueEditability, TokenLibraryValidationError } from './token-library/engine.js';
12
12
  export type * from './token-library/contracts.js';
13
+ export { createTokenValueResolver } from './token-library/valueResolver.js';
14
+ export type { TokenValueReferenceRange, TokenValueResolverOptions, ResolvedTokenText } from './token-library/valueResolver.js';
13
15
  export type * from './workspace/tokens/contracts.js';
14
16
  export { WorkspaceTokenLibraryController } from './workspace/tokens/controller.js';
15
17
  export type { WorkspaceTokenLibraryState } from './workspace/tokens/controller.js';
@@ -27,8 +29,8 @@ export type { InspectSourceRuntime, SourceWritePolicy, SourceWriteErrorCode } fr
27
29
  export { WorkspaceController } from './workspace/controller.js';
28
30
  export { createLegacyModeAdapter } from './workspace/legacyModeAdapter.js';
29
31
  export type * from './workspace/contracts.js';
30
- export { resolveWorkspaceTarget } from './workspace/configuration.js';
31
- export type { WorkspaceConfiguration, WorkspaceTargetConfiguration } from './workspace/configuration.js';
32
+ export { createWorkspaceTargetResolver, resolveWorkspaceTarget } from './workspace/configuration.js';
33
+ export type { WorkspaceConfiguration, WorkspaceTargetConfiguration, WorkspaceTargetResolverOptions } from './workspace/configuration.js';
32
34
  export { compileWorkspaceVisualSource, workspaceSourceFingerprint } from './workspace/source/compiler.js';
33
35
  export { compileWorkspaceDocumentSource, jsxStructureWrapperClassName } from './workspace/source/structure.js';
34
36
  export { compileWorkspaceResponsiveSource, resolveResponsiveLayout, setResponsiveLayoutOverride } from './workspace/source/responsive.js';
@@ -422,3 +424,5 @@ export { PrototypePanel } from './workspace/edit/PrototypePanel.js';
422
424
  export { PrototypePlayer } from './workspace/view/PrototypePlayer.js';
423
425
  export type { PrototypePlayerProps } from './workspace/view/PrototypePlayer.js';
424
426
  export * from './workspace/view/prototypePlayback.js';
427
+ export { createPrototypeProjectionContext } from './workspace/edit/prototypeProjection.js';
428
+ export type { PrototypeProjectionContext } from './workspace/edit/prototypeProjection.js';
@@ -0,0 +1,19 @@
1
+ import type { ResolvedTokenReference, TokenLibraryDiagnostic, TokenLibraryDocument, TokenValueResolution } from './contracts.js';
2
+ export interface TokenValueReferenceRange {
3
+ start: number;
4
+ end: number;
5
+ /** Must be one of the resolution's declared references. */
6
+ reference: ResolvedTokenReference;
7
+ }
8
+ export interface TokenValueResolverOptions {
9
+ /** Extend a reference's replacement range to host-owned surrounding syntax, such as a function call. */
10
+ referenceRanges?(resolution: TokenValueResolution): readonly TokenValueReferenceRange[];
11
+ }
12
+ export interface ResolvedTokenText {
13
+ status: 'resolved' | 'blocked';
14
+ text?: string;
15
+ resolution?: TokenValueResolution;
16
+ diagnostics: readonly TokenLibraryDiagnostic[];
17
+ }
18
+ /** Resolves a captured library's declared reference graph and caches text by token and requested mode. */
19
+ export declare function createTokenValueResolver(input: TokenLibraryDocument, options?: TokenValueResolverOptions): (tokenId: string, modeId: string) => ResolvedTokenText;
@@ -33,6 +33,14 @@ export interface WorkspaceConfiguration extends WorkspaceTargetConfiguration {
33
33
  /** Undefined explicitly marks an unsupported screen or source revision. */
34
34
  resolveTarget?(target: WorkspaceTarget): WorkspaceTargetConfiguration | undefined;
35
35
  }
36
+ export interface WorkspaceTargetResolverOptions {
37
+ /** The host decides whether this screen and revision have usable capabilities. */
38
+ resolve(target: Readonly<WorkspaceTarget>): WorkspaceTargetConfiguration | undefined;
39
+ /** Maximum retained target configurations; zero disables caching. Default: 8. */
40
+ maxEntries?: number;
41
+ }
42
+ /** Retain capability identities per screen and revision; unsupported targets remain retryable. */
43
+ export declare function createWorkspaceTargetResolver({ resolve, maxEntries }: WorkspaceTargetResolverOptions): (target: WorkspaceTarget) => WorkspaceTargetConfiguration | undefined;
36
44
  /** An explicit target resolver never inherits capabilities from the root configuration. */
37
45
  export declare function resolveWorkspaceTarget(configuration: WorkspaceConfiguration, target: WorkspaceTarget): WorkspaceTargetConfiguration | undefined;
38
46
  /** Compares captured capability identities without invoking any host callbacks. */
@@ -9,4 +9,4 @@ export interface ProjectedDocumentNode {
9
9
  children: ProjectedDocumentNode[];
10
10
  }
11
11
  /** Fixture rows affect this disposable scene only, never canonical document geometry. */
12
- export declare function projectDocumentScene(document: DesignDocument, data: DataProjection | null, resolveOptions?: DocumentResolveOptions): ProjectedDocumentNode;
12
+ export declare function projectDocumentScene(document: DesignDocument, data: DataProjection | null, resolveOptions?: DocumentResolveOptions, resolveRowOptions?: (rowPath: BindingRuntimeScope['rowPath']) => DocumentResolveOptions): ProjectedDocumentNode;
@@ -0,0 +1,7 @@
1
+ import type { DataProjection } from '../../binding/preview.js';
2
+ import type { PrototypeRuntimeContext } from '../../document/prototype.js';
3
+ export interface PrototypeProjectionContext extends PrototypeRuntimeContext {
4
+ readonly projection: DataProjection | null;
5
+ }
6
+ /** One captured data projection supplies runtime hit testing, layout and presentation rendering. */
7
+ export declare function createPrototypeProjectionContext(data: DataProjection | null): PrototypeProjectionContext;
@@ -1,16 +1,17 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { DesignDocument } from '../../document/contracts.js';
3
- import { type PrototypeState } from '../../document/prototype.js';
3
+ import { type PrototypeRuntimeContext, type PrototypeState } from '../../document/prototype.js';
4
4
  import { type PrototypeClock } from './prototypePlayback.js';
5
5
 
6
6
  export interface PrototypePlayerProps {
7
7
  document: DesignDocument;
8
8
  /** Uses the same document/component renderer as Edit, with authoring controls disabled. */
9
- renderScene(rootId: string, state: PrototypeState): ReactNode;
10
- sceneSize?(rootId: string, state: PrototypeState): {
9
+ renderScene(rootId: string, state: PrototypeState, sceneId?: string): ReactNode;
10
+ sceneSize?(rootId: string, state: PrototypeState, sceneId?: string): {
11
11
  width: number;
12
12
  height: number;
13
13
  } | undefined;
14
+ context?: PrototypeRuntimeContext;
14
15
  onClose(): void;
15
16
  clock?: PrototypeClock;
16
17
  }
@@ -1,5 +1,5 @@
1
1
  import type { DesignDocument, DocumentNode, ResolvedDocumentNode } from '../../document/contracts.js';
2
- import { type PrototypeEffect, type PrototypeEvent, type PrototypeStartOptions, type PrototypeState } from '../../document/prototype.js';
2
+ import { type PrototypeEffect, type PrototypeEvent, type PrototypeRuntimeContext, type PrototypeStartOptions, type PrototypeState } from '../../document/prototype.js';
3
3
  export interface PrototypeClock {
4
4
  now(): number;
5
5
  schedule(callback: () => void, delayMs: number): unknown;
@@ -38,7 +38,7 @@ export interface PrototypePresentationScene {
38
38
  opacity: number;
39
39
  } | null;
40
40
  }
41
- export declare function prototypeScene(document: DesignDocument, state: PrototypeState): PrototypePresentationScene[];
41
+ export declare function prototypeScene(document: DesignDocument, state: PrototypeState, context?: PrototypeRuntimeContext): PrototypePresentationScene[];
42
42
  export declare function prototypeFlowOptions(document: DesignDocument): {
43
43
  id: string;
44
44
  label: string;
@@ -0,0 +1,73 @@
1
+ import type { SpawnOptions } from 'node:child_process';
2
+ import type { PygmalionRevisionCatalog } from './dist-lib/types/editor/revisionCatalog.js';
3
+
4
+ export interface WorkspaceDraftProcessOptions extends Omit<SpawnOptions, 'signal' | 'detached' | 'stdio' | 'timeout' | 'killSignal'> {
5
+ signal?: AbortSignal;
6
+ /** Milliseconds before cancellation. Zero disables the deadline. Default: 240,000. */
7
+ timeout?: number;
8
+ /** Milliseconds between SIGTERM and SIGKILL. Default: 1,000. */
9
+ terminationGraceMs?: number;
10
+ /** Maximum bytes per output stream. Default: 8 MiB. */
11
+ maxBuffer?: number;
12
+ }
13
+
14
+ /** macOS/Linux: waits for process-group cleanup on success, failure, cancellation, and timeout. */
15
+ export declare function executeWorkspaceDraftProcess(
16
+ command: string, args: readonly string[], options?: WorkspaceDraftProcessOptions,
17
+ ): Promise<{ stdout: string; stderr: string }>;
18
+
19
+ export interface RevisionCatalogResolver {
20
+ /** Only complete SHA-1 or SHA-256 commits are accepted; returned catalogs are frozen copies. */
21
+ resolve(sourceRevision: string, signal?: AbortSignal): Promise<PygmalionRevisionCatalog>;
22
+ currentRevision(signal?: AbortSignal): Promise<string>;
23
+ /** Least recently used first; expired and current-build entries are omitted. */
24
+ retained(): string[];
25
+ /** Cancels owned generation, discards cached results, and rejects subsequent requests. */
26
+ dispose(): void;
27
+ }
28
+
29
+ export interface RevisionCatalogResolverOptions {
30
+ projectRoot?: string;
31
+ /** Repository-relative application directory. Default: the repository root. */
32
+ appDirectory?: string;
33
+ currentCatalog(signal?: AbortSignal): unknown | Promise<unknown>;
34
+ generate(context: { archiveRoot: string; sourceRevision: string; signal: AbortSignal }): unknown | Promise<unknown>;
35
+ /** Defaults to commits reachable from HEAD or repository branches, remotes, and tags. */
36
+ reachable?(sourceRevision: string, signal: AbortSignal): boolean | Promise<boolean>;
37
+ /** Custom extraction must await its callback and stop workers before removing source files. */
38
+ archive?<T>(sourceRevision: string, signal: AbortSignal,
39
+ operation: (context: Readonly<{ appRoot: string; sourceRevision: string; signal: AbortSignal }>) => Promise<T>): Promise<T>;
40
+ /** Defaults to 4 archived catalogs; zero disables retention. */
41
+ maxEntries?: number;
42
+ /** Absolute lifetime since generation, in milliseconds. Default: 30 minutes. */
43
+ maxAgeMs?: number;
44
+ }
45
+
46
+ /** Shares requests for each revision and serializes archive/build work across revisions. */
47
+ export declare function createRevisionCatalogResolver(options: RevisionCatalogResolverOptions): RevisionCatalogResolver;
48
+
49
+ /** Returns dependency search paths from the first byte-identical lockfile, or [] for an archive-local install. */
50
+ export declare function resolveSourceArchiveDependencies(options: {
51
+ archiveRoot: string; dependencyRoots: readonly string[]; lockfile?: string;
52
+ }): Promise<string[]>;
53
+
54
+ /** Rejects host worktrees, Git metadata, and missing or linked required directories. */
55
+ export declare function assertIsolatedSourceRoot(options: {
56
+ archiveRoot: string; hostRoot: string; requiredDirectories?: readonly string[];
57
+ }): Promise<string>;
58
+
59
+ /** Converts SIGTERM/SIGINT into cancellation and exit status; dispose removes its process listeners. */
60
+ export declare function createWorkspaceDraftLifecycle(): Readonly<{
61
+ signal: AbortSignal;
62
+ dispose(): void;
63
+ }>;
64
+
65
+ /** Requires canonical source/output directories belonging to one SDK draft-preview temporary root. */
66
+ export declare function assertWorkspaceDraftDirectories(options: {
67
+ appRoot: string; outputDirectory: string; appDirectory?: string;
68
+ }): Promise<Readonly<{ appRoot: string; outputDirectory: string; temporaryRoot: string }>>;
69
+
70
+ /** Imports the installed package's explicit exports['.'].import entry; no parent-install fallback. */
71
+ export declare function importIsolatedPackage<T = Record<string, unknown>>(options: {
72
+ appRoot: string; name: string;
73
+ }): Promise<T>;
@@ -0,0 +1,35 @@
1
+ import type { WorkspaceTarget } from './dist-lib/types/workspace/contracts.js';
2
+ import type { WorkspaceSourceBaseline, WorkspaceSourceReconcileInput, WorkspaceSourceReconcileResult } from './dist-lib/types/workspace/source/flowContracts.js';
3
+ import type { compileWorkspaceDocumentSource } from './dist-lib/types/workspace/source/structure.js';
4
+ import type { JsxStructureAnchor, JsxStructureIdentity, JsxStructureSourceNode, JsxStructureWrapperEvidence } from './source-structure.js';
5
+
6
+ export type ReconcileSourceInputs = Readonly<Record<string, string | null>>;
7
+ export interface ReconcileSourceProjection { anchors: JsxStructureAnchor[]; nodes: JsxStructureSourceNode[] }
8
+ export interface JsxSourceReconcilerOptions {
9
+ file: string;
10
+ identityFile: string;
11
+ rootId: string;
12
+ definitionId: string;
13
+ screenId: string;
14
+ documentId: string;
15
+ opaqueNodeIds: readonly string[];
16
+ /** The owner validates original expressions and allowed source structure. */
17
+ projectSource(input: { source: string; identity: JsxStructureIdentity | null; sourceInputs: ReconcileSourceInputs }): ReconcileSourceProjection;
18
+ resolveWrapper?(inputs: ReconcileSourceInputs): JsxStructureWrapperEvidence | undefined;
19
+ /** Pass the SDK's document compiler; injecting it avoids loading the browser bundle in Node. */
20
+ compileDocumentSource: typeof compileWorkspaceDocumentSource;
21
+ /** Explicit policy for a unique byte-identical island; changed or duplicate islands remain conflicts. Default: false. */
22
+ allowUnchangedIslandRelocation?: boolean;
23
+ }
24
+ export interface JsxReconcileInputs {
25
+ baselineInputs: ReconcileSourceInputs;
26
+ externalInputs: ReconcileSourceInputs;
27
+ baselineIdentity?: JsxStructureIdentity;
28
+ }
29
+ export interface JsxSourceReconciler {
30
+ projectInputs(input: JsxReconcileInputs): { identity: JsxStructureIdentity; projectedStructure: ReconcileSourceProjection };
31
+ reconcile(input: WorkspaceSourceReconcileInput & JsxReconcileInputs & {
32
+ loadBaseline(input: { target: WorkspaceTarget; sourceInputs: ReconcileSourceInputs; projectedStructure: ReconcileSourceProjection }, signal?: AbortSignal): Promise<WorkspaceSourceBaseline>;
33
+ }, signal?: AbortSignal): Promise<WorkspaceSourceReconcileResult>;
34
+ }
35
+ export declare function createJsxSourceReconciler(options: JsxSourceReconcilerOptions): JsxSourceReconciler;
@@ -0,0 +1,4 @@
1
+ export { executeWorkspaceDraftProcess } from './workspace-draft-process.mjs';
2
+ export { createRevisionCatalogResolver } from './revision-catalog-resolver.mjs';
3
+ export { assertIsolatedSourceRoot, resolveSourceArchiveDependencies } from './source-archive-runtime.mjs';
4
+ export { createWorkspaceDraftLifecycle, assertWorkspaceDraftDirectories, importIsolatedPackage } from './workspace-draft-runtime.mjs';
@@ -0,0 +1,155 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { workspaceSourceFingerprint as fingerprint } from './workspace-source-identity.mjs';
3
+ import { createJsxStructureIdentity, reconcileJsxStructurePlan, reimportJsxStructureSource } from './source-structure.mjs';
4
+
5
+ const hash = value => createHash('sha256').update(value).digest('hex');
6
+ const same = (left, right) => fingerprint(left) === fingerprint(right);
7
+ const record = value => value !== null && typeof value === 'object' && !Array.isArray(value);
8
+ function freeze(value) {
9
+ if (value && typeof value === 'object' && !Object.isFrozen(value)) { Object.values(value).forEach(freeze); Object.freeze(value); }
10
+ return value;
11
+ }
12
+ const capture = value => freeze(JSON.parse(fingerprint(value)));
13
+ function fail(code, message, file) {
14
+ throw Object.assign(new Error(message), { diagnostics: [{ code: `source-reconcile-${code}`, message, ...(file ? { file } : {}) }] });
15
+ }
16
+ const sourceLayout = layout => layout ? { direction: layout.direction, gap: layout.gap, padding: layout.padding, ...(layout.align === undefined ? {} : { align: layout.align }) } : null;
17
+
18
+ /** Reconciles one explicitly declared, unchanged source island while delegating product ownership to its projector. */
19
+ export function createJsxSourceReconciler({
20
+ file, identityFile, rootId, definitionId, screenId, documentId, opaqueNodeIds,
21
+ projectSource, resolveWrapper = () => undefined, compileDocumentSource,
22
+ allowUnchangedIslandRelocation = false,
23
+ }) {
24
+ if ([file, identityFile, rootId, definitionId, screenId, documentId].some(value => typeof value !== 'string' || !value)
25
+ || file === identityFile || !Array.isArray(opaqueNodeIds) || new Set(opaqueNodeIds).size !== opaqueNodeIds.length
26
+ || opaqueNodeIds.some(value => typeof value !== 'string' || !value)
27
+ || ![projectSource, resolveWrapper, compileDocumentSource].every(value => typeof value === 'function')) {
28
+ throw new TypeError('A source reconciler requires explicit files, node identities, and projection/compiler callbacks.');
29
+ }
30
+ opaqueNodeIds = [...opaqueNodeIds];
31
+ function capturedInputs(value) {
32
+ if (!record(value) || Object.keys(value).length > 500 || !Object.hasOwn(value, identityFile)
33
+ || typeof value[file] !== 'string' || !value[file]) fail('inputs-invalid', 'Complete source and identity inputs are required.');
34
+ let size = 0;
35
+ for (const [inputFile, content] of Object.entries(value)) {
36
+ if (content === null && inputFile === identityFile) continue;
37
+ if (typeof content !== 'string' || Buffer.from(content).toString('utf8') !== content) fail('inputs-invalid', 'Source inputs must be UTF-8 strings.', inputFile);
38
+ size += Buffer.byteLength(content);
39
+ }
40
+ if (size > 32 * 1024 * 1024) fail('inputs-invalid', 'Source inputs exceed the reconciliation size limit.');
41
+ }
42
+ function sourceIdentity(inputs, baselineIdentity) {
43
+ const source = inputs[file];
44
+ const supplied = baselineIdentity ?? (inputs[identityFile] === null ? null : JSON.parse(inputs[identityFile]));
45
+ const wrapper = resolveWrapper(inputs);
46
+ const projectedStructure = capture(projectSource({ source, identity: supplied, sourceInputs: inputs }));
47
+ const identity = supplied ?? createJsxStructureIdentity({
48
+ file, source, rootId, anchors: projectedStructure.anchors, opaqueNodeIds, ...(wrapper ? { wrapper } : {}),
49
+ });
50
+ return { identity, projectedStructure, wrapper };
51
+ }
52
+ function projectInputs(input) {
53
+ const { baselineInputs, externalInputs, baselineIdentity } = capture({
54
+ baselineInputs: input.baselineInputs, externalInputs: input.externalInputs,
55
+ ...(input.baselineIdentity === undefined ? {} : { baselineIdentity: input.baselineIdentity }),
56
+ });
57
+ capturedInputs(baselineInputs); capturedInputs(externalInputs);
58
+ if (!same(Object.keys(baselineInputs).sort(), Object.keys(externalInputs).sort())) fail('input-set-conflict', 'The complete source input sets differ.');
59
+ for (const inputFile of Object.keys(baselineInputs)) {
60
+ if (![file, identityFile].includes(inputFile) && baselineInputs[inputFile] !== externalInputs[inputFile]) {
61
+ fail('dependency-conflict', 'A source dependency changed and requires separate review.', inputFile);
62
+ }
63
+ }
64
+ const original = sourceIdentity(baselineInputs, baselineIdentity);
65
+ const oldRoot = original.projectedStructure.anchors.find(anchor => anchor.nodeId === rootId);
66
+ if (!oldRoot) fail('island-conflict', 'The declared source island is missing.', file);
67
+ const source = externalInputs[file];
68
+ const start = source.indexOf(oldRoot.expected);
69
+ if (start < 0 || source.indexOf(oldRoot.expected, start + oldRoot.expected.length) !== -1) {
70
+ fail('island-conflict', 'The source island changed or no longer has a unique unchanged match.', file);
71
+ }
72
+ if (source !== baselineInputs[file] && !allowUnchangedIslandRelocation) {
73
+ fail('relocation-disabled', 'The source owner has not allowed unchanged island relocation.', file);
74
+ }
75
+ const offset = start - oldRoot.start;
76
+ const anchors = original.projectedStructure.anchors.map(anchor => ({ ...anchor, start: anchor.start + offset, end: anchor.end + offset }));
77
+ const wrapper = resolveWrapper(externalInputs);
78
+ const identity = createJsxStructureIdentity({ file, source, rootId, anchors, opaqueNodeIds: original.identity.opaqueNodeIds, ...(wrapper ? { wrapper } : {}) });
79
+ if (externalInputs[identityFile] !== baselineInputs[identityFile]
80
+ && (externalInputs[identityFile] === null || !same(JSON.parse(externalInputs[identityFile]), identity))) {
81
+ fail('ledger-conflict', 'The external identity ledger differs from the proven current source correspondence.', identityFile);
82
+ }
83
+ return capture({ identity, projectedStructure: projectSource({ source, identity, sourceInputs: externalInputs }) });
84
+ }
85
+ function verifyProofs(mapping, inputs) {
86
+ if (!Array.isArray(mapping?.files) || !mapping.files.length) fail('proofs-invalid', 'Baseline source proofs are required.');
87
+ const seen = new Set();
88
+ for (const proof of mapping.files) {
89
+ if (!proof || typeof proof.file !== 'string' || seen.has(proof.file) || !Object.hasOwn(inputs, proof.file)
90
+ || proof.sha256 !== (inputs[proof.file] === null ? null : hash(inputs[proof.file]))) {
91
+ fail('proofs-invalid', 'Baseline proofs do not match the source bytes.', proof?.file);
92
+ }
93
+ seen.add(proof.file);
94
+ }
95
+ if (!seen.has(file) || !seen.has(identityFile)) fail('proofs-invalid', 'Source and identity proofs are both required.');
96
+ }
97
+ function verifyProjection(document, projection) {
98
+ for (const node of projection.nodes) {
99
+ const current = document.nodes[node.nodeId];
100
+ const parentId = node.nodeId === rootId ? definitionId : node.parentId;
101
+ if (!current || current.parentId !== parentId || !same(current.children, node.children) || !same(sourceLayout(current.layout), sourceLayout(node.layout))) {
102
+ fail('document-conflict', 'The reconciled source hierarchy differs from the edited document.');
103
+ }
104
+ }
105
+ }
106
+ async function reconcile(input, signal) {
107
+ try {
108
+ signal?.throwIfAborted();
109
+ const loadBaseline = input?.loadBaseline;
110
+ if (typeof loadBaseline !== 'function') fail('loader-invalid', 'A source baseline loader is required.');
111
+ const { target, baseline, document, baselineInputs, externalInputs, baselineIdentity } = capture({
112
+ target: input.target, baseline: input.baseline, document: input.document,
113
+ baselineInputs: input.baselineInputs, externalInputs: input.externalInputs,
114
+ ...(input.baselineIdentity === undefined ? {} : { baselineIdentity: input.baselineIdentity }),
115
+ });
116
+ if (target?.screenId !== screenId || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(target.sourceRevision ?? '')
117
+ || baseline?.document?.id !== documentId || document?.id !== documentId
118
+ || baseline.document.sourceRevision !== target.sourceRevision || document.sourceRevision !== target.sourceRevision
119
+ || !same(baseline.mapping?.target, target)) fail('target-conflict', 'Screen, document, and exact source revision must match.');
120
+ capturedInputs(baselineInputs); capturedInputs(externalInputs);
121
+ verifyProofs(baseline.mapping, baselineInputs);
122
+ const original = sourceIdentity(baselineInputs, baselineIdentity);
123
+ const latest = projectInputs({ baselineInputs, externalInputs, ...(baselineIdentity === undefined ? {} : { baselineIdentity }) });
124
+ const verifiedBaseline = capture(await loadBaseline(freeze({ target, sourceInputs: baselineInputs, projectedStructure: original.projectedStructure }), signal));
125
+ signal?.throwIfAborted();
126
+ if (!same(verifiedBaseline, baseline)) fail('baseline-conflict', 'The supplied baseline differs from the document projected from its source.');
127
+ const planned = compileDocumentSource({ target, before: baseline.document, after: document, mapping: baseline.mapping });
128
+ if (planned.status === 'blocked') return freeze({ status: 'conflict', diagnostics: planned.diagnostics });
129
+ const wrapper = original.wrapper;
130
+ const stylesheetFile = wrapper?.contract.stylesheet;
131
+ const reconciled = reconcileJsxStructurePlan({
132
+ file, baseline: { sourceRevision: target.sourceRevision, source: baselineInputs[file], identity: original.identity },
133
+ external: { sourceRevision: target.sourceRevision, source: externalInputs[file], sha256: hash(externalInputs[file]), identity: latest.identity },
134
+ operations: planned.operations, allowedFiles: wrapper ? [file, stylesheetFile] : [file],
135
+ ...(wrapper ? { wrapper: { contract: wrapper.contract, baselineSource: baselineInputs[stylesheetFile], externalSource: externalInputs[stylesheetFile], externalSha256: hash(externalInputs[stylesheetFile]) } } : {}),
136
+ });
137
+ if (reconciled.status === 'conflict') return reconciled;
138
+ const projection = reimportJsxStructureSource({ file, source: reconciled.source, identity: reconciled.identity,
139
+ ...(wrapper ? { wrapper: { contract: wrapper.contract, source: reconciled.stylesheetSource ?? externalInputs[stylesheetFile] } } : {}) });
140
+ verifyProjection(document, projection);
141
+ const current = capture(await loadBaseline(freeze({ target, sourceInputs: externalInputs, projectedStructure: latest.projectedStructure }), signal));
142
+ signal?.throwIfAborted();
143
+ verifyProofs(current.mapping, externalInputs);
144
+ if (!same(current.document, baseline.document)) fail('source-document-conflict', 'The current source projection changed the original document contract.');
145
+ const checked = compileDocumentSource({ target, before: current.document, after: document, mapping: current.mapping });
146
+ if (checked.status === 'blocked') return freeze({ status: 'conflict', diagnostics: checked.diagnostics });
147
+ signal?.throwIfAborted();
148
+ return freeze({ status: 'ready', baseline: JSON.parse(fingerprint(current)), document });
149
+ } catch (error) {
150
+ signal?.throwIfAborted();
151
+ return freeze({ status: 'conflict', diagnostics: error?.diagnostics ?? [{ code: 'source-reconcile-invalid', message: error instanceof Error ? error.message : String(error) }] });
152
+ }
153
+ }
154
+ return Object.freeze({ projectInputs, reconcile });
155
+ }