@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.
- package/dist/index.cjs +88 -1742
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +78 -1731
- package/dist/loader-core-BthLSJQF.cjs +47 -0
- package/dist/loader-core-x6cOzF9v.mjs +42 -0
- package/dist/loader-core.d.ts +6 -13
- package/dist/loader-core.d.ts.map +1 -1
- package/dist/loader-path.d.ts +0 -13
- package/dist/loader-path.d.ts.map +1 -1
- package/dist/loader.cjs +10 -34
- package/dist/loader.d.ts +2 -24
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.mjs +3 -26
- package/dist/plugin.d.ts +18 -70
- package/dist/plugin.d.ts.map +1 -1
- package/dist/turbopack-config.d.ts +11 -38
- package/dist/turbopack-config.d.ts.map +1 -1
- package/dist/turbopack-loader.cjs +44 -64
- package/dist/turbopack-loader.d.ts +12 -24
- package/dist/turbopack-loader.d.ts.map +1 -1
- package/dist/turbopack-loader.mjs +40 -56
- package/dist/types.d.ts +25 -79
- package/dist/types.d.ts.map +1 -1
- package/dist/with-animus.d.ts +39 -24
- package/dist/with-animus.d.ts.map +1 -1
- package/package.json +8 -2
- package/dist/extraction-session.d.ts +0 -378
- package/dist/extraction-session.d.ts.map +0 -1
- package/dist/loader-core-C40WbC6s.cjs +0 -71
- package/dist/loader-core-D6Tm_b8F.mjs +0 -66
- package/dist/resolve-packages.d.ts +0 -8
- package/dist/resolve-packages.d.ts.map +0 -1
- package/dist/session-paths-CZoi19NW.cjs +0 -229
- package/dist/session-paths-DfYIPDcM.mjs +0 -107
- package/dist/session-paths.d.ts +0 -107
- package/dist/session-paths.d.ts.map +0 -1
- package/dist/singleton-BjSsGjsg.mjs +0 -217
- package/dist/singleton-BjZyw_8b.cjs +0 -366
- package/dist/singleton.d.ts +0 -96
- package/dist/singleton.d.ts.map +0 -1
- package/dist/timing.d.ts +0 -7
- package/dist/timing.d.ts.map +0 -1
- package/dist/turbopack-orchestrator.d.ts +0 -34
- 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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
95
|
-
for (let attempt = 0; 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(
|
|
118
|
-
const inputsRaw = readFileOrNull(
|
|
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 =
|
|
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(
|
|
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 } =
|
|
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(
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
*
|
|
166
|
-
|
|
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 (
|
|
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), (
|
|
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 ?
|
|
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 =
|
|
195
|
-
const statusPath =
|
|
196
|
-
const epochPath =
|
|
197
|
-
if (
|
|
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
|
-
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
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 ${
|
|
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
|
|
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 (
|
|
273
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
|
21
|
+
/** @internal test seam. */
|
|
27
22
|
export declare function __setTurbopackLoaderFsForTests(fs: LoaderFs | null): void;
|
|
28
|
-
/** @internal test seam
|
|
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":"
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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 <
|
|
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(
|
|
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 } =
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
*
|
|
139
|
-
|
|
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 (
|
|
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), (
|
|
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 (
|
|
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
|
-
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
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 ${
|
|
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
|
|
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 (
|
|
246
|
-
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|