@pygmalionjs/pygmalion 0.9.0 → 0.10.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 +34 -0
- package/dist-lib/{CameraLayer-BdOko8XE.js → CameraLayer-BUOlJcUa.js} +1 -1
- package/dist-lib/pygmalion.js +6801 -6685
- package/dist-lib/{runtime-Dxb-IaLN.js → runtime-BOfDlGPR.js} +72 -52
- package/dist-lib/testing.js +15 -15
- package/dist-lib/types/binding/duplicateMetadata.d.ts +8 -0
- package/dist-lib/types/binding/index.d.ts +1 -0
- package/dist-lib/types/lib.d.ts +4 -2
- package/dist-lib/types/token-library/valueResolver.d.ts +19 -0
- package/dist-lib/types/workspace/configuration.d.ts +8 -0
- package/host-runtime.d.ts +73 -0
- package/jsx-source-reconciler.d.ts +35 -0
- package/node/host-runtime.mjs +4 -0
- package/node/jsx-source-reconciler.mjs +155 -0
- package/node/revision-catalog-resolver.mjs +159 -0
- package/node/source-archive-runtime.mjs +63 -0
- package/node/source-module.mjs +17 -0
- package/node/source-service-inputs.mjs +81 -0
- package/node/source-service-plugin.mjs +57 -0
- package/node/source-service.mjs +4 -0
- package/node/token-source-service.mjs +151 -0
- package/node/workspace-draft-entry.mjs +52 -0
- package/node/workspace-draft-preview.mjs +3 -2
- package/node/workspace-draft-process.mjs +129 -0
- package/node/workspace-draft-runtime.mjs +67 -0
- package/node/workspace-source-session.mjs +187 -0
- package/package.json +40 -2
- package/source-module.d.ts +16 -0
- package/source-service.d.ts +92 -0
- package/workspace-draft-entry.d.ts +15 -0
|
@@ -18872,6 +18872,25 @@ class XS {
|
|
|
18872
18872
|
this.disposed || (this.cancelPreparation(), this.disposed = !0, this.listeners.clear(), this.designContexts.clear());
|
|
18873
18873
|
}
|
|
18874
18874
|
}
|
|
18875
|
+
function dI({ resolve: t, maxEntries: e = 8 }) {
|
|
18876
|
+
if (typeof t != "function" || !Number.isSafeInteger(e) || e < 0)
|
|
18877
|
+
throw new Error("A target resolver requires a callback and a non-negative integer cache limit.");
|
|
18878
|
+
const n = /* @__PURE__ */ new Map();
|
|
18879
|
+
return (r) => {
|
|
18880
|
+
if (typeof (r == null ? void 0 : r.screenId) != "string" || !r.screenId.trim() || typeof r.sourceRevision != "string" || !r.sourceRevision.trim())
|
|
18881
|
+
throw new Error("A workspace target requires a screen and source revision.");
|
|
18882
|
+
const i = Object.freeze({ screenId: r.screenId, sourceRevision: r.sourceRevision }), s = JSON.stringify([i.screenId, i.sourceRevision]), o = n.get(s);
|
|
18883
|
+
if (o !== void 0)
|
|
18884
|
+
return n.delete(s), n.set(s, o), o;
|
|
18885
|
+
const a = t(i);
|
|
18886
|
+
if (a !== void 0 && e > 0)
|
|
18887
|
+
for (n.set(s, a); n.size > e; ) {
|
|
18888
|
+
const u = n.keys().next().value;
|
|
18889
|
+
u !== void 0 && n.delete(u);
|
|
18890
|
+
}
|
|
18891
|
+
return a;
|
|
18892
|
+
};
|
|
18893
|
+
}
|
|
18875
18894
|
function ZS(t, e) {
|
|
18876
18895
|
var r, i;
|
|
18877
18896
|
if (!((r = e.screenId) != null && r.trim()) || !((i = e.sourceRevision) != null && i.trim())) throw new Error("A workspace target requires a screen and source revision.");
|
|
@@ -19379,7 +19398,7 @@ function c_() {
|
|
|
19379
19398
|
createFrameLodController: p
|
|
19380
19399
|
};
|
|
19381
19400
|
}
|
|
19382
|
-
const
|
|
19401
|
+
const fI = 3, u_ = 5;
|
|
19383
19402
|
function l_() {
|
|
19384
19403
|
const t = /* @__PURE__ */ new Set();
|
|
19385
19404
|
let e = [], n = [], r = [], i = 0, s = 0;
|
|
@@ -19532,7 +19551,7 @@ const h_ = /* @__PURE__ */ new Set([
|
|
|
19532
19551
|
total: 0,
|
|
19533
19552
|
jobCount: 0
|
|
19534
19553
|
});
|
|
19535
|
-
function
|
|
19554
|
+
function hI(t) {
|
|
19536
19555
|
return `${t.replace(/\/$/, "")}${Nv}`;
|
|
19537
19556
|
}
|
|
19538
19557
|
function p_(t) {
|
|
@@ -19592,7 +19611,7 @@ function g_(t, e, n) {
|
|
|
19592
19611
|
state: s
|
|
19593
19612
|
};
|
|
19594
19613
|
}
|
|
19595
|
-
function
|
|
19614
|
+
function pI(t) {
|
|
19596
19615
|
const e = t.transport, n = e.endpoints.previewCaptureProgress ?? null, [r, i] = Ei(
|
|
19597
19616
|
_i
|
|
19598
19617
|
), s = t.schedulers.captureProgress, o = ku(
|
|
@@ -20684,7 +20703,7 @@ function L_(t) {
|
|
|
20684
20703
|
stamped: u ? i.isStampedFrozenPreview(u.root) : null
|
|
20685
20704
|
},
|
|
20686
20705
|
lodState: a.__debugFrameLodState(),
|
|
20687
|
-
readFramesNow: (await import("./CameraLayer-
|
|
20706
|
+
readFramesNow: (await import("./CameraLayer-BUOlJcUa.js")).__debugReadEditorFrames(t).find((h) => h.id === n)
|
|
20688
20707
|
};
|
|
20689
20708
|
},
|
|
20690
20709
|
/** Flow-session delivery states for headless diagnosis of the captureless supply. */
|
|
@@ -20735,7 +20754,7 @@ function L_(t) {
|
|
|
20735
20754
|
}
|
|
20736
20755
|
};
|
|
20737
20756
|
}
|
|
20738
|
-
function
|
|
20757
|
+
function mI(t = {}) {
|
|
20739
20758
|
const e = new F_(t.host), n = Pa(), r = gd(), i = Sa(), s = new Ki({ host: e, surfaces: r, declarations: n, shadowMounts: i }), o = Ia(), a = Fw({ metrics: o, declarations: n });
|
|
20740
20759
|
return j_({
|
|
20741
20760
|
store: s,
|
|
@@ -20908,7 +20927,7 @@ export {
|
|
|
20908
20927
|
F0 as ay,
|
|
20909
20928
|
k0 as az,
|
|
20910
20929
|
Mr as b,
|
|
20911
|
-
|
|
20930
|
+
ih as b$,
|
|
20912
20931
|
zu as b0,
|
|
20913
20932
|
Yf as b1,
|
|
20914
20933
|
Wf as b2,
|
|
@@ -20919,39 +20938,39 @@ export {
|
|
|
20919
20938
|
no as b7,
|
|
20920
20939
|
Pc as b8,
|
|
20921
20940
|
No as b9,
|
|
20922
|
-
|
|
20923
|
-
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
20935
|
-
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
|
|
20941
|
-
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20941
|
+
dI as bA,
|
|
20942
|
+
G0 as bB,
|
|
20943
|
+
ww as bC,
|
|
20944
|
+
_w as bD,
|
|
20945
|
+
X0 as bE,
|
|
20946
|
+
Q_ as bF,
|
|
20947
|
+
e0 as bG,
|
|
20948
|
+
g0 as bH,
|
|
20949
|
+
yu as bI,
|
|
20950
|
+
Uu as bJ,
|
|
20951
|
+
co as bK,
|
|
20952
|
+
ao as bL,
|
|
20953
|
+
Ep as bM,
|
|
20954
|
+
Va as bN,
|
|
20955
|
+
hI as bO,
|
|
20956
|
+
W_ as bP,
|
|
20957
|
+
E0 as bQ,
|
|
20958
|
+
A0 as bR,
|
|
20959
|
+
Jf as bS,
|
|
20960
|
+
q_ as bT,
|
|
20961
|
+
Sw as bU,
|
|
20962
|
+
eb as bV,
|
|
20963
|
+
$v as bW,
|
|
20964
|
+
j0 as bX,
|
|
20965
|
+
J0 as bY,
|
|
20966
|
+
fw as bZ,
|
|
20967
|
+
pI as b_,
|
|
20949
20968
|
d_ as ba,
|
|
20950
20969
|
dt as bb,
|
|
20951
20970
|
pd as bc,
|
|
20952
20971
|
Dd as bd,
|
|
20953
|
-
|
|
20954
|
-
|
|
20972
|
+
mI as be,
|
|
20973
|
+
fI as bf,
|
|
20955
20974
|
O0 as bg,
|
|
20956
20975
|
N0 as bh,
|
|
20957
20976
|
d0 as bi,
|
|
@@ -20973,23 +20992,24 @@ export {
|
|
|
20973
20992
|
Fd as by,
|
|
20974
20993
|
Gy as bz,
|
|
20975
20994
|
Zn as c,
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
|
|
20980
|
-
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
20995
|
+
m0 as c0,
|
|
20996
|
+
Lp as c1,
|
|
20997
|
+
Qy as c2,
|
|
20998
|
+
os as c3,
|
|
20999
|
+
Xb as c4,
|
|
21000
|
+
D0 as c5,
|
|
21001
|
+
z0 as c6,
|
|
21002
|
+
f0 as c7,
|
|
21003
|
+
T0 as c8,
|
|
21004
|
+
L0 as c9,
|
|
21005
|
+
H0 as ca,
|
|
21006
|
+
Y0 as cb,
|
|
21007
|
+
Ki as cc,
|
|
21008
|
+
NS as cd,
|
|
21009
|
+
je as ce,
|
|
21010
|
+
o0 as cf,
|
|
21011
|
+
s0 as cg,
|
|
21012
|
+
u0 as ch,
|
|
20993
21013
|
Xf as d,
|
|
20994
21014
|
K_ as e,
|
|
20995
21015
|
Hu as f,
|
package/dist-lib/testing.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { bi as
|
|
1
|
+
import { bW as l, T as u } from "./runtime-BOfDlGPR.js";
|
|
2
|
+
import { bi as y, cc as b, cd as E, ce as P, be as T, as as $ } from "./runtime-BOfDlGPR.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 ?? "",
|
|
12
|
+
const n = [], d = t.origin ?? "", e = (r) => {
|
|
13
13
|
var a;
|
|
14
14
|
const i = {};
|
|
15
|
-
for (const o of v[
|
|
16
|
-
const s = `${
|
|
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: (
|
|
31
|
-
mirror:
|
|
32
|
-
session:
|
|
33
|
-
inspect:
|
|
34
|
-
qa:
|
|
35
|
-
preview:
|
|
36
|
-
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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;
|
package/dist-lib/types/lib.d.ts
CHANGED
|
@@ -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';
|
|
@@ -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. */
|
|
@@ -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
|
+
}
|