@absolutejs/absolute 0.20.0-beta.77 → 0.20.0-beta.78
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/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +40 -26
- package/dist/build.js.map +4 -4
- package/dist/cli/{buildDepVendor-521faxfs.js → buildDepVendor-rz5w5cp0.js} +18 -9
- package/dist/cli/{dev-p8403w4q.js → dev-3xz7366n.js} +5 -5
- package/dist/cli/index.js +7 -2
- package/dist/cli/migrate-p4rp5r6b.js +521 -0
- package/dist/cli/{workspace-weacygfv.js → workspace-h7czpaqh.js} +29 -2
- package/dist/index.js +40 -26
- package/dist/index.js.map +4 -4
- package/dist/src/build/buildDepVendor.d.ts +1 -0
- package/dist/src/cli/scripts/migrate.d.ts +2 -0
- package/dist/src/cli/scripts/workspace.d.ts +14 -0
- package/dist/src/dev/moduleServer.d.ts +4 -0
- package/dist/src/migrate/detect.d.ts +25 -0
- package/dist/src/migrate/scan.d.ts +14 -0
- package/dist/src/migrate/substitutions.d.ts +37 -0
- package/dist/src/migrate/types.d.ts +40 -0
- package/package.json +1 -1
|
@@ -186,20 +186,30 @@ var collectBareImportsFromFile = async (entryPath, transpiler, maxDepth = 8) =>
|
|
|
186
186
|
}
|
|
187
187
|
return Array.from(bareOut);
|
|
188
188
|
};
|
|
189
|
+
var MAX_DISCOVERY_FILES = 2000;
|
|
189
190
|
var collectTransitiveImports = async (specs, alreadyVendored, alreadyScanned) => {
|
|
191
|
+
const { dirname } = await import("path");
|
|
190
192
|
const transpiler = new Bun.Transpiler({ loader: "js" });
|
|
191
193
|
const newSpecs = new Set;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
const queue = [...specs].map((spec) => ({ from: process.cwd(), spec }));
|
|
195
|
+
let visited = 0;
|
|
196
|
+
while (queue.length > 0 && visited < MAX_DISCOVERY_FILES) {
|
|
197
|
+
const next = queue.shift();
|
|
198
|
+
if (!next)
|
|
199
|
+
break;
|
|
200
|
+
const key = `${next.from}\x00${next.spec}`;
|
|
201
|
+
if (alreadyScanned.has(key))
|
|
194
202
|
continue;
|
|
195
|
-
alreadyScanned.add(
|
|
203
|
+
alreadyScanned.add(key);
|
|
196
204
|
let resolved;
|
|
197
205
|
try {
|
|
198
|
-
resolved = Bun.resolveSync(spec,
|
|
206
|
+
resolved = Bun.resolveSync(next.spec, next.from);
|
|
199
207
|
} catch {
|
|
200
208
|
continue;
|
|
201
209
|
}
|
|
210
|
+
visited += 1;
|
|
202
211
|
const bareImports = await collectBareImportsFromFile(resolved, transpiler);
|
|
212
|
+
const importerDirectory = dirname(resolved);
|
|
203
213
|
for (const child of bareImports) {
|
|
204
214
|
if (!isBareSpecifier(child))
|
|
205
215
|
continue;
|
|
@@ -209,11 +219,9 @@ var collectTransitiveImports = async (specs, alreadyVendored, alreadyScanned) =>
|
|
|
209
219
|
continue;
|
|
210
220
|
if (isAbsolutePackageSpecifier(child))
|
|
211
221
|
continue;
|
|
212
|
-
if (alreadyVendored.has(child))
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
continue;
|
|
216
|
-
newSpecs.add(child);
|
|
222
|
+
if (!alreadyVendored.has(child) && isResolvable(child))
|
|
223
|
+
newSpecs.add(child);
|
|
224
|
+
queue.push({ from: importerDirectory, spec: child });
|
|
217
225
|
}
|
|
218
226
|
}
|
|
219
227
|
return newSpecs;
|
|
@@ -383,5 +391,6 @@ var computeDepVendorPaths = async (directories, files = []) => {
|
|
|
383
391
|
};
|
|
384
392
|
export {
|
|
385
393
|
buildDepVendor,
|
|
394
|
+
collectTransitiveImports,
|
|
386
395
|
computeDepVendorPaths
|
|
387
396
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import {
|
|
3
|
-
acquireBuildDirectoryLock,
|
|
4
|
-
updateLockMetadata
|
|
5
|
-
} from "./index-4rme31py.js";
|
|
6
2
|
import {
|
|
7
3
|
TUNNEL_CONTROL_PATH,
|
|
8
4
|
TUNNEL_FORWARDED_HOST_HEADER,
|
|
9
5
|
decodeTunnelMessage,
|
|
10
6
|
encodeTunnelMessage
|
|
11
7
|
} from "./index-6h54jqdq.js";
|
|
8
|
+
import {
|
|
9
|
+
acquireBuildDirectoryLock,
|
|
10
|
+
updateLockMetadata
|
|
11
|
+
} from "./index-4rme31py.js";
|
|
12
12
|
import {
|
|
13
13
|
scanListeners
|
|
14
14
|
} from "./index-fhaf3tdk.js";
|
|
@@ -731,7 +731,7 @@ var serializeDependencies = (graph) => {
|
|
|
731
731
|
return dependencies;
|
|
732
732
|
};
|
|
733
733
|
var computePayload = async (config, vendorSourceDirs) => {
|
|
734
|
-
const { computeDepVendorPaths } = await import("./buildDepVendor-
|
|
734
|
+
const { computeDepVendorPaths } = await import("./buildDepVendor-rz5w5cp0.js");
|
|
735
735
|
const vendors = computeDepVendorPaths(vendorSourceDirs);
|
|
736
736
|
const graph = {
|
|
737
737
|
dependencies: new Map,
|
package/dist/cli/index.js
CHANGED
|
@@ -58,7 +58,7 @@ if (command === "dev") {
|
|
|
58
58
|
}
|
|
59
59
|
const positionalArgs = stripNamedArgs("--config", "--android-device", "--ios-device").filter((arg) => arg !== "--no-mobile" && arg !== "--eager");
|
|
60
60
|
const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
|
|
61
|
-
const { dev } = await import("./dev-
|
|
61
|
+
const { dev } = await import("./dev-3xz7366n.js");
|
|
62
62
|
await dev(serverEntry, configPath, {
|
|
63
63
|
androidDevice,
|
|
64
64
|
eager: args.includes("--eager"),
|
|
@@ -94,7 +94,7 @@ if (command === "dev") {
|
|
|
94
94
|
command: `workspace:${workspaceCommand ?? "unknown"}`
|
|
95
95
|
});
|
|
96
96
|
const configPath = parseNamedArg("--config");
|
|
97
|
-
const { workspace } = await import("./workspace-
|
|
97
|
+
const { workspace } = await import("./workspace-h7czpaqh.js");
|
|
98
98
|
await workspace(workspaceCommand, {
|
|
99
99
|
configPath,
|
|
100
100
|
noTui: args.includes("--no-tui")
|
|
@@ -215,6 +215,10 @@ if (command === "dev") {
|
|
|
215
215
|
const configPath = parseNamedArg("--config");
|
|
216
216
|
const { typecheck } = await import("./typecheck-7n5599tp.js");
|
|
217
217
|
await typecheck(configPath);
|
|
218
|
+
} else if (command === "migrate") {
|
|
219
|
+
sendTelemetryEvent("cli:command", { command });
|
|
220
|
+
const { migrate } = await import("./migrate-p4rp5r6b.js");
|
|
221
|
+
migrate(parseNamedArg("--path"));
|
|
218
222
|
} else if (command === "mkcert") {
|
|
219
223
|
sendTelemetryEvent("cli:command", { command });
|
|
220
224
|
const { setupMkcert } = await import("./devCert-avr89zf3.js");
|
|
@@ -257,6 +261,7 @@ if (command === "dev") {
|
|
|
257
261
|
console.error(" prettier Run Prettier check (cached)");
|
|
258
262
|
console.error(" routes [--json] List every route (pages + API) of a running dev server");
|
|
259
263
|
console.error(" typecheck Run type checkers for all frameworks");
|
|
264
|
+
console.error(" migrate Report what moving this project to AbsoluteJS involves");
|
|
260
265
|
console.error(" telemetry Manage anonymous telemetry");
|
|
261
266
|
console.error(" tunnel-relay Run the public reverse-tunnel relay (for webhook dev)");
|
|
262
267
|
process.exit(1);
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
colors
|
|
4
|
+
} from "./index-8nym68py.js";
|
|
5
|
+
import"./index-czwrxgdj.js";
|
|
6
|
+
import"./index-mywsk201.js";
|
|
7
|
+
|
|
8
|
+
// src/migrate/detect.ts
|
|
9
|
+
var CONFIDENT = 0.7;
|
|
10
|
+
var DEPENDENCY_SIGNALS = [
|
|
11
|
+
["@absolutejs/absolute", "absolutejs"],
|
|
12
|
+
["next", "nextjs"],
|
|
13
|
+
["nuxt", "nuxt"],
|
|
14
|
+
["@remix-run/react", "remix"],
|
|
15
|
+
["@remix-run/node", "remix"],
|
|
16
|
+
["@sveltejs/kit", "sveltekit"],
|
|
17
|
+
["astro", "astro"],
|
|
18
|
+
["react-scripts", "create-react-app"],
|
|
19
|
+
["vite", "vite"]
|
|
20
|
+
];
|
|
21
|
+
var FILE_SIGNALS = [
|
|
22
|
+
["absolute.config.ts", "absolutejs"],
|
|
23
|
+
["absolute.config.js", "absolutejs"],
|
|
24
|
+
["next.config.js", "nextjs"],
|
|
25
|
+
["next.config.mjs", "nextjs"],
|
|
26
|
+
["next.config.ts", "nextjs"],
|
|
27
|
+
["nuxt.config.ts", "nuxt"],
|
|
28
|
+
["remix.config.js", "remix"],
|
|
29
|
+
["svelte.config.js", "sveltekit"],
|
|
30
|
+
["astro.config.mjs", "astro"],
|
|
31
|
+
["vite.config.ts", "vite"],
|
|
32
|
+
["vite.config.js", "vite"]
|
|
33
|
+
];
|
|
34
|
+
var DEPENDENCY_WEIGHT = 0.75;
|
|
35
|
+
var FILE_WEIGHT = 0.3;
|
|
36
|
+
var CERTAIN = 1;
|
|
37
|
+
var STACK_PRECEDENCE = [
|
|
38
|
+
"absolutejs",
|
|
39
|
+
"nextjs",
|
|
40
|
+
"nuxt",
|
|
41
|
+
"remix",
|
|
42
|
+
"sveltekit",
|
|
43
|
+
"astro",
|
|
44
|
+
"create-react-app",
|
|
45
|
+
"vite",
|
|
46
|
+
"unknown"
|
|
47
|
+
];
|
|
48
|
+
var AMBIGUITY_PENALTY = 0.3;
|
|
49
|
+
var dependencyNames = (manifest) => {
|
|
50
|
+
if (!manifest)
|
|
51
|
+
return new Set;
|
|
52
|
+
return new Set([
|
|
53
|
+
...Object.keys(manifest.dependencies ?? {}),
|
|
54
|
+
...Object.keys(manifest.devDependencies ?? {}),
|
|
55
|
+
...Object.keys(manifest.peerDependencies ?? {})
|
|
56
|
+
]);
|
|
57
|
+
};
|
|
58
|
+
var SUBSUMED_BY_OTHERS = new Set(["vite"]);
|
|
59
|
+
var scoreSignals = (present, signals, weight, label) => {
|
|
60
|
+
const scores = new Map;
|
|
61
|
+
const evidence = [];
|
|
62
|
+
for (const [name, kind] of signals) {
|
|
63
|
+
if (!present(name))
|
|
64
|
+
continue;
|
|
65
|
+
scores.set(kind, (scores.get(kind) ?? 0) + weight);
|
|
66
|
+
evidence.push(label(name));
|
|
67
|
+
}
|
|
68
|
+
return { evidence, scores };
|
|
69
|
+
};
|
|
70
|
+
var merge = (into, from) => {
|
|
71
|
+
for (const [kind, score] of from) {
|
|
72
|
+
into.set(kind, (into.get(kind) ?? 0) + score);
|
|
73
|
+
}
|
|
74
|
+
return into;
|
|
75
|
+
};
|
|
76
|
+
var detectStack = ({
|
|
77
|
+
manifest,
|
|
78
|
+
files
|
|
79
|
+
}) => {
|
|
80
|
+
const dependencies = dependencyNames(manifest);
|
|
81
|
+
const names = new Set(files.map((file) => file.split("/").pop() ?? file));
|
|
82
|
+
const fromDependencies = scoreSignals((name) => dependencies.has(name), DEPENDENCY_SIGNALS, DEPENDENCY_WEIGHT, (name) => ({
|
|
83
|
+
detail: `depends on ${name}`,
|
|
84
|
+
source: "package.json"
|
|
85
|
+
}));
|
|
86
|
+
const fromFiles = scoreSignals((name) => names.has(name), FILE_SIGNALS, FILE_WEIGHT, (name) => ({ detail: "config file present", source: name }));
|
|
87
|
+
const scores = merge(new Map(fromDependencies.scores), fromFiles.scores);
|
|
88
|
+
const decisive = [...scores.keys()].some((kind2) => !SUBSUMED_BY_OTHERS.has(kind2));
|
|
89
|
+
if (decisive) {
|
|
90
|
+
for (const kind2 of SUBSUMED_BY_OTHERS)
|
|
91
|
+
scores.delete(kind2);
|
|
92
|
+
}
|
|
93
|
+
const rank = (kind2) => {
|
|
94
|
+
const index = STACK_PRECEDENCE.indexOf(kind2);
|
|
95
|
+
return index === -1 ? STACK_PRECEDENCE.length : index;
|
|
96
|
+
};
|
|
97
|
+
const ranked = [...scores.entries()].sort(([leftKind, left], [rightKind, right]) => right - left || rank(leftKind) - rank(rightKind));
|
|
98
|
+
const [top] = ranked;
|
|
99
|
+
if (!top) {
|
|
100
|
+
return {
|
|
101
|
+
alternates: [],
|
|
102
|
+
confidence: 0,
|
|
103
|
+
evidence: [],
|
|
104
|
+
kind: "unknown"
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const [kind, score] = top;
|
|
108
|
+
const [, runnerUpScore] = ranked[1] ?? [];
|
|
109
|
+
const ambiguous = runnerUpScore === score;
|
|
110
|
+
return {
|
|
111
|
+
alternates: ranked.slice(1).map(([alternate]) => alternate),
|
|
112
|
+
confidence: Math.max(0, Math.min(score, CERTAIN) - (ambiguous ? AMBIGUITY_PENALTY : 0)),
|
|
113
|
+
evidence: [...fromDependencies.evidence, ...fromFiles.evidence],
|
|
114
|
+
kind
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// src/migrate/scan.ts
|
|
119
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
120
|
+
import { join, relative } from "path";
|
|
121
|
+
// src/migrate/catalogSnapshot.json
|
|
122
|
+
var catalogSnapshot_default = {
|
|
123
|
+
comment: "Snapshot of first-party package names, used to validate migration suggestions offline. Studio passes its live catalog instead; regenerate with scripts/generateCatalog.ts in the Studio repo.",
|
|
124
|
+
generatedFrom: "@absolutejs/studio packageCatalog.json contract 2",
|
|
125
|
+
names: [
|
|
126
|
+
"@absolutejs/a2a",
|
|
127
|
+
"@absolutejs/agency",
|
|
128
|
+
"@absolutejs/agent",
|
|
129
|
+
"@absolutejs/agent-conformance",
|
|
130
|
+
"@absolutejs/agent-control",
|
|
131
|
+
"@absolutejs/agent-discovery",
|
|
132
|
+
"@absolutejs/agent-inbox",
|
|
133
|
+
"@absolutejs/agent-memory",
|
|
134
|
+
"@absolutejs/agent-reputation",
|
|
135
|
+
"@absolutejs/agent-runtime",
|
|
136
|
+
"@absolutejs/agent-sandbox",
|
|
137
|
+
"@absolutejs/agent-trust",
|
|
138
|
+
"@absolutejs/agents-mcp",
|
|
139
|
+
"@absolutejs/arazzo",
|
|
140
|
+
"@absolutejs/artifacts",
|
|
141
|
+
"@absolutejs/audience",
|
|
142
|
+
"@absolutejs/audit",
|
|
143
|
+
"@absolutejs/audit-elysia",
|
|
144
|
+
"@absolutejs/audit-postgres",
|
|
145
|
+
"@absolutejs/audit-s3",
|
|
146
|
+
"@absolutejs/auth",
|
|
147
|
+
"@absolutejs/beacon",
|
|
148
|
+
"@absolutejs/billing",
|
|
149
|
+
"@absolutejs/blob",
|
|
150
|
+
"@absolutejs/collectibles",
|
|
151
|
+
"@absolutejs/commerce",
|
|
152
|
+
"@absolutejs/commerce-customcat",
|
|
153
|
+
"@absolutejs/commerce-easypost",
|
|
154
|
+
"@absolutejs/commerce-resend",
|
|
155
|
+
"@absolutejs/commerce-stripe",
|
|
156
|
+
"@absolutejs/compliance",
|
|
157
|
+
"@absolutejs/crm",
|
|
158
|
+
"@absolutejs/discover",
|
|
159
|
+
"@absolutejs/dispatch",
|
|
160
|
+
"@absolutejs/dispatch-apns",
|
|
161
|
+
"@absolutejs/dispatch-fcm",
|
|
162
|
+
"@absolutejs/dispatch-postmark",
|
|
163
|
+
"@absolutejs/dispatch-resend",
|
|
164
|
+
"@absolutejs/dispatch-telnyx",
|
|
165
|
+
"@absolutejs/dispatch-twilio",
|
|
166
|
+
"@absolutejs/egress",
|
|
167
|
+
"@absolutejs/email",
|
|
168
|
+
"@absolutejs/engagement",
|
|
169
|
+
"@absolutejs/engagement-apollo",
|
|
170
|
+
"@absolutejs/enrich",
|
|
171
|
+
"@absolutejs/errors",
|
|
172
|
+
"@absolutejs/errors-postgres",
|
|
173
|
+
"@absolutejs/execution",
|
|
174
|
+
"@absolutejs/git",
|
|
175
|
+
"@absolutejs/health",
|
|
176
|
+
"@absolutejs/incidents",
|
|
177
|
+
"@absolutejs/logs",
|
|
178
|
+
"@absolutejs/marketplace",
|
|
179
|
+
"@absolutejs/mcp",
|
|
180
|
+
"@absolutejs/media",
|
|
181
|
+
"@absolutejs/meeting",
|
|
182
|
+
"@absolutejs/meeting-discord",
|
|
183
|
+
"@absolutejs/meeting-recall",
|
|
184
|
+
"@absolutejs/metering",
|
|
185
|
+
"@absolutejs/metrics",
|
|
186
|
+
"@absolutejs/observability",
|
|
187
|
+
"@absolutejs/onchain",
|
|
188
|
+
"@absolutejs/onchain-base",
|
|
189
|
+
"@absolutejs/outcomes",
|
|
190
|
+
"@absolutejs/partnership",
|
|
191
|
+
"@absolutejs/policy",
|
|
192
|
+
"@absolutejs/pwa",
|
|
193
|
+
"@absolutejs/queue",
|
|
194
|
+
"@absolutejs/queue-postgres",
|
|
195
|
+
"@absolutejs/queue-redis",
|
|
196
|
+
"@absolutejs/rag",
|
|
197
|
+
"@absolutejs/rag-pinecone",
|
|
198
|
+
"@absolutejs/rag-postgres",
|
|
199
|
+
"@absolutejs/rag-sqlite",
|
|
200
|
+
"@absolutejs/rate-limit",
|
|
201
|
+
"@absolutejs/reliability",
|
|
202
|
+
"@absolutejs/renown",
|
|
203
|
+
"@absolutejs/replay",
|
|
204
|
+
"@absolutejs/rules",
|
|
205
|
+
"@absolutejs/scoped-state",
|
|
206
|
+
"@absolutejs/secrets",
|
|
207
|
+
"@absolutejs/site-admin",
|
|
208
|
+
"@absolutejs/sync",
|
|
209
|
+
"@absolutejs/sync-automerge",
|
|
210
|
+
"@absolutejs/sync-bus-pg",
|
|
211
|
+
"@absolutejs/sync-bus-redis",
|
|
212
|
+
"@absolutejs/sync-loro",
|
|
213
|
+
"@absolutejs/sync-pack-comments",
|
|
214
|
+
"@absolutejs/sync-pack-counters",
|
|
215
|
+
"@absolutejs/sync-pack-digest",
|
|
216
|
+
"@absolutejs/sync-pack-favorites",
|
|
217
|
+
"@absolutejs/sync-pack-mentions",
|
|
218
|
+
"@absolutejs/sync-pack-notifications",
|
|
219
|
+
"@absolutejs/sync-pack-presence",
|
|
220
|
+
"@absolutejs/sync-pack-utils",
|
|
221
|
+
"@absolutejs/sync-yjs",
|
|
222
|
+
"@absolutejs/tour",
|
|
223
|
+
"@absolutejs/voice",
|
|
224
|
+
"@absolutejs/voice-assemblyai",
|
|
225
|
+
"@absolutejs/voice-azure",
|
|
226
|
+
"@absolutejs/voice-cartesia",
|
|
227
|
+
"@absolutejs/voice-deepgram",
|
|
228
|
+
"@absolutejs/voice-elevenlabs",
|
|
229
|
+
"@absolutejs/voice-gemini",
|
|
230
|
+
"@absolutejs/voice-gladia",
|
|
231
|
+
"@absolutejs/voice-google-speech",
|
|
232
|
+
"@absolutejs/voice-lmnt",
|
|
233
|
+
"@absolutejs/voice-neets",
|
|
234
|
+
"@absolutejs/voice-openai",
|
|
235
|
+
"@absolutejs/voice-openai-whisper",
|
|
236
|
+
"@absolutejs/voice-playht",
|
|
237
|
+
"@absolutejs/voice-rime",
|
|
238
|
+
"@absolutejs/voice-smallest",
|
|
239
|
+
"@absolutejs/voice-soniox",
|
|
240
|
+
"@absolutejs/voice-speechmatics",
|
|
241
|
+
"@absolutejs/vulnerabilities",
|
|
242
|
+
"@absolutejs/vulnerabilities-epss",
|
|
243
|
+
"@absolutejs/vulnerabilities-kev",
|
|
244
|
+
"@absolutejs/vulnerabilities-osv",
|
|
245
|
+
"@absolutejs/vulnerabilities-postgres",
|
|
246
|
+
"@absolutejs/vulnerabilities-report",
|
|
247
|
+
"@absolutejs/vulnerabilities-ubuntu",
|
|
248
|
+
"@absolutejs/vulnerabilities-witness",
|
|
249
|
+
"@absolutejs/vulnerabilities-worker",
|
|
250
|
+
"@absolutejs/wallet",
|
|
251
|
+
"@absolutejs/wallet-stripe",
|
|
252
|
+
"@absolutejs/webmcp"
|
|
253
|
+
]
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/migrate/substitutions.ts
|
|
257
|
+
var DEFAULT_SUBSTITUTION_RULES = [
|
|
258
|
+
{
|
|
259
|
+
from: "next-auth",
|
|
260
|
+
rationale: "Sessions, OAuth providers, MFA and passkeys, wired to the same request context.",
|
|
261
|
+
to: "@absolutejs/auth"
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
from: "bullmq",
|
|
265
|
+
rationale: "Durable job queue with a Postgres or Redis adapter instead of a required Redis.",
|
|
266
|
+
to: "@absolutejs/queue"
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
from: "bull",
|
|
270
|
+
rationale: "Durable job queue with first-party adapters.",
|
|
271
|
+
to: "@absolutejs/queue"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
from: "@sentry/node",
|
|
275
|
+
rationale: "Error capture, session replay and symbolication without a third-party account.",
|
|
276
|
+
to: "@absolutejs/errors"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
from: "@sentry/browser",
|
|
280
|
+
rationale: "Browser error capture reported through the same pipeline.",
|
|
281
|
+
to: "@absolutejs/beacon"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
from: "web-vitals",
|
|
285
|
+
rationale: "Real-user vitals already collected by the framework client.",
|
|
286
|
+
to: "@absolutejs/beacon"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
from: "nodemailer",
|
|
290
|
+
rationale: "Transactional send with swappable Resend, Postmark or SMTP adapters.",
|
|
291
|
+
to: "@absolutejs/dispatch"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
from: "resend",
|
|
295
|
+
rationale: "Same provider, behind an adapter you can change without touching call sites.",
|
|
296
|
+
to: "@absolutejs/dispatch-resend"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
from: "stripe",
|
|
300
|
+
rationale: "Checkout, subscriptions and webhooks with the ledger already modelled.",
|
|
301
|
+
to: "@absolutejs/commerce-stripe"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
from: "@aws-sdk/client-s3",
|
|
305
|
+
rationale: "Object storage with presigning and lifecycle handled for you.",
|
|
306
|
+
to: "@absolutejs/blob"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
from: "yjs",
|
|
310
|
+
rationale: "Collaborative state with presence, comments and history packs included.",
|
|
311
|
+
to: "@absolutejs/sync"
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
from: "zustand",
|
|
315
|
+
rationale: "Scoped client state that survives island boundaries.",
|
|
316
|
+
to: "@absolutejs/scoped-state"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
from: "express-rate-limit",
|
|
320
|
+
rationale: "Rate limiting applied at the route contract.",
|
|
321
|
+
to: "@absolutejs/rate-limit"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
from: "pino",
|
|
325
|
+
rationale: "Structured logging that joins the observability pipeline.",
|
|
326
|
+
to: "@absolutejs/logs"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
from: "winston",
|
|
330
|
+
rationale: "Structured logging that joins the observability pipeline.",
|
|
331
|
+
to: "@absolutejs/logs"
|
|
332
|
+
}
|
|
333
|
+
];
|
|
334
|
+
var planSubstitutions = ({
|
|
335
|
+
dependencies,
|
|
336
|
+
importsByPackage = {},
|
|
337
|
+
catalog,
|
|
338
|
+
rules = DEFAULT_SUBSTITUTION_RULES
|
|
339
|
+
}) => {
|
|
340
|
+
const declared = new Set(dependencies);
|
|
341
|
+
const available = new Set(catalog.map((entry) => entry.name));
|
|
342
|
+
return rules.filter((rule) => declared.has(rule.from) && available.has(rule.to)).map((rule) => ({
|
|
343
|
+
from: rule.from,
|
|
344
|
+
rationale: rule.rationale,
|
|
345
|
+
to: rule.to,
|
|
346
|
+
usedIn: [...importsByPackage[rule.from] ?? []]
|
|
347
|
+
})).sort((left, right) => right.usedIn.length - left.usedIn.length || left.from.localeCompare(right.from));
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// src/migrate/scan.ts
|
|
351
|
+
var SKIP_DIRECTORIES = new Set([
|
|
352
|
+
".git",
|
|
353
|
+
".next",
|
|
354
|
+
".turbo",
|
|
355
|
+
"build",
|
|
356
|
+
"coverage",
|
|
357
|
+
"dist",
|
|
358
|
+
"node_modules",
|
|
359
|
+
"out"
|
|
360
|
+
]);
|
|
361
|
+
var SOURCE_EXTENSIONS = [
|
|
362
|
+
".ts",
|
|
363
|
+
".tsx",
|
|
364
|
+
".js",
|
|
365
|
+
".jsx",
|
|
366
|
+
".mjs",
|
|
367
|
+
".vue",
|
|
368
|
+
".svelte"
|
|
369
|
+
];
|
|
370
|
+
var MAX_FILES = 5000;
|
|
371
|
+
var isSource = (file) => SOURCE_EXTENSIONS.some((extension) => file.endsWith(extension));
|
|
372
|
+
var entriesOf = (directory) => {
|
|
373
|
+
try {
|
|
374
|
+
return readdirSync(directory);
|
|
375
|
+
} catch {
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
var isDirectory = (path) => {
|
|
380
|
+
try {
|
|
381
|
+
return statSync(path).isDirectory();
|
|
382
|
+
} catch {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
var textOf = (path) => {
|
|
387
|
+
try {
|
|
388
|
+
return readFileSync(path, "utf-8");
|
|
389
|
+
} catch {
|
|
390
|
+
return "";
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
var walkOne = (root, directory) => entriesOf(directory).reduce((carry, entry) => {
|
|
394
|
+
if (SKIP_DIRECTORIES.has(entry))
|
|
395
|
+
return carry;
|
|
396
|
+
const absolute = join(directory, entry);
|
|
397
|
+
if (isDirectory(absolute)) {
|
|
398
|
+
carry.directories.push(absolute);
|
|
399
|
+
return carry;
|
|
400
|
+
}
|
|
401
|
+
carry.files.push(relative(root, absolute));
|
|
402
|
+
return carry;
|
|
403
|
+
}, { directories: [], files: [] });
|
|
404
|
+
var listProjectFiles = (root, limit = MAX_FILES) => {
|
|
405
|
+
const found = [];
|
|
406
|
+
const queue = [root];
|
|
407
|
+
while (queue.length > 0 && found.length < limit) {
|
|
408
|
+
const directory = queue.shift();
|
|
409
|
+
if (directory === undefined)
|
|
410
|
+
break;
|
|
411
|
+
const walk = walkOne(root, directory);
|
|
412
|
+
queue.push(...walk.directories);
|
|
413
|
+
found.push(...walk.files.slice(0, limit - found.length));
|
|
414
|
+
}
|
|
415
|
+
return found;
|
|
416
|
+
};
|
|
417
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
418
|
+
var sectionNames = (value) => isRecord(value) ? Object.keys(value) : [];
|
|
419
|
+
var readManifest = (root) => {
|
|
420
|
+
const path = join(root, "package.json");
|
|
421
|
+
if (!existsSync(path))
|
|
422
|
+
return null;
|
|
423
|
+
const parsed = JSON.parse(textOf(path) || "null");
|
|
424
|
+
if (!isRecord(parsed))
|
|
425
|
+
return null;
|
|
426
|
+
return parsed;
|
|
427
|
+
};
|
|
428
|
+
var declaredDependencies = (manifest) => {
|
|
429
|
+
if (!manifest)
|
|
430
|
+
return [];
|
|
431
|
+
const source = manifest;
|
|
432
|
+
return ["dependencies", "devDependencies", "peerDependencies"].flatMap((section) => sectionNames(source[section]));
|
|
433
|
+
};
|
|
434
|
+
var importsInFile = (text, packages) => packages.filter((name) => text.includes(`'${name}`) || text.includes(`"${name}`));
|
|
435
|
+
var findImporters = (root, files, packages) => files.filter(isSource).reduce((carry, file) => {
|
|
436
|
+
for (const name of importsInFile(textOf(join(root, file)), packages)) {
|
|
437
|
+
carry[name] = [...carry[name] ?? [], file];
|
|
438
|
+
}
|
|
439
|
+
return carry;
|
|
440
|
+
}, {});
|
|
441
|
+
var scanProject = (root, { catalog, rules } = {}) => {
|
|
442
|
+
const manifest = readManifest(root);
|
|
443
|
+
const files = listProjectFiles(root);
|
|
444
|
+
const dependencies = declaredDependencies(manifest);
|
|
445
|
+
const resolvedCatalog = catalog ?? catalogSnapshot_default.names.map((name) => ({ name }));
|
|
446
|
+
return {
|
|
447
|
+
detection: detectStack({ files, manifest }),
|
|
448
|
+
substitutions: planSubstitutions({
|
|
449
|
+
catalog: resolvedCatalog,
|
|
450
|
+
dependencies,
|
|
451
|
+
importsByPackage: findImporters(root, files, dependencies),
|
|
452
|
+
rules
|
|
453
|
+
})
|
|
454
|
+
};
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/cli/scripts/migrate.ts
|
|
458
|
+
var PERCENT = 100;
|
|
459
|
+
var paint = (code, text) => `${code}${text}${colors.reset}`;
|
|
460
|
+
var bold = (text) => paint(colors.bold, text);
|
|
461
|
+
var dim = (text) => paint(colors.dim, text);
|
|
462
|
+
var warn = (text) => paint(colors.yellow, text);
|
|
463
|
+
var stackLabel = {
|
|
464
|
+
absolutejs: "AbsoluteJS",
|
|
465
|
+
astro: "Astro",
|
|
466
|
+
"create-react-app": "Create React App",
|
|
467
|
+
nextjs: "Next.js",
|
|
468
|
+
nuxt: "Nuxt",
|
|
469
|
+
remix: "Remix",
|
|
470
|
+
sveltekit: "SvelteKit",
|
|
471
|
+
unknown: "not recognised",
|
|
472
|
+
vite: "Vite"
|
|
473
|
+
};
|
|
474
|
+
var renderDetection = (plan) => {
|
|
475
|
+
const { detection } = plan;
|
|
476
|
+
const percent = Math.round(detection.confidence * PERCENT);
|
|
477
|
+
const label = stackLabel[detection.kind] ?? detection.kind;
|
|
478
|
+
const lines = [`
|
|
479
|
+
Stack: ${bold(label)} (${percent}% confident)`];
|
|
480
|
+
for (const item of detection.evidence.slice(0, 4)) {
|
|
481
|
+
lines.push(dim(` ${item.source} \u2014 ${item.detail}`));
|
|
482
|
+
}
|
|
483
|
+
if (detection.alternates.length > 0) {
|
|
484
|
+
lines.push(dim(` also matched: ${detection.alternates.join(", ")}`));
|
|
485
|
+
}
|
|
486
|
+
if (detection.confidence < CONFIDENT && detection.kind !== "unknown") {
|
|
487
|
+
lines.push(warn(" Low confidence \u2014 confirm the stack before migrating."));
|
|
488
|
+
}
|
|
489
|
+
return lines.join(`
|
|
490
|
+
`);
|
|
491
|
+
};
|
|
492
|
+
var renderSubstitutions = (plan) => {
|
|
493
|
+
const { substitutions } = plan;
|
|
494
|
+
if (substitutions.length === 0) {
|
|
495
|
+
return `
|
|
496
|
+
${dim("No first-party substitutions found.")}`;
|
|
497
|
+
}
|
|
498
|
+
const lines = [
|
|
499
|
+
`
|
|
500
|
+
${bold(String(substitutions.length))} dependencies have a first-party equivalent:`
|
|
501
|
+
];
|
|
502
|
+
for (const item of substitutions) {
|
|
503
|
+
const where = item.usedIn.length > 0 ? `${item.usedIn.length} file${item.usedIn.length === 1 ? "" : "s"}` : "declared, never imported";
|
|
504
|
+
lines.push(` ${item.from} \u2192 ${bold(item.to)} ${dim(`(${where})`)}`);
|
|
505
|
+
lines.push(dim(` ${item.rationale}`));
|
|
506
|
+
}
|
|
507
|
+
lines.push(dim(`
|
|
508
|
+
Nothing was changed. This command only reports; apply the swaps yourself
|
|
509
|
+
or let Studio propose them as a reviewable diff.`));
|
|
510
|
+
return lines.join(`
|
|
511
|
+
`);
|
|
512
|
+
};
|
|
513
|
+
var migrate = (root = process.cwd()) => {
|
|
514
|
+
const plan = scanProject(root);
|
|
515
|
+
console.log(renderDetection(plan));
|
|
516
|
+
console.log(renderSubstitutions(plan));
|
|
517
|
+
console.log("");
|
|
518
|
+
};
|
|
519
|
+
export {
|
|
520
|
+
migrate
|
|
521
|
+
};
|
|
@@ -1104,6 +1104,20 @@ var warnForSharedBuildDirectories = (services, addLog) => {
|
|
|
1104
1104
|
addLog("workspace", `Services ${names.map((name) => `"${name}"`).join(" and ")} resolve to the same buildDirectory: ${buildDirectory}. Builds will be serialized and the last completed build will own that directory.`, "warn");
|
|
1105
1105
|
}
|
|
1106
1106
|
};
|
|
1107
|
+
var startWorkspaceOrShutDown = async ({
|
|
1108
|
+
onFailure,
|
|
1109
|
+
shutdown,
|
|
1110
|
+
start
|
|
1111
|
+
}) => {
|
|
1112
|
+
try {
|
|
1113
|
+
await start();
|
|
1114
|
+
return true;
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
onFailure(error instanceof Error ? error.message : String(error));
|
|
1117
|
+
await shutdown(1);
|
|
1118
|
+
return false;
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1107
1121
|
var workspace = async (subcommand, options) => {
|
|
1108
1122
|
if (subcommand !== "dev") {
|
|
1109
1123
|
throw new Error(subcommand ? `Unknown workspace command: ${subcommand}` : "No workspace subcommand specified. Use `absolute workspace dev`.");
|
|
@@ -1337,7 +1351,13 @@ var workspace = async (subcommand, options) => {
|
|
|
1337
1351
|
await killProcesses();
|
|
1338
1352
|
restarting = false;
|
|
1339
1353
|
workspaceBootStartedAt = performance.now();
|
|
1340
|
-
await
|
|
1354
|
+
const restarted = await startWorkspaceOrShutDown({
|
|
1355
|
+
shutdown,
|
|
1356
|
+
start: startServices,
|
|
1357
|
+
onFailure: (message) => addLog("workspace", `Workspace failed to restart: ${message}`, "error")
|
|
1358
|
+
});
|
|
1359
|
+
if (!restarted)
|
|
1360
|
+
return;
|
|
1341
1361
|
tui.setReadyDuration(performance.now() - workspaceBootStartedAt);
|
|
1342
1362
|
};
|
|
1343
1363
|
const togglePause = () => {
|
|
@@ -1402,12 +1422,19 @@ var workspace = async (subcommand, options) => {
|
|
|
1402
1422
|
shutdown(0);
|
|
1403
1423
|
});
|
|
1404
1424
|
tui.start();
|
|
1405
|
-
await
|
|
1425
|
+
const started = await startWorkspaceOrShutDown({
|
|
1426
|
+
shutdown,
|
|
1427
|
+
start: startServices,
|
|
1428
|
+
onFailure: (message) => addLog("workspace", `Workspace failed to start: ${message}`, "error")
|
|
1429
|
+
});
|
|
1430
|
+
if (!started)
|
|
1431
|
+
return;
|
|
1406
1432
|
tui.setReadyDuration(performance.now() - workspaceBootStartedAt);
|
|
1407
1433
|
await Promise.withResolvers().promise;
|
|
1408
1434
|
};
|
|
1409
1435
|
export {
|
|
1410
1436
|
findSharedWorkspaceBuildDirectories,
|
|
1411
1437
|
resolveWorkspaceServicePort,
|
|
1438
|
+
startWorkspaceOrShutDown,
|
|
1412
1439
|
workspace
|
|
1413
1440
|
};
|