@animus-ui/next-plugin 0.1.9 → 0.1.12

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 (45) hide show
  1. package/dist/index.cjs +88 -1742
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.mjs +78 -1731
  5. package/dist/loader-core-BthLSJQF.cjs +47 -0
  6. package/dist/loader-core-x6cOzF9v.mjs +42 -0
  7. package/dist/loader-core.d.ts +6 -13
  8. package/dist/loader-core.d.ts.map +1 -1
  9. package/dist/loader-path.d.ts +0 -13
  10. package/dist/loader-path.d.ts.map +1 -1
  11. package/dist/loader.cjs +10 -34
  12. package/dist/loader.d.ts +2 -24
  13. package/dist/loader.d.ts.map +1 -1
  14. package/dist/loader.mjs +3 -26
  15. package/dist/plugin.d.ts +18 -70
  16. package/dist/plugin.d.ts.map +1 -1
  17. package/dist/turbopack-config.d.ts +11 -38
  18. package/dist/turbopack-config.d.ts.map +1 -1
  19. package/dist/turbopack-loader.cjs +44 -64
  20. package/dist/turbopack-loader.d.ts +12 -24
  21. package/dist/turbopack-loader.d.ts.map +1 -1
  22. package/dist/turbopack-loader.mjs +40 -56
  23. package/dist/types.d.ts +25 -79
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/with-animus.d.ts +39 -24
  26. package/dist/with-animus.d.ts.map +1 -1
  27. package/package.json +8 -2
  28. package/dist/extraction-session.d.ts +0 -378
  29. package/dist/extraction-session.d.ts.map +0 -1
  30. package/dist/loader-core-C40WbC6s.cjs +0 -71
  31. package/dist/loader-core-D6Tm_b8F.mjs +0 -66
  32. package/dist/resolve-packages.d.ts +0 -8
  33. package/dist/resolve-packages.d.ts.map +0 -1
  34. package/dist/session-paths-CZoi19NW.cjs +0 -229
  35. package/dist/session-paths-DfYIPDcM.mjs +0 -107
  36. package/dist/session-paths.d.ts +0 -107
  37. package/dist/session-paths.d.ts.map +0 -1
  38. package/dist/singleton-BjSsGjsg.mjs +0 -217
  39. package/dist/singleton-BjZyw_8b.cjs +0 -366
  40. package/dist/singleton.d.ts +0 -96
  41. package/dist/singleton.d.ts.map +0 -1
  42. package/dist/timing.d.ts +0 -7
  43. package/dist/timing.d.ts.map +0 -1
  44. package/dist/turbopack-orchestrator.d.ts +0 -34
  45. package/dist/turbopack-orchestrator.d.ts.map +0 -1
@@ -19,14 +19,14 @@ var __copyProps = (to, from, except, desc) => {
19
19
  }
20
20
  return to;
21
21
  };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
23
23
  value: mod,
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
- const require_session_paths = require("./session-paths-CZoi19NW.cjs");
28
- const require_loader_core = require("./loader-core-C40WbC6s.cjs");
27
+ const require_loader_core = require("./loader-core-BthLSJQF.cjs");
29
28
  let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
29
+ let _animus_ui_extract_session = require("@animus-ui/extract/session");
30
30
  let fs = require("fs");
31
31
  fs = __toESM(fs);
32
32
  let path = require("path");
@@ -34,11 +34,8 @@ let path = require("path");
34
34
  let engine = null;
35
35
  let sentSources = null;
36
36
  let driftWarned = false;
37
- /** Hydration identity: `${commitPath}:${contentHash(commitRaw)}` — the
38
- * commit CONTENT keys hydration; mtime/size never do (design D1). */
39
37
  let hydratedKey = null;
40
38
  let hydratedManifestJson = null;
41
- /** Committed per-file analyzed content hashes (from the inputs). */
42
39
  let hydratedFileHashes = null;
43
40
  const engineModuleId = "@animus-ui/extract";
44
41
  const engineApi = (0, _animus_ui_extract_pipeline.createV2EngineApi)({
@@ -61,15 +58,18 @@ const engineApi = (0, _animus_ui_extract_pipeline.createV2EngineApi)({
61
58
  }
62
59
  }
63
60
  });
61
+ let engineApiImpl = engineApi;
62
+ /** @internal test seam. */
63
+ function __setTurbopackLoaderEngineApiForTests(api) {
64
+ engineApiImpl = api ?? engineApi;
65
+ }
64
66
  let fsImpl = fs;
65
- /** @internal test seam — pass null to restore the real fs. */
67
+ /** @internal test seam. */
66
68
  function __setTurbopackLoaderFsForTests(fs$1) {
67
69
  fsImpl = fs$1 ?? fs;
68
70
  }
69
- /** Epoch artifact paths SEEN on disk (per-worker) — the artifact is never
70
- * deleted once written, so a positive probe is stable; negatives re-probe. */
71
71
  const epochSeenPaths = /* @__PURE__ */ new Set();
72
- /** @internal test seam — drop worker-local hydration state. */
72
+ /** @internal test seam. */
73
73
  function __resetTurbopackLoaderStateForTests() {
74
74
  hydratedKey = null;
75
75
  hydratedManifestJson = null;
@@ -83,16 +83,10 @@ function readFileOrNull(path$1) {
83
83
  return null;
84
84
  }
85
85
  }
86
- const SEQLOCK_RETRIES = 5;
87
- /**
88
- * Seqlock-style artifact read: commit C0 → payloads → hash verification
89
- * against C0 → commit re-read; accept only if unchanged, else retry
90
- * (bounded) — a torn or mismatched set is never consumed (design D1).
91
- * Hydration replays the committed analysis inputs once per commit content.
92
- */
86
+ const ARTIFACT_READ_RETRIES = 5;
93
87
  function hydrateSession(sessionDir, sessionId) {
94
- const commitPath = require_session_paths.analysisCommitPath(sessionDir);
95
- for (let attempt = 0; attempt < SEQLOCK_RETRIES; attempt++) {
88
+ const commitPath = (0, _animus_ui_extract_session.analysisCommitPath)(sessionDir);
89
+ for (let attempt = 0; attempt < ARTIFACT_READ_RETRIES; attempt++) {
96
90
  const c0raw = readFileOrNull(commitPath);
97
91
  if (c0raw === null) return { kind: "absent" };
98
92
  let c0;
@@ -114,26 +108,26 @@ function hydrateSession(sessionDir, sessionId) {
114
108
  commitRaw: c0raw
115
109
  }
116
110
  };
117
- const manifestRaw = readFileOrNull(require_session_paths.manifestPath(sessionDir));
118
- const inputsRaw = readFileOrNull(require_session_paths.analysisInputsPath(sessionDir));
111
+ const manifestRaw = readFileOrNull((0, _animus_ui_extract_session.manifestPath)(sessionDir));
112
+ const inputsRaw = readFileOrNull((0, _animus_ui_extract_session.analysisInputsPath)(sessionDir));
119
113
  if (manifestRaw === null || inputsRaw === null) continue;
120
114
  if ((0, _animus_ui_extract_pipeline.contentHash)(manifestRaw) !== c0.manifestHash || (0, _animus_ui_extract_pipeline.contentHash)(inputsRaw) !== c0.inputsHash) continue;
121
115
  if (readFileOrNull(commitPath) !== c0raw) continue;
122
116
  let inputs;
123
117
  let fileHashes;
124
118
  try {
125
- const envelopeSession = require_session_paths.readJsonEnvelope(manifestRaw)?.sessionId;
119
+ const envelopeSession = (0, _animus_ui_extract_session.readJsonEnvelope)(manifestRaw)?.sessionId;
126
120
  if (envelopeSession !== void 0 && envelopeSession !== sessionId) return {
127
121
  kind: "foreign",
128
122
  artifactSessionId: String(envelopeSession)
129
123
  };
130
124
  const inputsParsed = JSON.parse(inputsRaw);
131
125
  inputs = inputsParsed;
132
- fileHashes = inputsParsed.analyzedHashes ? new Map(Object.entries(inputsParsed.analyzedHashes)) : new Map(JSON.parse(inputs.filesJson).map((entry) => [entry.path, entry.hash ?? (0, _animus_ui_extract_pipeline.contentHash)(entry.source)]));
126
+ fileHashes = inputsParsed.analyzedHashes ? new Map(Object.entries(inputsParsed.analyzedHashes)) : new Map((0, _animus_ui_extract_pipeline.parseFilesJson)(inputs.filesJson, "animus-next-turbopack").map((entry) => [entry.path, entry.hash ?? (0, _animus_ui_extract_pipeline.contentHash)(entry.source)]));
133
127
  } catch {
134
128
  return { kind: "torn" };
135
129
  }
136
- const { analyzeProject } = engineApi();
130
+ const { analyzeProject } = engineApiImpl();
137
131
  hydratedManifestJson = analyzeProject(...(0, _animus_ui_extract_pipeline.buildAnalyzeProjectArgs)(inputs));
138
132
  hydratedFileHashes = fileHashes;
139
133
  hydratedKey = key;
@@ -149,7 +143,7 @@ function hydrateSession(sessionDir, sessionId) {
149
143
  return { kind: "torn" };
150
144
  }
151
145
  function readStatus(sessionDir) {
152
- const raw = readFileOrNull(require_session_paths.analysisStatusPath(sessionDir));
146
+ const raw = readFileOrNull((0, _animus_ui_extract_session.analysisStatusPath)(sessionDir));
153
147
  if (raw === null) return null;
154
148
  try {
155
149
  return JSON.parse(raw);
@@ -157,54 +151,47 @@ function readStatus(sessionDir) {
157
151
  return null;
158
152
  }
159
153
  }
154
+ const hasReadableDeadline = (status) => Object.prototype.toString.call(status.deadlineAt) === "[object Number]";
160
155
  const CATCHUP_POLL_INITIAL_MS = 10;
161
156
  const CATCHUP_POLL_MAX_MS = 25;
162
157
  const CATCHUP_WAIT_MARGIN_MS = 50;
163
- const CATCHUP_WATCHDOG_MS = 2e3;
164
- /** The loader cannot read the orchestrator's configured debounce, so its
165
- * own ceiling assumes the default watcher debounce. */
166
- const DEFAULT_DEBOUNCE_CEILING_MS = 75;
158
+ /** Bounds the one wait carrying no deadline — a status whose `deadlineAt` is
159
+ * not numeric. The session-published deadline bounds every other wait.
160
+ * Equal to the session's status watchdog by coincidence only: never unify
161
+ * them, and never re-derive a deadline from the watcher debounce. */
162
+ const CATCHUP_NO_DEADLINE_CAP_MS = 2e3;
167
163
  function sleep(ms) {
168
164
  return new Promise((resolve) => setTimeout(resolve, ms));
169
165
  }
170
- const ACTIVE_STATES = new Set([
166
+ const ACTIVE_STATES = /* @__PURE__ */ new Set([
171
167
  "starting",
172
168
  "debouncing",
173
169
  "analyzing",
174
170
  "committing"
175
171
  ]);
176
- /**
177
- * Turbopack loader for Animus source transformation. Derives everything
178
- * from the incoming source, its JSON-serializable options (carrying the
179
- * session identity), and the session's COMMITTED disk artifacts written by
180
- * the out-of-band orchestrator. Async by contract — the evidence-based
181
- * catch-up (design D3) waits, bounded, for an imminent commit.
182
- */
183
172
  function animusTurbopackLoader(source) {
184
- if (typeof this.async !== "function") throw new Error("[animus-extract] the Animus Turbopack loader requires an async-capable loader runner (this.async is unavailable)");
173
+ if (this.async === void 0) throw new Error("[animus-extract] the Animus Turbopack loader requires an async-capable loader runner (this.async is unavailable)");
185
174
  const callback = this.async();
186
- runLoader(this, source).then((code) => callback(null, code), (err) => callback(err instanceof Error ? err : new Error(String(err))));
175
+ runLoader(this, source).then((code) => callback(null, code), (thrown) => callback(thrown instanceof Error ? thrown : new Error(String(thrown))));
187
176
  }
188
177
  async function runLoader(ctx, source) {
189
178
  const opts = ctx.getOptions?.() ?? {};
190
179
  const rootDir = opts.rootDir ?? ctx.rootContext;
191
180
  const sessionId = opts.sessionId;
192
- const sessionDir = opts.sessionDir ?? (sessionId !== void 0 ? require_session_paths.sessionArtifactDir(rootDir, sessionId) : void 0);
181
+ const sessionDir = opts.sessionDir ?? (sessionId !== void 0 ? (0, _animus_ui_extract_session.sessionArtifactDir)(rootDir, sessionId) : void 0);
193
182
  if (sessionId === void 0 || sessionDir === void 0) return source;
194
- const commitPath = require_session_paths.analysisCommitPath(sessionDir);
195
- const statusPath = require_session_paths.analysisStatusPath(sessionDir);
196
- const epochPath = require_session_paths.replacementEpochPath(sessionDir);
197
- if (typeof ctx.addDependency === "function" && (epochSeenPaths.has(epochPath) || fsImpl.existsSync(epochPath))) {
183
+ const commitPath = (0, _animus_ui_extract_session.analysisCommitPath)(sessionDir);
184
+ const statusPath = (0, _animus_ui_extract_session.analysisStatusPath)(sessionDir);
185
+ const epochPath = (0, _animus_ui_extract_session.replacementEpochPath)(sessionDir);
186
+ if (ctx.addDependency !== void 0 && (epochSeenPaths.has(epochPath) || fsImpl.existsSync(epochPath))) {
198
187
  epochSeenPaths.add(epochPath);
199
188
  ctx.addDependency(epochPath);
200
189
  }
201
190
  const filename = (0, path.relative)(rootDir, ctx.resourcePath);
202
- /** EVERY failing path registers the commit + status artifacts as
203
- * dependencies BEFORE throwing (the loader-runner sends dependency IPC
204
- * ahead of the error check upstream), so a landing commit or status
205
- * transition re-triggers the invocation — the documented
206
- * read→registration race makes this a recovery aid, never the primary
207
- * wakeup (design D3). Successful paths add NO commit/status dependency. */
191
+ /** Registers commit and status as dependencies before throwing: the runner
192
+ * sends dependency IPC ahead of the error, so a landing commit re-fires.
193
+ * Failure paths only — successful paths register neither, so this must not
194
+ * be hoisted out of the failure helper. */
208
195
  const coverageFailure = (message) => {
209
196
  ctx.addDependency?.(commitPath);
210
197
  ctx.addDependency?.(statusPath);
@@ -214,12 +201,12 @@ async function runLoader(ctx, source) {
214
201
  source,
215
202
  filename,
216
203
  manifestJson,
217
- engineApi,
204
+ engineApi: engineApiImpl,
218
205
  opts
219
206
  });
220
207
  const outcome = hydrateSession(sessionDir, sessionId);
221
208
  if (outcome.kind === "foreign") throw coverageFailure(`ANIMUS_FOREIGN_SESSION: artifacts under ${sessionDir} belong to session ${outcome.artifactSessionId}, not ${sessionId} — refusing to transform from a foreign generation`);
222
- if (outcome.kind === "torn") throw coverageFailure(`ANIMUS_ARTIFACT_READ_TORN: could not obtain a consistent committed artifact set for ${filename} after ${SEQLOCK_RETRIES} attempts`);
209
+ if (outcome.kind === "torn") throw coverageFailure(`ANIMUS_ARTIFACT_READ_TORN: could not obtain a consistent committed artifact set for ${filename} after ${ARTIFACT_READ_RETRIES} attempts`);
223
210
  let sourceHash = null;
224
211
  if (outcome.kind === "ok") {
225
212
  const analyzedHash = outcome.hydration.fileHashes.get(filename);
@@ -237,17 +224,9 @@ async function runLoader(ctx, source) {
237
224
  transform
238
225
  });
239
226
  }
240
- /**
241
- * The catch-up decision table (design D3 — implemented verbatim). Waits —
242
- * polled, bounded — ONLY in the two evidence states: commit absent while an
243
- * attempt is active, or the active attempt's pending set contains this file
244
- * at its current hash. Everything else fails immediately with its stable
245
- * diagnostic. The moment a commit lands that covers the input, the seqlock
246
- * hydration consumes it and the transform proceeds with no diagnostic.
247
- */
248
227
  async function awaitCoverage(args) {
249
228
  const { source, sourceHash, filename, sessionId, sessionDir, coverageFailure, transform } = args;
250
- const ceiling = Date.now() + DEFAULT_DEBOUNCE_CEILING_MS + CATCHUP_WATCHDOG_MS + CATCHUP_WAIT_MARGIN_MS;
229
+ const noDeadlineCap = Date.now() + CATCHUP_NO_DEADLINE_CAP_MS + CATCHUP_WAIT_MARGIN_MS;
251
230
  let poll = CATCHUP_POLL_INITIAL_MS;
252
231
  for (;;) {
253
232
  const outcome = hydrateSession(sessionDir, sessionId);
@@ -269,14 +248,15 @@ async function awaitCoverage(args) {
269
248
  if (!(status.pending ?? []).some(([key, hash]) => key === filename && hash === sourceHash)) throw coverageFailure(`ANIMUS_ANALYSIS_NOT_SCHEDULED: the active analysis attempt (${status.attemptId}) has not observed ${filename} at its current content`);
270
249
  }
271
250
  const now = Date.now();
272
- if (typeof status.deadlineAt === "number" && now > status.deadlineAt) throw coverageFailure(`ANIMUS_ANALYSIS_STALLED: analysis attempt ${status.attemptId} exceeded its deadline while ${filename} waited for coverage`);
273
- if (now > ceiling) throw coverageFailure(`ANIMUS_ANALYSIS_CATCHING_UP: ${filename} changed after the committed analysis; timed out waiting for the commit to advance — retrying on the next invalidation`);
251
+ if (hasReadableDeadline(status)) {
252
+ if (now > status.deadlineAt) throw coverageFailure(`ANIMUS_ANALYSIS_STALLED: analysis attempt ${status.attemptId} exceeded its deadline while ${filename} waited for coverage`);
253
+ } else if (now > noDeadlineCap) throw coverageFailure(`ANIMUS_ANALYSIS_CATCHING_UP: ${filename} changed after the committed analysis; timed out waiting for the commit to advance — retrying on the next invalidation`);
274
254
  await sleep(poll);
275
255
  poll = CATCHUP_POLL_MAX_MS;
276
256
  }
277
257
  }
278
258
  //#endregion
279
259
  exports.__resetTurbopackLoaderStateForTests = __resetTurbopackLoaderStateForTests;
260
+ exports.__setTurbopackLoaderEngineApiForTests = __setTurbopackLoaderEngineApiForTests;
280
261
  exports.__setTurbopackLoaderFsForTests = __setTurbopackLoaderFsForTests;
281
- exports.__toESM = __toESM;
282
262
  exports.default = animusTurbopackLoader;
@@ -1,39 +1,27 @@
1
1
  import * as nodeFs from 'fs';
2
- import type { LoaderPolicyOptions } from './loader-core';
3
- /** JSON-serializable loader options (Turbopack forwards them across process
4
- * boundaries). The session identity is a REAL task input (design D2):
5
- * restarts mint a new session id, so cross-session cache reuse is
6
- * impossible by construction — restart-cold is normative. */
2
+ import type { LoaderContextBase, LoaderPolicyOptions } from './loader-core';
3
+ import type { EngineApi } from '@animus-ui/extract/pipeline';
4
+ /** Turbopack runs loaders in isolated worker processes, so globalThis-keyed
5
+ * session state answers about another process: identity arrives through
6
+ * these options. The session barrel may be imported here for its pure path
7
+ * builders and JSON readers only, never for its singleton accessors. */
7
8
  export interface TurbopackLoaderOptions extends LoaderPolicyOptions {
8
9
  rootDir?: string;
9
10
  sessionId?: string;
10
11
  sessionDir?: string;
11
12
  }
12
13
  type LoaderCallback = (err: Error | null, content?: string) => void;
13
- type LoaderContext = {
14
- resourcePath: string;
15
- rootContext: string;
16
- getOptions: () => TurbopackLoaderOptions;
17
- /** File-dependency registration (Turbopack loader-runner IPC sends
18
- * dependencies BEFORE the error check — throw-after-addDependency is a
19
- * sound recovery primitive; see design D3). */
20
- addDependency?: (file: string) => void;
21
- /** Async-completion handle (webpack loader convention) — required: the
22
- * evidence-based catch-up wait cannot run synchronously. */
14
+ type LoaderContext = LoaderContextBase<TurbopackLoaderOptions> & {
23
15
  async?: () => LoaderCallback;
24
16
  };
17
+ type LoaderEngineApi = () => Pick<EngineApi, 'analyzeProject' | 'transformFile'>;
18
+ /** @internal test seam. */
19
+ export declare function __setTurbopackLoaderEngineApiForTests(api: LoaderEngineApi | null): void;
25
20
  type LoaderFs = Pick<typeof nodeFs, 'readFileSync' | 'existsSync'>;
26
- /** @internal test seam — pass null to restore the real fs. */
21
+ /** @internal test seam. */
27
22
  export declare function __setTurbopackLoaderFsForTests(fs: LoaderFs | null): void;
28
- /** @internal test seam — drop worker-local hydration state. */
23
+ /** @internal test seam. */
29
24
  export declare function __resetTurbopackLoaderStateForTests(): void;
30
- /**
31
- * Turbopack loader for Animus source transformation. Derives everything
32
- * from the incoming source, its JSON-serializable options (carrying the
33
- * session identity), and the session's COMMITTED disk artifacts written by
34
- * the out-of-band orchestrator. Async by contract — the evidence-based
35
- * catch-up (design D3) waits, bounded, for an imminent commit.
36
- */
37
25
  export default function animusTurbopackLoader(this: LoaderContext, source: string): void;
38
26
  export {};
39
27
  //# sourceMappingURL=turbopack-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"turbopack-loader.d.ts","sourceRoot":"","sources":["../src/turbopack-loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC;AAc7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAOzD;;;8DAG8D;AAC9D,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAEpE,KAAK,aAAa,GAAG;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,sBAAsB,CAAC;IACzC;;oDAEgD;IAChD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC;iEAC6D;IAC7D,KAAK,CAAC,EAAE,MAAM,cAAc,CAAC;CAC9B,CAAC;AAgDF,KAAK,QAAQ,GAAG,IAAI,CAAC,OAAO,MAAM,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;AAGnE,8DAA8D;AAC9D,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAExE;AAMD,+DAA+D;AAC/D,wBAAgB,mCAAmC,IAAI,IAAI,CAK1D;AAkKD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAC3C,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,MAAM,GACb,IAAI,CAcN"}
1
+ {"version":3,"file":"turbopack-loader.d.ts","sourceRoot":"","sources":["../src/turbopack-loader.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC;AAK7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,KAAK,EAEV,SAAS,EAEV,MAAM,6BAA6B,CAAC;AAUrC;;;yEAGyE;AACzE,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAEpE,KAAK,aAAa,GAAG,iBAAiB,CAAC,sBAAsB,CAAC,GAAG;IAC/D,KAAK,CAAC,EAAE,MAAM,cAAc,CAAC;CAC9B,CAAC;AAgCF,KAAK,eAAe,GAAG,MAAM,IAAI,CAC/B,SAAS,EACT,gBAAgB,GAAG,eAAe,CACnC,CAAC;AAGF,2BAA2B;AAC3B,wBAAgB,qCAAqC,CACnD,GAAG,EAAE,eAAe,GAAG,IAAI,GAC1B,IAAI,CAEN;AAED,KAAK,QAAQ,GAAG,IAAI,CAAC,OAAO,MAAM,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;AAGnE,2BAA2B;AAC3B,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAExE;AAID,2BAA2B;AAC3B,wBAAgB,mCAAmC,IAAI,IAAI,CAK1D;AA6ID,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAC3C,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,MAAM,GACb,IAAI,CAYN"}
@@ -1,20 +1,21 @@
1
- import { S as __require, _ as replacementEpochPath, d as analysisStatusPath, g as readJsonEnvelope, l as analysisCommitPath, m as manifestPath, u as analysisInputsPath, v as sessionArtifactDir } from "./session-paths-DfYIPDcM.mjs";
2
- import { t as transformWithManifest } from "./loader-core-D6Tm_b8F.mjs";
3
- import { buildAnalyzeProjectArgs, contentHash, createV2EngineApi } from "@animus-ui/extract/pipeline";
1
+ import { t as transformWithManifest } from "./loader-core-x6cOzF9v.mjs";
2
+ import { createRequire } from "node:module";
3
+ import { buildAnalyzeProjectArgs, contentHash, createV2EngineApi, parseFilesJson } from "@animus-ui/extract/pipeline";
4
+ import { analysisCommitPath, analysisInputsPath, analysisStatusPath, manifestPath, readJsonEnvelope, replacementEpochPath, sessionArtifactDir } from "@animus-ui/extract/session";
4
5
  import * as nodeFs from "fs";
5
6
  import { relative } from "path";
7
+ //#region \0rolldown/runtime.js
8
+ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
9
+ //#endregion
6
10
  //#region src/turbopack-loader.ts
7
11
  let engine = null;
8
12
  let sentSources = null;
9
13
  let driftWarned = false;
10
- /** Hydration identity: `${commitPath}:${contentHash(commitRaw)}` — the
11
- * commit CONTENT keys hydration; mtime/size never do (design D1). */
12
14
  let hydratedKey = null;
13
15
  let hydratedManifestJson = null;
14
- /** Committed per-file analyzed content hashes (from the inputs). */
15
16
  let hydratedFileHashes = null;
16
17
  const engineModuleId = "@animus-ui/extract";
17
- const engineApi = createV2EngineApi({
18
+ const engineApi$1 = createV2EngineApi({
18
19
  label: "animus-next-turbopack",
19
20
  isV2: () => true,
20
21
  loadNativeEngine: () => __require(engineModuleId),
@@ -34,15 +35,18 @@ const engineApi = createV2EngineApi({
34
35
  }
35
36
  }
36
37
  });
38
+ let engineApiImpl = engineApi$1;
39
+ /** @internal test seam. */
40
+ function __setTurbopackLoaderEngineApiForTests(api) {
41
+ engineApiImpl = api ?? engineApi$1;
42
+ }
37
43
  let fsImpl = nodeFs;
38
- /** @internal test seam — pass null to restore the real fs. */
44
+ /** @internal test seam. */
39
45
  function __setTurbopackLoaderFsForTests(fs) {
40
46
  fsImpl = fs ?? nodeFs;
41
47
  }
42
- /** Epoch artifact paths SEEN on disk (per-worker) — the artifact is never
43
- * deleted once written, so a positive probe is stable; negatives re-probe. */
44
48
  const epochSeenPaths = /* @__PURE__ */ new Set();
45
- /** @internal test seam — drop worker-local hydration state. */
49
+ /** @internal test seam. */
46
50
  function __resetTurbopackLoaderStateForTests() {
47
51
  hydratedKey = null;
48
52
  hydratedManifestJson = null;
@@ -56,16 +60,10 @@ function readFileOrNull(path) {
56
60
  return null;
57
61
  }
58
62
  }
59
- const SEQLOCK_RETRIES = 5;
60
- /**
61
- * Seqlock-style artifact read: commit C0 → payloads → hash verification
62
- * against C0 → commit re-read; accept only if unchanged, else retry
63
- * (bounded) — a torn or mismatched set is never consumed (design D1).
64
- * Hydration replays the committed analysis inputs once per commit content.
65
- */
63
+ const ARTIFACT_READ_RETRIES = 5;
66
64
  function hydrateSession(sessionDir, sessionId) {
67
65
  const commitPath = analysisCommitPath(sessionDir);
68
- for (let attempt = 0; attempt < SEQLOCK_RETRIES; attempt++) {
66
+ for (let attempt = 0; attempt < ARTIFACT_READ_RETRIES; attempt++) {
69
67
  const c0raw = readFileOrNull(commitPath);
70
68
  if (c0raw === null) return { kind: "absent" };
71
69
  let c0;
@@ -102,11 +100,11 @@ function hydrateSession(sessionDir, sessionId) {
102
100
  };
103
101
  const inputsParsed = JSON.parse(inputsRaw);
104
102
  inputs = inputsParsed;
105
- fileHashes = inputsParsed.analyzedHashes ? new Map(Object.entries(inputsParsed.analyzedHashes)) : new Map(JSON.parse(inputs.filesJson).map((entry) => [entry.path, entry.hash ?? contentHash(entry.source)]));
103
+ fileHashes = inputsParsed.analyzedHashes ? new Map(Object.entries(inputsParsed.analyzedHashes)) : new Map(parseFilesJson(inputs.filesJson, "animus-next-turbopack").map((entry) => [entry.path, entry.hash ?? contentHash(entry.source)]));
106
104
  } catch {
107
105
  return { kind: "torn" };
108
106
  }
109
- const { analyzeProject } = engineApi();
107
+ const { analyzeProject } = engineApiImpl();
110
108
  hydratedManifestJson = analyzeProject(...buildAnalyzeProjectArgs(inputs));
111
109
  hydratedFileHashes = fileHashes;
112
110
  hydratedKey = key;
@@ -130,33 +128,28 @@ function readStatus(sessionDir) {
130
128
  return null;
131
129
  }
132
130
  }
131
+ const hasReadableDeadline = (status) => Object.prototype.toString.call(status.deadlineAt) === "[object Number]";
133
132
  const CATCHUP_POLL_INITIAL_MS = 10;
134
133
  const CATCHUP_POLL_MAX_MS = 25;
135
134
  const CATCHUP_WAIT_MARGIN_MS = 50;
136
- const CATCHUP_WATCHDOG_MS = 2e3;
137
- /** The loader cannot read the orchestrator's configured debounce, so its
138
- * own ceiling assumes the default watcher debounce. */
139
- const DEFAULT_DEBOUNCE_CEILING_MS = 75;
135
+ /** Bounds the one wait carrying no deadline — a status whose `deadlineAt` is
136
+ * not numeric. The session-published deadline bounds every other wait.
137
+ * Equal to the session's status watchdog by coincidence only: never unify
138
+ * them, and never re-derive a deadline from the watcher debounce. */
139
+ const CATCHUP_NO_DEADLINE_CAP_MS = 2e3;
140
140
  function sleep(ms) {
141
141
  return new Promise((resolve) => setTimeout(resolve, ms));
142
142
  }
143
- const ACTIVE_STATES = new Set([
143
+ const ACTIVE_STATES = /* @__PURE__ */ new Set([
144
144
  "starting",
145
145
  "debouncing",
146
146
  "analyzing",
147
147
  "committing"
148
148
  ]);
149
- /**
150
- * Turbopack loader for Animus source transformation. Derives everything
151
- * from the incoming source, its JSON-serializable options (carrying the
152
- * session identity), and the session's COMMITTED disk artifacts written by
153
- * the out-of-band orchestrator. Async by contract — the evidence-based
154
- * catch-up (design D3) waits, bounded, for an imminent commit.
155
- */
156
149
  function animusTurbopackLoader(source) {
157
- if (typeof this.async !== "function") throw new Error("[animus-extract] the Animus Turbopack loader requires an async-capable loader runner (this.async is unavailable)");
150
+ if (this.async === void 0) throw new Error("[animus-extract] the Animus Turbopack loader requires an async-capable loader runner (this.async is unavailable)");
158
151
  const callback = this.async();
159
- runLoader(this, source).then((code) => callback(null, code), (err) => callback(err instanceof Error ? err : new Error(String(err))));
152
+ runLoader(this, source).then((code) => callback(null, code), (thrown) => callback(thrown instanceof Error ? thrown : new Error(String(thrown))));
160
153
  }
161
154
  async function runLoader(ctx, source) {
162
155
  const opts = ctx.getOptions?.() ?? {};
@@ -167,17 +160,15 @@ async function runLoader(ctx, source) {
167
160
  const commitPath = analysisCommitPath(sessionDir);
168
161
  const statusPath = analysisStatusPath(sessionDir);
169
162
  const epochPath = replacementEpochPath(sessionDir);
170
- if (typeof ctx.addDependency === "function" && (epochSeenPaths.has(epochPath) || fsImpl.existsSync(epochPath))) {
163
+ if (ctx.addDependency !== void 0 && (epochSeenPaths.has(epochPath) || fsImpl.existsSync(epochPath))) {
171
164
  epochSeenPaths.add(epochPath);
172
165
  ctx.addDependency(epochPath);
173
166
  }
174
167
  const filename = relative(rootDir, ctx.resourcePath);
175
- /** EVERY failing path registers the commit + status artifacts as
176
- * dependencies BEFORE throwing (the loader-runner sends dependency IPC
177
- * ahead of the error check upstream), so a landing commit or status
178
- * transition re-triggers the invocation — the documented
179
- * read→registration race makes this a recovery aid, never the primary
180
- * wakeup (design D3). Successful paths add NO commit/status dependency. */
168
+ /** Registers commit and status as dependencies before throwing: the runner
169
+ * sends dependency IPC ahead of the error, so a landing commit re-fires.
170
+ * Failure paths only — successful paths register neither, so this must not
171
+ * be hoisted out of the failure helper. */
181
172
  const coverageFailure = (message) => {
182
173
  ctx.addDependency?.(commitPath);
183
174
  ctx.addDependency?.(statusPath);
@@ -187,12 +178,12 @@ async function runLoader(ctx, source) {
187
178
  source,
188
179
  filename,
189
180
  manifestJson,
190
- engineApi,
181
+ engineApi: engineApiImpl,
191
182
  opts
192
183
  });
193
184
  const outcome = hydrateSession(sessionDir, sessionId);
194
185
  if (outcome.kind === "foreign") throw coverageFailure(`ANIMUS_FOREIGN_SESSION: artifacts under ${sessionDir} belong to session ${outcome.artifactSessionId}, not ${sessionId} — refusing to transform from a foreign generation`);
195
- if (outcome.kind === "torn") throw coverageFailure(`ANIMUS_ARTIFACT_READ_TORN: could not obtain a consistent committed artifact set for ${filename} after ${SEQLOCK_RETRIES} attempts`);
186
+ if (outcome.kind === "torn") throw coverageFailure(`ANIMUS_ARTIFACT_READ_TORN: could not obtain a consistent committed artifact set for ${filename} after ${ARTIFACT_READ_RETRIES} attempts`);
196
187
  let sourceHash = null;
197
188
  if (outcome.kind === "ok") {
198
189
  const analyzedHash = outcome.hydration.fileHashes.get(filename);
@@ -210,17 +201,9 @@ async function runLoader(ctx, source) {
210
201
  transform
211
202
  });
212
203
  }
213
- /**
214
- * The catch-up decision table (design D3 — implemented verbatim). Waits —
215
- * polled, bounded — ONLY in the two evidence states: commit absent while an
216
- * attempt is active, or the active attempt's pending set contains this file
217
- * at its current hash. Everything else fails immediately with its stable
218
- * diagnostic. The moment a commit lands that covers the input, the seqlock
219
- * hydration consumes it and the transform proceeds with no diagnostic.
220
- */
221
204
  async function awaitCoverage(args) {
222
205
  const { source, sourceHash, filename, sessionId, sessionDir, coverageFailure, transform } = args;
223
- const ceiling = Date.now() + DEFAULT_DEBOUNCE_CEILING_MS + CATCHUP_WATCHDOG_MS + CATCHUP_WAIT_MARGIN_MS;
206
+ const noDeadlineCap = Date.now() + CATCHUP_NO_DEADLINE_CAP_MS + CATCHUP_WAIT_MARGIN_MS;
224
207
  let poll = CATCHUP_POLL_INITIAL_MS;
225
208
  for (;;) {
226
209
  const outcome = hydrateSession(sessionDir, sessionId);
@@ -242,11 +225,12 @@ async function awaitCoverage(args) {
242
225
  if (!(status.pending ?? []).some(([key, hash]) => key === filename && hash === sourceHash)) throw coverageFailure(`ANIMUS_ANALYSIS_NOT_SCHEDULED: the active analysis attempt (${status.attemptId}) has not observed ${filename} at its current content`);
243
226
  }
244
227
  const now = Date.now();
245
- if (typeof status.deadlineAt === "number" && now > status.deadlineAt) throw coverageFailure(`ANIMUS_ANALYSIS_STALLED: analysis attempt ${status.attemptId} exceeded its deadline while ${filename} waited for coverage`);
246
- if (now > ceiling) throw coverageFailure(`ANIMUS_ANALYSIS_CATCHING_UP: ${filename} changed after the committed analysis; timed out waiting for the commit to advance — retrying on the next invalidation`);
228
+ if (hasReadableDeadline(status)) {
229
+ if (now > status.deadlineAt) throw coverageFailure(`ANIMUS_ANALYSIS_STALLED: analysis attempt ${status.attemptId} exceeded its deadline while ${filename} waited for coverage`);
230
+ } else if (now > noDeadlineCap) throw coverageFailure(`ANIMUS_ANALYSIS_CATCHING_UP: ${filename} changed after the committed analysis; timed out waiting for the commit to advance — retrying on the next invalidation`);
247
231
  await sleep(poll);
248
232
  poll = CATCHUP_POLL_MAX_MS;
249
233
  }
250
234
  }
251
235
  //#endregion
252
- export { __resetTurbopackLoaderStateForTests, __setTurbopackLoaderFsForTests, animusTurbopackLoader as default };
236
+ export { __resetTurbopackLoaderStateForTests, __setTurbopackLoaderEngineApiForTests, __setTurbopackLoaderFsForTests, animusTurbopackLoader as default };
package/dist/types.d.ts CHANGED
@@ -2,104 +2,50 @@ import type { StaticCssConfig } from '@animus-ui/extract/pipeline';
2
2
  export interface AnimusNextOptions {
3
3
  /** Path to a module exporting a SystemInstance from `@animus-ui/system`. */
4
4
  system: string;
5
- /** Glob patterns to exclude from analysis. */
5
+ /** Substrings, or globs when `*`/`?` present. Replaces `dist`/`.test.`/
6
+ * `.spec.`; `node_modules`, `.next` and `.animus` are always excluded. */
6
7
  exclude?: string[];
7
- /**
8
- * File extensions to scan for component definitions and JSX usages.
9
- * Replaces the default list entirely (not additive). Include `.mdx` to
10
- * extract components rendered from MDX files — `@mdx-js/mdx` must be
11
- * installed as a peer for MDX files to be preprocessed; otherwise the
12
- * plugin warns once at buildStart and skips them.
13
- *
14
- * @default ['.ts', '.tsx', '.js', '.jsx', '.mdx']
15
- */
8
+ /** Extensions scanned for definitions and usages; replaces the default
9
+ * `['.ts','.tsx','.js','.jsx','.mdx']`. `.mdx` needs `@mdx-js/mdx`. */
16
10
  extensions?: string[];
17
11
  /** When true, extraction failures throw instead of warning. */
18
12
  strict?: boolean;
19
- /**
20
- * Project-root-relative path of the single file that receives the
21
- * `import '.animus/styles.css'` injection (the stylesheet import is
22
- * stripped from every other file to prevent per-chunk CSS duplication).
23
- * Set this when your root entry doesn't match the default detection of
24
- * `app/layout.*` / `pages/_app.*`, optionally under `src/` — e.g. a
25
- * nested App Router root layout like `'src/app/[locale]/layout.tsx'`.
26
- */
13
+ /** Project-root-relative path of the one file that receives the stylesheet
14
+ * import; replaces the `app/layout.*` / `pages/_app.*` detection. */
27
15
  cssImportTarget?: string;
28
- /** Enable verbose logging. */
29
16
  verbose?: boolean;
30
17
  /** Namespace prefix for CSS variables and class names. */
31
18
  prefix?: string;
32
- /**
33
- * Forced-emission declarations for usage the scanner cannot observe
34
- * (CMS-driven variants, spread-hidden props, dynamically selected
35
- * components). Declared variants/states/system-prop values and custom
36
- * dynamic slots are emitted as if used; entries are labeled as forced
37
- * in the extraction report. Empty/absent is a no-op.
38
- */
19
+ /** Forced-emission declarations for usage the scanner cannot observe
20
+ * (CMS-driven variants, spread-hidden props). Absent is a no-op. */
39
21
  staticCss?: StaticCssConfig;
40
- /**
41
- * Browser targets for CSS autoprefixing and syntax lowering.
42
- * Accepts a browserslist query string or array of queries.
43
- * Falls back to project's browserslist config, then to `defaults`.
44
- */
22
+ /** Browserslist query string or array. Falls back to the project's
23
+ * browserslist config, then to `defaults`. */
45
24
  targets?: string | string[];
46
- /**
47
- * Control CSS minification of the emitted stylesheet.
48
- * - `true`: always minify (dev + prod)
49
- * - `false`: never minify (autoprefixing still applies)
50
- * - `undefined` (default): minify in production (`NODE_ENV === 'production'`)
51
- */
25
+ /** `true`/`false` force minification on or off; absent minifies in
26
+ * production. Autoprefixing applies either way. */
52
27
  minify?: boolean;
53
- /**
54
- * Extraction engine selection. `'v2'` is the only engine and the default,
55
- * propagated to every compiler instance (including non-owning ones). The v1
56
- * engine was retired (openspec: retire-extract-v1); configuring
57
- * `engine: 'v1'` (or setting `ANIMUS_ENGINE=v1`) throws — the selection is
58
- * never silently upgraded.
59
- *
60
- * @default 'v2'
61
- */
28
+ /** Explicit emission mode, winning over the environment signal that
29
+ * otherwise decides minification and the dev-diagnostics define. */
30
+ mode?: 'development' | 'production';
31
+ /** `'v2'` is the only engine and the default. `engine: 'v1'` or
32
+ * `ANIMUS_ENGINE=v1` throws — the selection is never silently upgraded. */
62
33
  engine?: 'v2';
63
- /**
64
- * Turbopack integration mode.
65
- *
66
- * - `'auto'` (default): activate whenever the process runs under
67
- * Turbopack (Next sets the `TURBOPACK` environment variable for every
68
- * Turbopack dev/build). Zero-config support for Next 16 defaults and
69
- * `next dev --turbopack` on Next 15.
70
- * - `'on'`: always generate Turbopack wiring.
71
- * - `'off'`: webpack-only; no Turbopack keys are generated.
72
- *
73
- * When active, extraction runs during next.config resolution (the wrapped
74
- * config resolves asynchronously — Next awaits it), a dev watcher re-runs
75
- * analysis on source changes, and per-file transforms run in a stateless
76
- * loader fed by `.animus/` disk artifacts.
77
- */
34
+ /** `'auto'` (default) generates Turbopack wiring whenever the `TURBOPACK`
35
+ * environment variable is set; `'on'` always, `'off'` never. */
78
36
  turbopack?: {
79
37
  mode?: 'auto' | 'on' | 'off';
80
38
  };
81
- /**
82
- * @deprecated Use `turbopack` — same shape. Honored for one release
83
- * cycle with a one-time warning; `turbopack` wins when both are set.
84
- */
39
+ /** @deprecated Use `turbopack` — same shape, and it wins when both are
40
+ * set. This alias warns once. */
85
41
  unstable_turbopack?: {
86
42
  mode?: 'off' | 'auto' | 'on';
87
43
  };
88
- /**
89
- * @internal Absolute path of the loader module registered in the webpack
90
- * rule when it is not this package's own dist/src loader (test harnesses
91
- * wrapping the real loader in a require-able shim). Feeds the needBuild
92
- * loader-chain predicate; `withAnimus` never sets it.
93
- */
44
+ /** @internal Absolute path of a loader module registered in place of this
45
+ * package's own; feeds the needBuild loader-chain predicate. */
94
46
  loaderPath?: string;
95
- /**
96
- * Full `@layer` declaration order. Must include all 7 Animus `anm-*`
97
- * layers (`anm-global`, `anm-base`, `anm-variants`, `anm-compounds`,
98
- * `anm-states`, `anm-system`, `anm-custom`) as a subsequence. Consumer
99
- * layers may be interleaved. Names are emitted as-is.
100
- *
101
- * @example ['reset', 'anm-global', 'anm-base', 'anm-variants', 'anm-compounds', 'anm-states', 'anm-system', 'anm-custom', 'overrides']
102
- */
47
+ /** Full `@layer` declaration order, emitted as-is. Must contain the seven
48
+ * `anm-*` layers as a subsequence; consumer layers may be interleaved. */
103
49
  layers?: string[];
104
50
  }
105
51
  //# sourceMappingURL=types.d.ts.map