everything-dev 2.0.0-rc.7 → 2.0.0-rc.9

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 (142) hide show
  1. package/dist/api/auth-middleware.cjs +14 -1
  2. package/dist/api/auth-middleware.cjs.map +1 -1
  3. package/dist/api/auth-middleware.d.cts +9 -0
  4. package/dist/api/auth-middleware.d.mts +9 -0
  5. package/dist/api/auth-middleware.mjs +14 -1
  6. package/dist/api/auth-middleware.mjs.map +1 -1
  7. package/dist/apps/upload/src/client.cjs +7 -1
  8. package/dist/apps/upload/src/client.cjs.map +1 -1
  9. package/dist/apps/upload/src/client.mjs +7 -1
  10. package/dist/apps/upload/src/client.mjs.map +1 -1
  11. package/dist/auth-login.cjs +24 -1
  12. package/dist/auth-login.cjs.map +1 -1
  13. package/dist/auth-login.mjs +24 -1
  14. package/dist/auth-login.mjs.map +1 -1
  15. package/dist/auth-session.cjs +4 -2
  16. package/dist/auth-session.cjs.map +1 -1
  17. package/dist/auth-session.mjs +4 -2
  18. package/dist/auth-session.mjs.map +1 -1
  19. package/dist/cdn-deploy.cjs +11 -7
  20. package/dist/cdn-deploy.cjs.map +1 -1
  21. package/dist/cdn-deploy.mjs +11 -7
  22. package/dist/cdn-deploy.mjs.map +1 -1
  23. package/dist/cli/import.cjs +1140 -0
  24. package/dist/cli/import.cjs.map +1 -0
  25. package/dist/cli/import.mjs +1126 -0
  26. package/dist/cli/import.mjs.map +1 -0
  27. package/dist/cli/init.cjs +33 -14
  28. package/dist/cli/init.cjs.map +1 -1
  29. package/dist/cli/init.d.cts +8 -2
  30. package/dist/cli/init.d.mts +8 -2
  31. package/dist/cli/init.mjs +33 -15
  32. package/dist/cli/init.mjs.map +1 -1
  33. package/dist/cli.cjs +59 -3
  34. package/dist/cli.cjs.map +1 -1
  35. package/dist/cli.mjs +59 -3
  36. package/dist/cli.mjs.map +1 -1
  37. package/dist/commands/import.cjs +49 -0
  38. package/dist/commands/import.cjs.map +1 -0
  39. package/dist/commands/import.mjs +46 -0
  40. package/dist/commands/import.mjs.map +1 -0
  41. package/dist/commands/keys.cjs +25 -4
  42. package/dist/commands/keys.cjs.map +1 -1
  43. package/dist/commands/keys.mjs +25 -4
  44. package/dist/commands/keys.mjs.map +1 -1
  45. package/dist/config.d.cts +1 -0
  46. package/dist/config.d.mts +1 -0
  47. package/dist/contract.cjs +89 -0
  48. package/dist/contract.cjs.map +1 -1
  49. package/dist/contract.d.cts +190 -1
  50. package/dist/contract.d.mts +190 -1
  51. package/dist/contract.meta.cjs +17 -1
  52. package/dist/contract.meta.cjs.map +1 -1
  53. package/dist/contract.meta.d.cts +27 -1
  54. package/dist/contract.meta.d.mts +27 -1
  55. package/dist/contract.meta.mjs +17 -1
  56. package/dist/contract.meta.mjs.map +1 -1
  57. package/dist/contract.mjs +84 -1
  58. package/dist/contract.mjs.map +1 -1
  59. package/dist/custody-signer.cjs +70 -0
  60. package/dist/custody-signer.cjs.map +1 -0
  61. package/dist/custody-signer.mjs +68 -0
  62. package/dist/custody-signer.mjs.map +1 -0
  63. package/dist/db/core.cjs +22 -4
  64. package/dist/db/core.cjs.map +1 -1
  65. package/dist/db/core.mjs +22 -4
  66. package/dist/db/core.mjs.map +1 -1
  67. package/dist/descriptor/schema.cjs +8 -5
  68. package/dist/descriptor/schema.cjs.map +1 -1
  69. package/dist/descriptor/schema.d.cts +1 -0
  70. package/dist/descriptor/schema.d.mts +1 -0
  71. package/dist/descriptor/schema.mjs +8 -5
  72. package/dist/descriptor/schema.mjs.map +1 -1
  73. package/dist/descriptor/serialize.cjs +1 -1
  74. package/dist/descriptor/serialize.cjs.map +1 -1
  75. package/dist/descriptor/serialize.mjs +1 -1
  76. package/dist/descriptor/serialize.mjs.map +1 -1
  77. package/dist/http-client.cjs +14 -4
  78. package/dist/http-client.cjs.map +1 -1
  79. package/dist/http-client.mjs +14 -4
  80. package/dist/http-client.mjs.map +1 -1
  81. package/dist/index.cjs +6 -0
  82. package/dist/index.d.cts +2 -2
  83. package/dist/index.d.mts +2 -2
  84. package/dist/index.mjs +2 -2
  85. package/dist/infra/planner.cjs +6 -2
  86. package/dist/infra/planner.cjs.map +1 -1
  87. package/dist/infra/planner.mjs +6 -2
  88. package/dist/infra/planner.mjs.map +1 -1
  89. package/dist/outlayer-login.cjs +54 -0
  90. package/dist/outlayer-login.cjs.map +1 -0
  91. package/dist/outlayer-login.mjs +54 -0
  92. package/dist/outlayer-login.mjs.map +1 -0
  93. package/dist/plugin.cjs +2 -0
  94. package/dist/plugin.cjs.map +1 -1
  95. package/dist/plugin.d.cts +74 -0
  96. package/dist/plugin.d.mts +74 -0
  97. package/dist/plugin.mjs +2 -0
  98. package/dist/plugin.mjs.map +1 -1
  99. package/dist/publish.cjs +145 -22
  100. package/dist/publish.cjs.map +1 -1
  101. package/dist/publish.mjs +147 -24
  102. package/dist/publish.mjs.map +1 -1
  103. package/dist/source-archive.cjs +51 -0
  104. package/dist/source-archive.cjs.map +1 -0
  105. package/dist/source-archive.mjs +50 -0
  106. package/dist/source-archive.mjs.map +1 -0
  107. package/dist/storage-upload.cjs +6 -1
  108. package/dist/storage-upload.cjs.map +1 -1
  109. package/dist/storage-upload.mjs +6 -1
  110. package/dist/storage-upload.mjs.map +1 -1
  111. package/dist/types.cjs +5 -1
  112. package/dist/types.cjs.map +1 -1
  113. package/dist/types.d.cts +2 -0
  114. package/dist/types.d.mts +2 -0
  115. package/dist/types.mjs +5 -1
  116. package/dist/types.mjs.map +1 -1
  117. package/dist/ui/auth.d.cts +217 -123
  118. package/dist/ui/auth.d.mts +217 -123
  119. package/dist/ui/router-client.cjs +2 -1
  120. package/dist/ui/router-client.cjs.map +1 -1
  121. package/dist/ui/router-client.mjs +2 -1
  122. package/dist/ui/router-client.mjs.map +1 -1
  123. package/dist/utils/workspace-subtree.cjs +37 -0
  124. package/dist/utils/workspace-subtree.cjs.map +1 -0
  125. package/dist/utils/workspace-subtree.mjs +36 -0
  126. package/dist/utils/workspace-subtree.mjs.map +1 -0
  127. package/dist/version-manifest-deploy.cjs +4 -2
  128. package/dist/version-manifest-deploy.cjs.map +1 -1
  129. package/dist/version-manifest-deploy.mjs +4 -2
  130. package/dist/version-manifest-deploy.mjs.map +1 -1
  131. package/dist/version-manifest-resolve.cjs +2 -1
  132. package/dist/version-manifest-resolve.cjs.map +1 -1
  133. package/dist/version-manifest-resolve.d.cts +3 -0
  134. package/dist/version-manifest-resolve.d.mts +3 -0
  135. package/dist/version-manifest-resolve.mjs +2 -1
  136. package/dist/version-manifest-resolve.mjs.map +1 -1
  137. package/package.json +11 -10
  138. package/skills/api-and-auth/SKILL.md +12 -9
  139. package/skills/api-and-auth/references/middleware.md +38 -0
  140. package/skills/cli-reference/SKILL.md +38 -6
  141. package/skills/init-upgrade/SKILL.md +76 -3
  142. package/skills/talk-to-the-app/SKILL.md +32 -5
@@ -0,0 +1,1140 @@
1
+ const require_http_client = require('../http-client.cjs');
2
+ const require_merge = require('../merge.cjs');
3
+ const require_fastkv = require('../fastkv.cjs');
4
+ const require_config = require('../config.cjs');
5
+ const require_snapshot_hash = require('../utils/snapshot-hash.cjs');
6
+ const require_snapshot = require('./snapshot.cjs');
7
+ const require_cli_init = require('./init.cjs');
8
+ const require_resolution_session = require('../resolution/session.cjs');
9
+ const require_integrity = require('../integrity.cjs');
10
+ const require_workspace = require('../workspace.cjs');
11
+ const require_workspace_subtree = require('../utils/workspace-subtree.cjs');
12
+ const require_sync = require('./sync.cjs');
13
+ let node_fs = require("node:fs");
14
+ let node_path = require("node:path");
15
+ let every_plugin_version_manifest = require("every-plugin/version-manifest");
16
+ let node_os = require("node:os");
17
+ let glob = require("glob");
18
+ let tar = require("tar");
19
+
20
+ //#region src/cli/import.ts
21
+ const IMPORT_SNAPSHOT_FILE = "import-snapshot.json";
22
+ const IMPORT_REPORT_FILE = "import-report.md";
23
+ const IMPORT_REPORT_PATH = `.bos/${IMPORT_REPORT_FILE}`;
24
+ function isExistingDir(path) {
25
+ try {
26
+ return (0, node_fs.existsSync)(path) && (0, node_fs.statSync)(path).isDirectory();
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+ function normalizeGithubRepo(spec) {
32
+ if (require_cli_init.parseGitHubUrl(spec)) return spec;
33
+ const match = spec.match(/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/?$/);
34
+ if (!match?.[1] || !match[2]) return null;
35
+ if (match[1] === "." || match[1] === ".." || match[2] === "." || match[2] === "..") return null;
36
+ return `https://github.com/${match[1]}/${match[2]}`;
37
+ }
38
+ function parseFromSpec(spec) {
39
+ const trimmed = spec.trim();
40
+ if (!trimmed) throw new Error("--from is required: bos://<account>/<gateway>, <owner>/<repo>[@<ref>], a GitHub URL, or a local directory");
41
+ if (trimmed.startsWith("bos://")) {
42
+ const hashIndex = trimmed.indexOf("#");
43
+ const bosPart = hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex);
44
+ if (hashIndex === -1 ? void 0 : trimmed.slice(hashIndex + 1).trim()) throw new Error(`Targeted extends refs are not supported for import sources: ${spec}. Omit the #target and pass the workspace as the positional argument.`);
45
+ const parsed = require_fastkv.parseBosUrl(bosPart);
46
+ if (parsed.pathSegments.length > 0) throw new Error(`Unsupported bos:// path — expected bos://<account>/<gateway>: ${spec}`);
47
+ return {
48
+ kind: "bos",
49
+ bosUrl: `bos://${parsed.accountId}/${parsed.gatewayId}`,
50
+ account: parsed.accountId,
51
+ gateway: parsed.gatewayId
52
+ };
53
+ }
54
+ if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
55
+ if (!isExistingDir(trimmed)) throw new Error(`Local directory not found: ${trimmed}`);
56
+ return {
57
+ kind: "local",
58
+ dir: (0, node_path.resolve)(trimmed)
59
+ };
60
+ }
61
+ const at = trimmed.indexOf("@");
62
+ if (at > 0) {
63
+ const head = trimmed.slice(0, at);
64
+ const ref = trimmed.slice(at + 1);
65
+ const repo = normalizeGithubRepo(head);
66
+ if (repo && ref) return {
67
+ kind: "github",
68
+ repo,
69
+ ref
70
+ };
71
+ }
72
+ const repo = normalizeGithubRepo(trimmed);
73
+ if (repo) return {
74
+ kind: "github",
75
+ repo
76
+ };
77
+ if (isExistingDir(trimmed)) return {
78
+ kind: "local",
79
+ dir: (0, node_path.resolve)(trimmed)
80
+ };
81
+ throw new Error(`Cannot parse --from source: ${spec}. Expected bos://<account>/<gateway>, <owner>/<repo>[@<ref>], a GitHub URL, or a local directory.`);
82
+ }
83
+ function normalizeWorkspacePath(input) {
84
+ const trimmed = input.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
85
+ if ((0, node_path.isAbsolute)(input)) throw new Error(`Workspace path must be repo-relative: ${input}`);
86
+ if (!trimmed || trimmed === "." || trimmed === "..") throw new Error(`Invalid workspace path: ${input}`);
87
+ if (trimmed.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) throw new Error(`Invalid workspace path: ${input}`);
88
+ return trimmed;
89
+ }
90
+ function classifyImportFile(input) {
91
+ const { localHash, upstreamHash, baselineHash, mergeable } = input;
92
+ if (localHash === null) return { kind: "added" };
93
+ if (localHash === upstreamHash) return { kind: "unchanged" };
94
+ if (baselineHash === void 0) return mergeable ? { kind: "merged" } : {
95
+ kind: "conflict",
96
+ reason: "no-baseline"
97
+ };
98
+ if (localHash === baselineHash) return mergeable ? { kind: "merged" } : { kind: "updated" };
99
+ if (upstreamHash === baselineHash) return { kind: "preserved-local-edit" };
100
+ return mergeable ? { kind: "merged" } : {
101
+ kind: "conflict",
102
+ reason: "both-changed"
103
+ };
104
+ }
105
+ function extractContractRoutes(source) {
106
+ const routes = /* @__PURE__ */ new Set();
107
+ const marker = ".route(";
108
+ let cursor = source.indexOf(marker);
109
+ while (cursor !== -1) {
110
+ const open = cursor + 7 - 1;
111
+ let depth = 0;
112
+ let end = open;
113
+ for (; end < source.length; end++) {
114
+ const char = source[end];
115
+ if (char === "(") depth++;
116
+ else if (char === ")") {
117
+ depth--;
118
+ if (depth === 0) break;
119
+ }
120
+ }
121
+ const arg = source.slice(open + 1, end);
122
+ const method = arg.match(/\bmethod:\s*["']([A-Za-z]+)["']/)?.[1];
123
+ const path = arg.match(/\bpath:\s*["']([^"']+)["']/)?.[1];
124
+ if (method && path) routes.add(`${method} ${path}`);
125
+ cursor = source.indexOf(marker, cursor + 1);
126
+ }
127
+ return [...routes].sort();
128
+ }
129
+ function computeRouteDelta(ours, theirs) {
130
+ const oursSet = new Set(ours);
131
+ const theirsSet = new Set(theirs);
132
+ return {
133
+ oursOnly: ours.filter((route) => !theirsSet.has(route)),
134
+ theirsOnly: theirs.filter((route) => !oursSet.has(route))
135
+ };
136
+ }
137
+ async function collectContractRouteSources(root, workspace) {
138
+ const sources = [];
139
+ const mainPath = (0, node_path.join)(root, workspace, "src", "contract.ts");
140
+ if ((0, node_fs.existsSync)(mainPath)) sources.push((0, node_fs.readFileSync)(mainPath, "utf-8"));
141
+ const splitDir = (0, node_path.join)(root, workspace, "src", "contract");
142
+ if ((0, node_fs.existsSync)(splitDir) && (0, node_fs.lstatSync)(splitDir).isDirectory()) {
143
+ const matches = await (0, glob.glob)("**/*.{ts,tsx}", {
144
+ cwd: splitDir,
145
+ nodir: true,
146
+ absolute: false
147
+ });
148
+ for (const match of matches.sort()) sources.push((0, node_fs.readFileSync)((0, node_path.join)(splitDir, match), "utf-8"));
149
+ }
150
+ return sources.length > 0 ? sources : null;
151
+ }
152
+ function extractWorkspaceRoutes(sources) {
153
+ if (sources === null) return null;
154
+ const routes = /* @__PURE__ */ new Set();
155
+ for (const source of sources) for (const route of extractContractRoutes(source)) routes.add(route);
156
+ return [...routes].sort();
157
+ }
158
+ function loadMigrationEntries(root, workspace) {
159
+ const journalPath = journalPathFor(root, workspace);
160
+ if (!(0, node_fs.existsSync)(journalPath)) return null;
161
+ let journal;
162
+ try {
163
+ journal = JSON.parse((0, node_fs.readFileSync)(journalPath, "utf-8"));
164
+ } catch {
165
+ return null;
166
+ }
167
+ const entries = journal.entries;
168
+ if (!Array.isArray(entries)) return null;
169
+ const migrationsDir = (0, node_path.join)(root, workspace, "src", "db", "migrations");
170
+ const result = [];
171
+ for (const raw of entries) {
172
+ const entry = raw;
173
+ if (typeof entry.idx !== "number" || typeof entry.tag !== "string") continue;
174
+ const sqlPath = (0, node_path.join)(migrationsDir, `${entry.tag}.sql`);
175
+ let sha = null;
176
+ if ((0, node_fs.existsSync)(sqlPath)) try {
177
+ sha = require_snapshot_hash.computeSnapshotHash((0, node_fs.readFileSync)(sqlPath));
178
+ } catch {
179
+ sha = null;
180
+ }
181
+ result.push({
182
+ idx: entry.idx,
183
+ tag: entry.tag,
184
+ sha
185
+ });
186
+ }
187
+ return result.sort((a, b) => a.idx - b.idx);
188
+ }
189
+ function journalPathFor(root, workspace) {
190
+ return (0, node_path.join)(root, workspace, "src", "db", "migrations", "meta", "_journal.json");
191
+ }
192
+ function loadRawJournal(root, workspace) {
193
+ const journalPath = journalPathFor(root, workspace);
194
+ if (!(0, node_fs.existsSync)(journalPath)) return null;
195
+ let parsed;
196
+ try {
197
+ parsed = JSON.parse((0, node_fs.readFileSync)(journalPath, "utf-8"));
198
+ } catch {
199
+ return null;
200
+ }
201
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
202
+ const entries = parsed.entries;
203
+ if (!Array.isArray(entries)) return null;
204
+ return {
205
+ journal: parsed,
206
+ entries
207
+ };
208
+ }
209
+ function buildMergedJournal(localRoot, upstreamRoot, workspace, appended) {
210
+ const localRaw = loadRawJournal(localRoot, workspace);
211
+ const upstreamRaw = loadRawJournal(upstreamRoot, workspace);
212
+ if (!localRaw || !upstreamRaw) return null;
213
+ const upstreamEntryByTag = /* @__PURE__ */ new Map();
214
+ for (const entry of upstreamRaw.entries) if (typeof entry.tag === "string") upstreamEntryByTag.set(entry.tag, entry);
215
+ let appendedWhen = localRaw.entries.reduce((max, entry) => Math.max(max, typeof entry.when === "number" ? entry.when : 0), 0);
216
+ const appendedEntries = appended.map((append) => {
217
+ const raw = upstreamEntryByTag.get(append.fromTag);
218
+ const when = typeof raw?.when === "number" && raw.when > appendedWhen ? raw.when : ++appendedWhen;
219
+ return {
220
+ ...raw ?? {},
221
+ idx: append.idx,
222
+ tag: append.toTag,
223
+ when
224
+ };
225
+ });
226
+ const merged = {
227
+ ...localRaw.journal,
228
+ entries: [...localRaw.entries, ...appendedEntries]
229
+ };
230
+ return `${JSON.stringify(merged, null, 2)}\n`;
231
+ }
232
+ const MIGRATION_TAG_PATTERN = /^(\d+)_(.+)$/;
233
+ function migrationTagFor(idx, fromTag) {
234
+ const suffix = fromTag.match(MIGRATION_TAG_PATTERN)?.[2];
235
+ return suffix ? `${String(idx).padStart(4, "0")}_${suffix}` : fromTag;
236
+ }
237
+ function snapshotPrefixOf(tag) {
238
+ const match = tag.match(MIGRATION_TAG_PATTERN);
239
+ return match?.[1] ? match[1].padStart(4, "0") : null;
240
+ }
241
+ function reconcileMigrationChains(local, upstream) {
242
+ const localTags = new Set(local.map((entry) => entry.tag));
243
+ const unresolvable = [];
244
+ const appended = [];
245
+ const contentMatched = [];
246
+ let nextIdx = local.reduce((max, entry) => Math.max(max, entry.idx), -1) + 1;
247
+ for (const upstreamEntry of upstream) {
248
+ if (localTags.has(upstreamEntry.tag)) {
249
+ const localEntry = local.find((candidate) => candidate.tag === upstreamEntry.tag);
250
+ if (localEntry?.sha && upstreamEntry.sha && localEntry.sha !== upstreamEntry.sha) unresolvable.push({
251
+ kind: "tag-mismatch",
252
+ idx: upstreamEntry.idx,
253
+ localTag: localEntry.tag,
254
+ upstreamTag: upstreamEntry.tag,
255
+ message: `${upstreamEntry.tag} has different content locally than upstream — already-applied hashes would drift`
256
+ });
257
+ continue;
258
+ }
259
+ if (upstreamEntry.sha && local.some((candidate) => candidate.sha === upstreamEntry.sha)) {
260
+ contentMatched.push(upstreamEntry.tag);
261
+ continue;
262
+ }
263
+ appended.push({
264
+ idx: nextIdx,
265
+ fromTag: upstreamEntry.tag,
266
+ toTag: migrationTagFor(nextIdx, upstreamEntry.tag)
267
+ });
268
+ nextIdx++;
269
+ }
270
+ return {
271
+ kept: local.map((entry) => entry.tag),
272
+ appended,
273
+ contentMatched,
274
+ unresolvable
275
+ };
276
+ }
277
+ function hashFile(root, path) {
278
+ const fullPath = (0, node_path.join)(root, path);
279
+ if (!(0, node_fs.existsSync)(fullPath)) return null;
280
+ try {
281
+ return require_snapshot_hash.computeSnapshotHash((0, node_fs.readFileSync)(fullPath));
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+ function safeProjectPath(projectDir, path) {
287
+ const dest = (0, node_path.join)(projectDir, path);
288
+ const rel = (0, node_path.relative)(projectDir, dest);
289
+ if (rel.startsWith("..") || (0, node_path.isAbsolute)(rel)) throw new Error(`Refusing to write outside the project: ${path}`);
290
+ return dest;
291
+ }
292
+ function readJson(path) {
293
+ try {
294
+ const parsed = JSON.parse((0, node_fs.readFileSync)(path, "utf-8"));
295
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
296
+ return null;
297
+ } catch {
298
+ return null;
299
+ }
300
+ }
301
+ function readImportSnapshot(projectDir) {
302
+ const path = (0, node_path.join)(projectDir, ".bos", IMPORT_SNAPSHOT_FILE);
303
+ if (!(0, node_fs.existsSync)(path)) return { workspaces: {} };
304
+ try {
305
+ const workspaces = JSON.parse((0, node_fs.readFileSync)(path, "utf-8")).workspaces;
306
+ if (workspaces && typeof workspaces === "object" && !Array.isArray(workspaces)) return { workspaces };
307
+ return { workspaces: {} };
308
+ } catch {
309
+ return { workspaces: {} };
310
+ }
311
+ }
312
+ function writeImportSnapshot(projectDir, snapshot) {
313
+ const dir = (0, node_path.join)(projectDir, ".bos");
314
+ (0, node_fs.mkdirSync)(dir, { recursive: true });
315
+ (0, node_fs.writeFileSync)((0, node_path.join)(dir, IMPORT_SNAPSHOT_FILE), `${JSON.stringify(snapshot, null, 2)}\n`);
316
+ }
317
+ function writeImportReport(projectDir, content) {
318
+ const dir = (0, node_path.join)(projectDir, ".bos");
319
+ (0, node_fs.mkdirSync)(dir, { recursive: true });
320
+ (0, node_fs.writeFileSync)((0, node_path.join)(dir, IMPORT_REPORT_FILE), content);
321
+ return IMPORT_REPORT_PATH;
322
+ }
323
+ function backupImportFiles(projectDir, paths) {
324
+ const existing = paths.filter((path) => (0, node_fs.existsSync)((0, node_path.join)(projectDir, path)));
325
+ if (existing.length === 0) return;
326
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
327
+ const backupDir = (0, node_path.join)(projectDir, ".bos", "import-backup", stamp);
328
+ for (const path of existing) {
329
+ const dest = safeProjectPath(backupDir, path);
330
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
331
+ (0, node_fs.copyFileSync)((0, node_path.join)(projectDir, path), dest);
332
+ }
333
+ return backupDir;
334
+ }
335
+ function listSection(title, items, held) {
336
+ if (items.length === 0) return [
337
+ `## ${title}`,
338
+ "",
339
+ "(none)",
340
+ ""
341
+ ];
342
+ return [
343
+ `## ${title}`,
344
+ "",
345
+ ...items.map((item) => held?.has(item) ? `- \`${item}\` (held)` : `- \`${item}\``),
346
+ ""
347
+ ];
348
+ }
349
+ function buildImportReport(analysis, outcome) {
350
+ const lines = [];
351
+ lines.push(`# bos import report — \`${analysis.workspace}\``);
352
+ lines.push("");
353
+ if (analysis.chain) {
354
+ lines.push(`- **Source:** \`${analysis.chain.extendsRef}\` (extends chain)`);
355
+ if (analysis.chain.sourceArchive) lines.push(`- **Source archive:** \`${analysis.chain.sourceArchive.url}\``);
356
+ if (analysis.chain.repository) lines.push(`- **Upstream:** \`${analysis.chain.repository}\` @ \`${analysis.ref}\`${analysis.chain.pinnedSha ? ` (pinned git sha \`${analysis.chain.pinnedSha}\`)` : ""}`);
357
+ for (const warning of analysis.chain.warnings) lines.push(`- **Note:** ${warning}`);
358
+ } else {
359
+ const source = analysis.sourceKind === "local" ? `\`${analysis.from}\` (local directory)` : `\`${analysis.from}\`${analysis.ref ? ` @ \`${analysis.ref}\`` : ""}`;
360
+ lines.push(`- **Source:** ${source}`);
361
+ }
362
+ if (analysis.resolvedSha) lines.push(`- **Resolved commit:** \`${analysis.resolvedSha}\``);
363
+ const modeLabel = outcome.mode === "dry-run" ? "Dry run — no files written" : outcome.mode === "applied" ? "Applied — upstream written over shared files" : "Refused — unresolvable conflicts exist, nothing was written";
364
+ lines.push(`- **Mode:** ${modeLabel}`);
365
+ lines.push(`- **Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}`);
366
+ lines.push("");
367
+ lines.push("## Summary");
368
+ lines.push("");
369
+ lines.push("| bucket | count |");
370
+ lines.push("| --- | --- |");
371
+ lines.push(`| added (upstream-only) | ${analysis.added.length} |`);
372
+ lines.push(`| updated (local unchanged, upstream changed) | ${analysis.updated.length} |`);
373
+ lines.push(`| merged (structured package.json merge) | ${analysis.merged.length} |`);
374
+ lines.push(`| conflicts (unresolvable) | ${analysis.conflicts.length} |`);
375
+ lines.push(`| migration collisions (unresolvable) | ${analysis.migrationCollisions.length} |`);
376
+ lines.push(`| migrations appended (renumbered from upstream) | ${analysis.migrationReconciliation?.appended.length ?? 0} |`);
377
+ lines.push(`| migration chain held back | ${analysis.heldBackMigrations.length} |`);
378
+ lines.push(`| local-only (preserved) | ${analysis.preservedLocal.length} |`);
379
+ lines.push(`| local edits preserved (upstream unchanged) | ${analysis.preservedLocalEdit.length} |`);
380
+ lines.push(`| unchanged | ${analysis.unchanged.length} |`);
381
+ lines.push("");
382
+ if (analysis.conflicts.length > 0) {
383
+ lines.push("## Conflicts — manual-port checklist");
384
+ lines.push("");
385
+ lines.push("| file | reason |");
386
+ lines.push("| --- | --- |");
387
+ for (const conflict of analysis.conflicts) {
388
+ const reason = conflict.reason === "no-baseline" ? "no import baseline (first import or snapshot gap)" : "local and upstream both changed since the baseline";
389
+ lines.push(`| \`${conflict.path}\` | ${reason}${conflict.held ? " — held (migration chain)" : ""} |`);
390
+ }
391
+ lines.push("");
392
+ const appliedConflicts = analysis.conflicts.filter((conflict) => !conflict.held);
393
+ if (outcome.mode === "refused") lines.push("Nothing was written. Re-run with `--yes` to apply upstream over these files (local copies are backed up first), then port local-only content back onto the new upstream versions.");
394
+ else if (outcome.mode === "applied") {
395
+ if (appliedConflicts.length > 0) lines.push(`Upstream was applied over ${appliedConflicts.length} of these file(s)${outcome.backupDir ? ` — local copies backed up at \`${outcome.backupDir}\`` : ""}. Port local-only content back onto the new upstream versions.`);
396
+ if (analysis.heldBackMigrations.length > 0) lines.push("Migration-chain conflicts were held back and NOT written — see the held-back section below.");
397
+ } else lines.push("A real run with `--yes` would overwrite these with upstream (backing up local copies first); migration-chain files would be held back.");
398
+ lines.push("");
399
+ }
400
+ if (analysis.migrationReconciliation) {
401
+ const reconciliation = analysis.migrationReconciliation;
402
+ lines.push("## Migration chain reconciliation");
403
+ lines.push("");
404
+ lines.push("Applied locally — kept verbatim, never renumbered or rehashed:");
405
+ lines.push("");
406
+ if (reconciliation.kept.length === 0) {
407
+ lines.push("(no local journal entries)");
408
+ lines.push("");
409
+ } else {
410
+ for (const tag of reconciliation.kept) lines.push(`- \`${tag}\``);
411
+ lines.push("");
412
+ }
413
+ if (reconciliation.appended.length > 0) {
414
+ lines.push("Appended — colliding upstream migrations renumbered after the local tail:");
415
+ lines.push("");
416
+ lines.push("| upstream tag | new tag | new idx |");
417
+ lines.push("| --- | --- | --- |");
418
+ for (const append of reconciliation.appended) lines.push(`| \`${append.fromTag}\` | \`${append.toTag}\` | ${append.idx} |`);
419
+ lines.push("");
420
+ lines.push(`Next boot will apply the ${reconciliation.appended.length} appended migration(s) above; the kept set is already applied to the database.`);
421
+ lines.push("");
422
+ } else {
423
+ lines.push("Nothing to append: every upstream migration is already applied (by tag or by content).");
424
+ lines.push("");
425
+ }
426
+ if (reconciliation.contentMatched.length > 0) {
427
+ lines.push("Already imported under another tag (matched by SQL content hash):");
428
+ lines.push("");
429
+ for (const tag of reconciliation.contentMatched) lines.push(`- \`${tag}\``);
430
+ lines.push("");
431
+ }
432
+ if (reconciliation.appended.length > 0) {
433
+ lines.push("Snapshot-chain note: renamed snapshot files record upstream intermediate schema states; review `drizzle-kit generate` output against the merged chain before generating new migrations.");
434
+ lines.push("");
435
+ }
436
+ }
437
+ if (analysis.migrationCollisions.length > 0) {
438
+ lines.push("## Migration chain collisions — unresolvable");
439
+ lines.push("");
440
+ lines.push("| kind | idx | local | upstream | detail |");
441
+ lines.push("| --- | --- | --- | --- | --- |");
442
+ for (const collision of analysis.migrationCollisions) lines.push(`| ${collision.kind} | ${collision.idx} | \`${collision.localTag ?? "—"}\` | \`${collision.upstreamTag ?? "—"}\` | ${collision.message} |`);
443
+ lines.push("");
444
+ }
445
+ if (analysis.heldBackMigrations.length > 0) {
446
+ lines.push("## Migration chain held back");
447
+ lines.push("");
448
+ for (const path of analysis.heldBackMigrations) lines.push(`- \`${path}\``);
449
+ lines.push("");
450
+ lines.push("`src/db/migrations/**` was not modified: reconciliation could not produce a safe merged chain (content drift on an already-applied migration, or an unreadable journal). Resolve the collisions above, then re-import. The rest of the workspace imported normally.");
451
+ lines.push("");
452
+ }
453
+ if (analysis.routeDelta) {
454
+ lines.push("## Contract route-set delta");
455
+ lines.push("");
456
+ if (analysis.routeDelta.oursOnly.length === 0 && analysis.routeDelta.theirsOnly.length === 0) {
457
+ lines.push("Route sets are identical.");
458
+ lines.push("");
459
+ } else {
460
+ lines.push(`Ours-only: ${analysis.routeDelta.oursOnly.length}, theirs-only: ${analysis.routeDelta.theirsOnly.length}.`);
461
+ lines.push("");
462
+ for (const route of analysis.routeDelta.oursOnly) lines.push(`- ours only: \`${route}\``);
463
+ for (const route of analysis.routeDelta.theirsOnly) lines.push(`- theirs only: \`${route}\``);
464
+ lines.push("");
465
+ }
466
+ }
467
+ const heldSet = new Set(analysis.heldBackMigrations);
468
+ lines.push(...listSection("Added (upstream-only)", analysis.added, heldSet));
469
+ lines.push(...listSection("Updated (would be overwritten with upstream)", analysis.updated, heldSet));
470
+ lines.push(...listSection("Merged (package.json — local-only keys preserved)", analysis.merged, heldSet));
471
+ lines.push(...listSection("Local-only (preserved, never deleted)", analysis.preservedLocal));
472
+ lines.push(...listSection("Local edits preserved (upstream unchanged)", analysis.preservedLocalEdit));
473
+ lines.push(`## Unchanged`, "", `${analysis.unchanged.length} file(s) identical on both sides.`, "");
474
+ lines.push("## Next steps");
475
+ lines.push("");
476
+ lines.push("- `bos types gen` (and a build) regenerates `*.gen.ts` and `types/contract.d.ts` against the imported contract.");
477
+ lines.push("- If preserved local changes should live upstream, open a PR against the source repo rather than keeping them only locally.");
478
+ lines.push("");
479
+ return lines.join("\n");
480
+ }
481
+ const LOCAL_PREFIX = "local:";
482
+ function localWorkspaceSlotOf(config, workspace) {
483
+ if (!config || typeof config !== "object" || Array.isArray(config)) return null;
484
+ const node = config;
485
+ const matches = (ref) => typeof ref === "string" && ref.startsWith(LOCAL_PREFIX) && ref.slice(6) === workspace;
486
+ const app = node.app;
487
+ if (app && typeof app === "object") for (const key of [
488
+ "host",
489
+ "ui",
490
+ "api",
491
+ "auth"
492
+ ]) {
493
+ const entry = app[key];
494
+ if (entry && typeof entry === "object" && matches(entry.development)) return { slot: `app.${key}` };
495
+ }
496
+ const plugins = node.plugins;
497
+ if (plugins && typeof plugins === "object") for (const key of Object.keys(plugins)) {
498
+ const entry = plugins[key];
499
+ if (entry && typeof entry === "object" && !Array.isArray(entry) && matches(entry.development)) return { slot: `plugins.${key}` };
500
+ }
501
+ return null;
502
+ }
503
+ function configEntryAt(config, slot) {
504
+ if (!config || typeof config !== "object") return null;
505
+ const dot = slot.indexOf(".");
506
+ if (dot === -1) return null;
507
+ const container = config[slot.slice(0, dot)];
508
+ const key = slot.slice(dot + 1);
509
+ const entry = typeof container === "object" && container !== null && !Array.isArray(container) ? container[key] : void 0;
510
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
511
+ return entry;
512
+ }
513
+ const HEAD_FALLBACK_NOTE = "importing the default branch (HEAD)";
514
+ async function defaultVersionManifestFetch(url) {
515
+ const response = await require_http_client.fetchResponse(url, { headers: { accept: "application/json" } });
516
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
517
+ const bytes = new Uint8Array(await response.arrayBuffer());
518
+ return {
519
+ bytes,
520
+ manifest: JSON.parse(new TextDecoder().decode(bytes))
521
+ };
522
+ }
523
+ function manifestSourceOf(manifest) {
524
+ const parsed = every_plugin_version_manifest.WorkspaceVersionManifestSchema.pick({ source: true }).safeParse(manifest);
525
+ return parsed.success ? parsed.data.source ?? null : null;
526
+ }
527
+ async function resolvePinnedSource(entry, fetchManifest = defaultVersionManifestFetch) {
528
+ if (!entry) return { warning: `no config entry found for the pinned slot — ${HEAD_FALLBACK_NOTE}` };
529
+ const pin = entry.pin;
530
+ const manifest = typeof pin?.manifest === "string" ? pin.manifest : void 0;
531
+ const production = typeof entry.production === "string" ? entry.production : void 0;
532
+ if (!manifest || !production) return { warning: `no pinned version manifest on the published config — ${HEAD_FALLBACK_NOTE}` };
533
+ const url = `${production.replace(/\/+$/, "")}/${manifest}`;
534
+ let body;
535
+ try {
536
+ body = await fetchManifest(url);
537
+ } catch (error) {
538
+ return { warning: `could not read the version manifest (${error instanceof Error ? error.message : String(error)}) — ${HEAD_FALLBACK_NOTE}` };
539
+ }
540
+ const pinIntegrity = typeof pin?.integrity === "string" ? pin.integrity : void 0;
541
+ if (pinIntegrity) {
542
+ const computed = require_integrity.computeSriHash(Buffer.from(body.bytes));
543
+ if (computed !== pinIntegrity) throw new Error(`Version manifest integrity mismatch for ${url} — expected ${pinIntegrity}, computed ${computed}. Refusing to fall back to GitHub.`);
544
+ }
545
+ const source = manifestSourceOf(body.manifest);
546
+ if (typeof body.manifest.gitSha !== "string" || body.manifest.gitSha.length === 0) return {
547
+ warning: `the version manifest records no git sha (it pins content hashes only) — ${HEAD_FALLBACK_NOTE}; future deploys pin the exact commit`,
548
+ ...source ? { source } : {}
549
+ };
550
+ return {
551
+ sha: body.manifest.gitSha,
552
+ ...source ? { source } : {}
553
+ };
554
+ }
555
+ const FROM_REMEDIES = "Pass the upstream explicitly: --from bos://<account>/<gateway> or --from <owner>/<repo>";
556
+ async function resolveImportChain(input) {
557
+ const fromBosSpec = input.entrySpec !== void 0;
558
+ const entry = fromBosSpec ? `bos://${input.entrySpec.account}/${input.entrySpec.gateway}` : require_config.localConfigEntryPath(input.projectDir) ?? null;
559
+ if (!entry) throw new Error(`No authored config (bos.app.ts) found — ${FROM_REMEDIES}`);
560
+ const links = [];
561
+ await require_resolution_session.walkExtendsChain(entry, {
562
+ env: "production",
563
+ io: input.io,
564
+ visit: async (link) => {
565
+ links.push(link);
566
+ }
567
+ });
568
+ links.reverse();
569
+ if (links.length === 0) throw new Error(`Empty extends chain from ${entry}`);
570
+ const declaredIndex = links.findIndex((link) => localWorkspaceSlotOf(link.config, input.workspace) !== null);
571
+ if (declaredIndex === -1) {
572
+ const chainRefs = links.map((link) => link.ref).join(" -> ");
573
+ throw new Error(`No runtime in the extends chain declares workspace "${input.workspace}" (${chainRefs}). ${FROM_REMEDIES}.`);
574
+ }
575
+ if (!fromBosSpec && declaredIndex === 0) throw new Error(`Workspace "${input.workspace}" is declared locally in your authored config — bos import targets an upstream ancestor`);
576
+ const ancestor = links[declaredIndex];
577
+ const ancestorRef = ancestor.ref.startsWith("bos://") ? ancestor.ref : `authored config at ${ancestor.ref}`;
578
+ let repository;
579
+ for (let i = declaredIndex; i < links.length && repository === void 0; i++) {
580
+ const value = links[i].config?.repository;
581
+ if (typeof value === "string" && value.trim().length > 0) repository = value;
582
+ }
583
+ const warnings = [];
584
+ const slot = localWorkspaceSlotOf(ancestor.config, input.workspace).slot;
585
+ const ancestorEntry = configEntryAt(ancestor.config, slot);
586
+ const pin = await resolvePinnedSource(ancestorEntry, input.fetchVersionManifest);
587
+ if (pin.warning) warnings.push(pin.warning);
588
+ const production = typeof ancestorEntry?.production === "string" ? ancestorEntry.production : void 0;
589
+ const sourceArchive = pin.source && production ? {
590
+ file: pin.source.file,
591
+ url: `${production.replace(/\/+$/, "")}/${pin.source.file}`,
592
+ integrity: pin.source.integrity
593
+ } : void 0;
594
+ if (!sourceArchive && !repository) throw new Error(`No repository found along the extends chain above ${ancestorRef} — the declaring runtime must publish its source (repository field in bos.app.ts)`);
595
+ const extendsRef = fromBosSpec ? entry : (() => {
596
+ const extendsValue = links[0].config?.extends;
597
+ if (extendsValue === void 0) return "authored config";
598
+ try {
599
+ return require_merge.resolveExtendsRef(extendsValue, "production") ?? "authored config";
600
+ } catch {
601
+ return "authored config";
602
+ }
603
+ })();
604
+ if (sourceArchive) return {
605
+ spec: {
606
+ kind: "archive",
607
+ url: sourceArchive.url,
608
+ integrity: sourceArchive.integrity
609
+ },
610
+ provenance: {
611
+ extendsRef,
612
+ ancestorRef,
613
+ ...repository ? { repository } : {},
614
+ ...pin.sha ? { pinnedSha: pin.sha } : {},
615
+ sourceArchive,
616
+ warnings
617
+ }
618
+ };
619
+ return {
620
+ spec: {
621
+ kind: "github",
622
+ repo: repository,
623
+ ...pin.sha ? { ref: pin.sha } : {}
624
+ },
625
+ provenance: {
626
+ extendsRef,
627
+ ancestorRef,
628
+ ...pin.sha ? { pinnedSha: pin.sha } : {},
629
+ repository,
630
+ warnings
631
+ }
632
+ };
633
+ }
634
+ async function resolveImportPlan(input) {
635
+ const from = input.from;
636
+ if (!from) {
637
+ const resolved = await resolveImportChain({
638
+ workspace: input.workspace,
639
+ projectDir: input.projectDir
640
+ });
641
+ return {
642
+ fromLabel: resolved.provenance.extendsRef,
643
+ spec: resolved.spec,
644
+ chain: resolved.provenance
645
+ };
646
+ }
647
+ const spec = parseFromSpec(from);
648
+ if (spec.kind === "bos") {
649
+ const resolved = await resolveImportChain({
650
+ workspace: input.workspace,
651
+ projectDir: input.projectDir,
652
+ entrySpec: spec
653
+ });
654
+ return {
655
+ fromLabel: spec.bosUrl,
656
+ spec: resolved.spec,
657
+ chain: resolved.provenance
658
+ };
659
+ }
660
+ return {
661
+ fromLabel: from,
662
+ spec
663
+ };
664
+ }
665
+ async function defaultArchiveFetch(url) {
666
+ const response = await require_http_client.fetchResponse(url, {
667
+ redirect: "follow",
668
+ timeout: "60 seconds"
669
+ });
670
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
671
+ return new Uint8Array(await response.arrayBuffer());
672
+ }
673
+ async function extractArchive(bytes) {
674
+ const tmpDir = (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "bos-import-archive-"));
675
+ const archivePath = (0, node_path.join)(tmpDir, "source.tar.gz");
676
+ const extractDir = (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "bos-import-archive-src-"));
677
+ try {
678
+ (0, node_fs.writeFileSync)(archivePath, bytes);
679
+ await (0, tar.extract)({
680
+ cwd: extractDir,
681
+ file: archivePath,
682
+ strip: 0
683
+ });
684
+ } finally {
685
+ (0, node_fs.rmSync)(tmpDir, {
686
+ recursive: true,
687
+ force: true
688
+ });
689
+ }
690
+ return {
691
+ root: extractDir,
692
+ cleanup: async () => {
693
+ (0, node_fs.rmSync)(extractDir, {
694
+ recursive: true,
695
+ force: true
696
+ });
697
+ }
698
+ };
699
+ }
700
+ async function fetchGithubSource(input) {
701
+ const pinned = input.pinnedSha !== void 0 && input.ref === input.pinnedSha;
702
+ try {
703
+ const tarball = await input.download(input.repo, input.ref);
704
+ return { source: {
705
+ root: tarball.dir,
706
+ cleanup: tarball.cleanup,
707
+ ref: input.ref ?? tarball.resolvedRef ?? "main",
708
+ resolvedSha: tarball.resolvedSha
709
+ } };
710
+ } catch (error) {
711
+ if (!pinned) throw error;
712
+ const message = error instanceof Error ? error.message : String(error);
713
+ const tarball = await input.download(input.repo, void 0);
714
+ return {
715
+ source: {
716
+ root: tarball.dir,
717
+ cleanup: tarball.cleanup,
718
+ ref: tarball.resolvedRef ?? "main",
719
+ resolvedSha: tarball.resolvedSha
720
+ },
721
+ fallbackWarning: `could not fetch the pinned commit ${input.pinnedSha} (${message}) — importing the default branch`
722
+ };
723
+ }
724
+ }
725
+ async function fetchSourceWithPinFallback(input) {
726
+ if (input.spec.kind === "local") return { source: {
727
+ root: input.spec.dir,
728
+ cleanup: async () => {},
729
+ ref: "local"
730
+ } };
731
+ const download = input.download ?? require_cli_init.downloadTarball;
732
+ if (input.spec.kind === "archive") {
733
+ const fetchArchive = input.fetchArchive ?? defaultArchiveFetch;
734
+ try {
735
+ const bytes = await fetchArchive(input.spec.url);
736
+ const computed = require_integrity.computeSriHash(Buffer.from(bytes));
737
+ if (computed !== input.spec.integrity) throw new Error(`integrity mismatch for ${input.spec.url} — expected ${input.spec.integrity}, computed ${computed}`);
738
+ return { source: {
739
+ ...await extractArchive(bytes),
740
+ ref: "source archive"
741
+ } };
742
+ } catch (error) {
743
+ if (!input.fallback) throw error;
744
+ const message = error instanceof Error ? error.message : String(error);
745
+ return {
746
+ ...await fetchGithubSource({
747
+ repo: input.fallback.repo,
748
+ ref: input.fallback.ref,
749
+ pinnedSha: input.pinnedSha,
750
+ download
751
+ }),
752
+ fallbackWarning: `could not fetch the source archive (${message}) — importing from ${input.fallback.repo} instead`
753
+ };
754
+ }
755
+ }
756
+ return fetchGithubSource({
757
+ repo: input.spec.repo,
758
+ ref: input.spec.ref,
759
+ pinnedSha: input.pinnedSha,
760
+ download
761
+ });
762
+ }
763
+ async function resolveImportSource(spec, chain) {
764
+ const { source, fallbackWarning } = await fetchSourceWithPinFallback({
765
+ spec,
766
+ pinnedSha: chain?.pinnedSha,
767
+ fallback: chain?.repository ? {
768
+ repo: chain.repository,
769
+ ...chain.pinnedSha ? { ref: chain.pinnedSha } : {}
770
+ } : void 0
771
+ });
772
+ return {
773
+ ...source,
774
+ fallbackWarning
775
+ };
776
+ }
777
+ async function importWorkspace(projectDir, options, deps) {
778
+ const errorResult = (workspace, ref, message) => ({
779
+ status: "error",
780
+ workspace,
781
+ from: options.from ?? "",
782
+ ref,
783
+ added: [],
784
+ updated: [],
785
+ unchanged: [],
786
+ preservedLocal: [],
787
+ merged: [],
788
+ conflicts: [],
789
+ heldBackMigrations: [],
790
+ migrationCollisions: [],
791
+ warnings: [],
792
+ error: message
793
+ });
794
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
795
+ let workspace = options.workspace;
796
+ let fromLabel = options.from ?? "";
797
+ let ref = "";
798
+ let chain;
799
+ try {
800
+ workspace = normalizeWorkspacePath(options.workspace);
801
+ const plan = await resolveImportPlan({
802
+ workspace,
803
+ from: options.from,
804
+ projectDir
805
+ });
806
+ fromLabel = plan.fromLabel;
807
+ chain = plan.chain;
808
+ const spec = plan.spec;
809
+ const source = await resolveImportSource(spec, chain);
810
+ if (source.fallbackWarning && chain) {
811
+ const { sourceArchive: _dropped, ...rest } = chain;
812
+ chain = {
813
+ ...rest,
814
+ pinnedSha: void 0,
815
+ warnings: [...chain.warnings, source.fallbackWarning]
816
+ };
817
+ }
818
+ ref = source.ref;
819
+ try {
820
+ const upstreamWorkspaceDir = (0, node_path.join)(source.root, workspace);
821
+ if (!(0, node_fs.existsSync)(upstreamWorkspaceDir) || !(0, node_fs.lstatSync)(upstreamWorkspaceDir).isDirectory()) throw new Error(`Workspace "${workspace}" not found in ${fromLabel}${source.ref !== "local" ? ` at ref ${source.ref}` : ""}`);
822
+ const upstreamFiles = await require_workspace_subtree.listWorkspaceSubtreeFiles(source.root, workspace);
823
+ const localWorkspaceDir = (0, node_path.join)(projectDir, workspace);
824
+ const localFiles = (0, node_fs.existsSync)(localWorkspaceDir) ? await require_workspace_subtree.listWorkspaceSubtreeFiles(projectDir, workspace) : [];
825
+ const upstreamSet = new Set(upstreamFiles);
826
+ const localSet = new Set(localFiles);
827
+ const importSnapshot = readImportSnapshot(projectDir);
828
+ const workspaceSnapshot = importSnapshot.workspaces[workspace];
829
+ const syncSnapshot = await require_snapshot.readSnapshot(projectDir);
830
+ const baselineFor = (path) => workspaceSnapshot?.files[path] ?? syncSnapshot?.files[path];
831
+ const packageJsonPath = `${workspace}/package.json`;
832
+ const mergeablePackageJson = (() => {
833
+ if (!localSet.has(packageJsonPath) || !upstreamSet.has(packageJsonPath)) return false;
834
+ return readJson((0, node_path.join)(projectDir, packageJsonPath)) !== null && readJson((0, node_path.join)(source.root, packageJsonPath)) !== null;
835
+ })();
836
+ const chainPrefix = `${workspace}/src/db/migrations/`;
837
+ const chainJournalPath = `${chainPrefix}meta/_journal.json`;
838
+ const localMigrations = loadMigrationEntries(projectDir, workspace);
839
+ const upstreamMigrations = loadMigrationEntries(source.root, workspace);
840
+ const localJournalExists = (0, node_fs.existsSync)((0, node_path.join)(projectDir, chainJournalPath));
841
+ const reconciliation = localMigrations && upstreamMigrations ? reconcileMigrationChains(localMigrations, upstreamMigrations) : null;
842
+ let chainUnsafe;
843
+ if (reconciliation) chainUnsafe = reconciliation.unresolvable.length > 0;
844
+ else if (localMigrations === null && localJournalExists) chainUnsafe = true;
845
+ else if (localMigrations !== null && upstreamMigrations === null) chainUnsafe = true;
846
+ else chainUnsafe = false;
847
+ const migrationRemap = /* @__PURE__ */ new Map();
848
+ const migrationSkipped = /* @__PURE__ */ new Set();
849
+ if (reconciliation && !chainUnsafe) {
850
+ const appendedByFromTag = new Map(reconciliation.appended.map((append) => [append.fromTag, append]));
851
+ const contentMatchedTags = new Set(reconciliation.contentMatched);
852
+ const upstreamTagBySnapshotPrefix = /* @__PURE__ */ new Map();
853
+ for (const entry of upstreamMigrations ?? []) {
854
+ const prefix = snapshotPrefixOf(entry.tag);
855
+ if (prefix) upstreamTagBySnapshotPrefix.set(prefix, entry.tag);
856
+ }
857
+ for (const path of upstreamFiles) {
858
+ if (!path.startsWith(chainPrefix) || path === chainJournalPath) continue;
859
+ const relInMigrations = path.slice(chainPrefix.length);
860
+ if (relInMigrations.endsWith(".sql")) {
861
+ const tag = relInMigrations.slice(0, -4);
862
+ const append = appendedByFromTag.get(tag);
863
+ if (append) migrationRemap.set(path, `${chainPrefix}${append.toTag}.sql`);
864
+ else if (contentMatchedTags.has(tag)) migrationSkipped.add(path);
865
+ } else if (relInMigrations.startsWith("meta/") && relInMigrations.endsWith("_snapshot.json")) {
866
+ const prefix = relInMigrations.slice(5, relInMigrations.length - 14);
867
+ const tag = upstreamTagBySnapshotPrefix.get(prefix);
868
+ const append = tag ? appendedByFromTag.get(tag) : void 0;
869
+ if (append) migrationRemap.set(path, `${chainPrefix}meta/${String(append.idx).padStart(4, "0")}_snapshot.json`);
870
+ else if (tag && contentMatchedTags.has(tag)) migrationSkipped.add(path);
871
+ }
872
+ }
873
+ }
874
+ const added = [];
875
+ const updated = [];
876
+ const unchanged = [];
877
+ const preservedLocal = [];
878
+ const preservedLocalEdit = [];
879
+ const merged = [];
880
+ const conflicts = [];
881
+ const upstreamHashByPath = /* @__PURE__ */ new Map();
882
+ const allPaths = [.../* @__PURE__ */ new Set([...localFiles, ...upstreamFiles])].sort();
883
+ for (const path of allPaths) {
884
+ if (!upstreamSet.has(path)) {
885
+ preservedLocal.push(path);
886
+ continue;
887
+ }
888
+ if (reconciliation && path === chainJournalPath) continue;
889
+ if (migrationRemap.has(path) || migrationSkipped.has(path)) continue;
890
+ const localHash = localSet.has(path) ? hashFile(projectDir, path) : null;
891
+ const upstreamHash = hashFile(source.root, path);
892
+ if (upstreamHash === null) throw new Error(`Failed to read upstream file: ${path}`);
893
+ upstreamHashByPath.set(path, upstreamHash);
894
+ const classification = classifyImportFile({
895
+ localHash,
896
+ upstreamHash,
897
+ baselineHash: baselineFor(path),
898
+ mergeable: path === packageJsonPath && mergeablePackageJson
899
+ });
900
+ switch (classification.kind) {
901
+ case "added":
902
+ added.push(path);
903
+ break;
904
+ case "unchanged":
905
+ unchanged.push(path);
906
+ break;
907
+ case "updated":
908
+ updated.push(path);
909
+ break;
910
+ case "preserved-local":
911
+ preservedLocal.push(path);
912
+ break;
913
+ case "preserved-local-edit":
914
+ preservedLocalEdit.push(path);
915
+ break;
916
+ case "merged":
917
+ merged.push(path);
918
+ break;
919
+ case "conflict": conflicts.push({
920
+ path,
921
+ reason: classification.reason,
922
+ held: false
923
+ });
924
+ }
925
+ }
926
+ let routeDelta;
927
+ const localRoutes = extractWorkspaceRoutes(await collectContractRouteSources(projectDir, workspace));
928
+ const upstreamRoutes = extractWorkspaceRoutes(await collectContractRouteSources(source.root, workspace));
929
+ if (localRoutes !== null || upstreamRoutes !== null) routeDelta = computeRouteDelta(localRoutes ?? [], upstreamRoutes ?? []);
930
+ const migrationCollisions = reconciliation?.unresolvable ?? [];
931
+ const writeCandidates = /* @__PURE__ */ new Set([
932
+ ...added,
933
+ ...updated,
934
+ ...merged,
935
+ ...conflicts.map((conflict) => conflict.path)
936
+ ]);
937
+ const heldSet = new Set(chainUnsafe ? [...writeCandidates].filter((path) => path.startsWith(chainPrefix)) : []);
938
+ if (chainUnsafe && reconciliation) heldSet.add(chainJournalPath);
939
+ const heldBackMigrations = [...heldSet].sort();
940
+ for (const conflict of conflicts) conflict.held = heldSet.has(conflict.path);
941
+ const analysis = {
942
+ workspace,
943
+ from: fromLabel,
944
+ ref: source.ref,
945
+ resolvedSha: source.resolvedSha,
946
+ sourceKind: spec.kind,
947
+ chain,
948
+ added,
949
+ updated,
950
+ unchanged,
951
+ preservedLocal,
952
+ preservedLocalEdit,
953
+ merged,
954
+ conflicts,
955
+ routeDelta,
956
+ migrationCollisions,
957
+ migrationReconciliation: reconciliation,
958
+ heldBackMigrations
959
+ };
960
+ const resultBase = {
961
+ workspace,
962
+ from: fromLabel,
963
+ ref: source.ref,
964
+ resolvedSha: source.resolvedSha,
965
+ sourceRepository: chain?.repository,
966
+ warnings: chain ? chain.warnings : [],
967
+ added,
968
+ updated,
969
+ unchanged,
970
+ preservedLocal,
971
+ merged,
972
+ conflicts: conflicts.map((conflict) => conflict.path),
973
+ heldBackMigrations,
974
+ routeDelta,
975
+ migrationCollisions,
976
+ migrationReconciliation: reconciliation ?? void 0
977
+ };
978
+ if (options.dryRun) {
979
+ const reportPath = writeImportReport(projectDir, buildImportReport(analysis, { mode: "dry-run" }));
980
+ return {
981
+ status: "dry-run",
982
+ ...resultBase,
983
+ reportPath
984
+ };
985
+ }
986
+ const appendedCount = reconciliation?.appended.length ?? 0;
987
+ if ((conflicts.length > 0 || heldBackMigrations.length > 0 || appendedCount > 0) && !options.yes) {
988
+ let approved = false;
989
+ if (deps?.confirm) approved = await deps.confirm({
990
+ workspace,
991
+ conflictCount: conflicts.length,
992
+ migrationCollisionCount: migrationCollisions.length,
993
+ heldCount: heldBackMigrations.length,
994
+ appendedCount
995
+ });
996
+ if (!approved) {
997
+ const reportPath = writeImportReport(projectDir, buildImportReport(analysis, { mode: "refused" }));
998
+ return {
999
+ status: "conflicts",
1000
+ ...resultBase,
1001
+ reportPath
1002
+ };
1003
+ }
1004
+ }
1005
+ const isHeld = (path) => heldSet.has(path);
1006
+ const mergeContents = /* @__PURE__ */ new Map();
1007
+ for (const path of merged) {
1008
+ const local = readJson((0, node_path.join)(projectDir, path));
1009
+ const upstream = readJson((0, node_path.join)(source.root, path));
1010
+ if (!local || !upstream) throw new Error(`Failed to merge ${path}: unreadable package.json`);
1011
+ mergeContents.set(path, `${JSON.stringify(require_sync.mergePackageJson(path, local, upstream), null, 2)}\n`);
1012
+ }
1013
+ const reconciliationActive = reconciliation !== null && !chainUnsafe;
1014
+ const journalRebuildContent = reconciliationActive && reconciliation.appended.length > 0 ? buildMergedJournal(projectDir, source.root, workspace, reconciliation.appended) : null;
1015
+ const journalChanged = journalRebuildContent !== null && journalRebuildContent !== (0, node_fs.readFileSync)((0, node_path.join)(projectDir, chainJournalPath), "utf-8");
1016
+ const reconciliationOverwrites = [];
1017
+ if (reconciliationActive) for (const [upstreamPath, localPath] of migrationRemap) {
1018
+ const upstreamContent = (0, node_fs.readFileSync)((0, node_path.join)(source.root, upstreamPath), "utf-8");
1019
+ if (!(0, node_fs.existsSync)((0, node_path.join)(projectDir, localPath)) || (0, node_fs.readFileSync)((0, node_path.join)(projectDir, localPath), "utf-8") !== upstreamContent) reconciliationOverwrites.push({
1020
+ upstreamPath,
1021
+ localPath
1022
+ });
1023
+ }
1024
+ const backupDir = backupImportFiles(projectDir, [
1025
+ ...updated.filter((path) => !isHeld(path)),
1026
+ ...conflicts.map((conflict) => conflict.path).filter((path) => !isHeld(path)),
1027
+ ...merged.filter((path) => mergeContents.get(path) !== (0, node_fs.readFileSync)((0, node_path.join)(projectDir, path), "utf-8")),
1028
+ ...journalChanged ? [chainJournalPath] : [],
1029
+ ...reconciliationOverwrites.map((write) => write.localPath)
1030
+ ]);
1031
+ const writeUpstream = (path) => {
1032
+ const dest = safeProjectPath(projectDir, path);
1033
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
1034
+ (0, node_fs.copyFileSync)((0, node_path.join)(source.root, path), dest);
1035
+ };
1036
+ for (const path of added) if (!isHeld(path)) writeUpstream(path);
1037
+ for (const path of updated) if (!isHeld(path)) writeUpstream(path);
1038
+ for (const conflict of conflicts) if (!isHeld(conflict.path)) writeUpstream(conflict.path);
1039
+ for (const path of merged) {
1040
+ const content = mergeContents.get(path);
1041
+ if (content === void 0 || isHeld(path)) continue;
1042
+ if (content === (0, node_fs.readFileSync)((0, node_path.join)(projectDir, path), "utf-8")) continue;
1043
+ const dest = safeProjectPath(projectDir, path);
1044
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
1045
+ (0, node_fs.writeFileSync)(dest, content);
1046
+ }
1047
+ for (const { upstreamPath, localPath } of reconciliationOverwrites) {
1048
+ const dest = safeProjectPath(projectDir, localPath);
1049
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
1050
+ (0, node_fs.copyFileSync)((0, node_path.join)(source.root, upstreamPath), dest);
1051
+ }
1052
+ if (journalChanged && journalRebuildContent) {
1053
+ const dest = safeProjectPath(projectDir, chainJournalPath);
1054
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
1055
+ (0, node_fs.writeFileSync)(dest, journalRebuildContent);
1056
+ }
1057
+ const writtenPaths = [
1058
+ ...added,
1059
+ ...updated,
1060
+ ...merged,
1061
+ ...conflicts.map((conflict) => conflict.path)
1062
+ ].filter((path) => !isHeld(path));
1063
+ const recorded = {};
1064
+ for (const path of writtenPaths) {
1065
+ const hash = hashFile(projectDir, path);
1066
+ if (hash) recorded[path] = hash;
1067
+ }
1068
+ if (journalChanged && journalRebuildContent) recorded[chainJournalPath] = require_snapshot_hash.computeSnapshotHash(journalRebuildContent);
1069
+ for (const { localPath } of reconciliationOverwrites) {
1070
+ const hash = hashFile(projectDir, localPath);
1071
+ if (hash) recorded[localPath] = hash;
1072
+ }
1073
+ for (const path of [...unchanged, ...preservedLocalEdit]) {
1074
+ const hash = upstreamHashByPath.get(path);
1075
+ if (hash) recorded[path] = hash;
1076
+ }
1077
+ writeImportSnapshot(projectDir, { workspaces: {
1078
+ ...importSnapshot.workspaces,
1079
+ [workspace]: {
1080
+ from: fromLabel,
1081
+ ref: source.ref,
1082
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1083
+ files: recorded
1084
+ }
1085
+ } });
1086
+ if (syncSnapshot) await require_snapshot.writeSnapshot(projectDir, {
1087
+ parentRef: syncSnapshot.parentRef,
1088
+ files: {
1089
+ ...syncSnapshot.files,
1090
+ ...recorded
1091
+ },
1092
+ starter: syncSnapshot.starter
1093
+ });
1094
+ const reportPath = writeImportReport(projectDir, buildImportReport(analysis, {
1095
+ mode: "applied",
1096
+ backupDir
1097
+ }));
1098
+ if (!options.noInstall) try {
1099
+ await require_cli_init.runPnpmInstall(require_workspace.findWorkspaceRoot(projectDir)?.dir ?? projectDir);
1100
+ await require_cli_init.runTypesGen(projectDir);
1101
+ } catch (error) {
1102
+ return {
1103
+ status: "error",
1104
+ ...resultBase,
1105
+ backupDir,
1106
+ reportPath,
1107
+ error: `Import applied, but the post-install step failed: ${errorMessage(error)}`
1108
+ };
1109
+ }
1110
+ return {
1111
+ status: "imported",
1112
+ ...resultBase,
1113
+ backupDir,
1114
+ reportPath
1115
+ };
1116
+ } finally {
1117
+ await source.cleanup();
1118
+ }
1119
+ } catch (error) {
1120
+ return errorResult(workspace, ref, errorMessage(error));
1121
+ }
1122
+ }
1123
+
1124
+ //#endregion
1125
+ exports.IMPORT_REPORT_PATH = IMPORT_REPORT_PATH;
1126
+ exports.buildImportReport = buildImportReport;
1127
+ exports.classifyImportFile = classifyImportFile;
1128
+ exports.computeRouteDelta = computeRouteDelta;
1129
+ exports.configEntryAt = configEntryAt;
1130
+ exports.extractContractRoutes = extractContractRoutes;
1131
+ exports.fetchSourceWithPinFallback = fetchSourceWithPinFallback;
1132
+ exports.importWorkspace = importWorkspace;
1133
+ exports.loadMigrationEntries = loadMigrationEntries;
1134
+ exports.localWorkspaceSlotOf = localWorkspaceSlotOf;
1135
+ exports.normalizeWorkspacePath = normalizeWorkspacePath;
1136
+ exports.parseFromSpec = parseFromSpec;
1137
+ exports.reconcileMigrationChains = reconcileMigrationChains;
1138
+ exports.resolveImportChain = resolveImportChain;
1139
+ exports.resolvePinnedSource = resolvePinnedSource;
1140
+ //# sourceMappingURL=import.cjs.map