@akanjs/cli 2.4.1-rc.6 → 2.4.1

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 (44) hide show
  1. package/.build-stamp +1 -1
  2. package/{agent.command-t4346579.js → agent.command-sa12ccz3.js} +2 -2
  3. package/{application.command-zkmeemdt.js → application.command-jv63vv7z.js} +4 -4
  4. package/{applicationBuildRunner-5w8j9yp7.js → applicationBuildRunner-vzjdgv1g.js} +3 -2
  5. package/buildBatch.proc.js +3 -2
  6. package/{cloud.command-5ztqxppm.js → cloud.command-8z2nc6b2.js} +7 -7
  7. package/{context.command-smwxwgwf.js → context.command-0m854xme.js} +8 -8
  8. package/{guideline.command-1r0esdjs.js → guideline.command-dsdpae45.js} +1 -1
  9. package/incrementalBuilder.proc.js +3 -3
  10. package/{index-3sd2vape.js → index-1b9f27pm.js} +1 -1
  11. package/{index-fh990y67.js → index-3a4vntr0.js} +1 -1
  12. package/{index-jbtn8h1y.js → index-81epkybm.js} +1 -1
  13. package/{index-0fn1r7gg.js → index-bjpxzr6s.js} +31 -4
  14. package/{index-ahpcr9ss.js → index-d2e2y85b.js} +1 -1
  15. package/{index-0jwvs8vp.js → index-e3b8ms64.js} +6 -6
  16. package/{index-s9yajzkj.js → index-eja0sbm6.js} +4 -4
  17. package/{index-wh201a76.js → index-h6pevatb.js} +2 -2
  18. package/{index-2v6a4wd5.js → index-m04as81s.js} +2 -2
  19. package/index-m0vg9n8z.js +2219 -0
  20. package/{index-n5y2gbf2.js → index-n2mp0f48.js} +200 -19
  21. package/{index-nv7et4t3.js → index-p9f1rpbp.js} +1 -1
  22. package/{index-qpz4csbs.js → index-phj3ewxv.js} +7 -1
  23. package/{index-pya1h7wx.js → index-q4sc46ec.js} +1 -1
  24. package/{index-y4kg682s.js → index-qacv44mm.js} +1 -1
  25. package/index-rh16j3c7.js +2166 -0
  26. package/{index-gr4cjv99.js → index-zw4aqvwh.js} +2 -2
  27. package/index.js +17 -17
  28. package/{library.command-jrew04m6.js → library.command-fhx5ce8n.js} +2 -2
  29. package/{localRegistry.command-m08dkm1b.js → localRegistry.command-18f61945.js} +6 -6
  30. package/{module.command-p98t7522.js → module.command-29wvnb56.js} +3 -3
  31. package/{package.command-57dyrfmn.js → package.command-46r1dncm.js} +2 -2
  32. package/package.json +2 -2
  33. package/{page.command-tas6f3na.js → page.command-vxvqdzc0.js} +2 -2
  34. package/{primitive.command-bjrbsakw.js → primitive.command-yk622wmt.js} +4 -4
  35. package/{quality.command-zavaafbw.js → quality.command-p0mb5xwt.js} +1 -1
  36. package/{repair.command-je57wx67.js → repair.command-nptckp6w.js} +2 -2
  37. package/{scalar.command-gqqyy1kr.js → scalar.command-bss14hez.js} +2 -2
  38. package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +19 -0
  39. package/templates/workspaceRoot/package.json.template +3 -1
  40. package/templates/workspaceRoot/patches/zod@3.25.76.patch.template +22 -0
  41. package/templates/workspaceRoot/patches/zod@4.4.3.patch.template +11 -0
  42. package/{workflow.command-d6mrc47s.js → workflow.command-7fhqb33d.js} +6 -6
  43. package/{workspace.command-bbg96z8k.js → workspace.command-xkcec7ky.js} +15 -15
  44. package/index-khzzttv1.js +0 -4274
@@ -0,0 +1,2166 @@
1
+ // @bun
2
+ // pkgs/@akanjs/devkit/frontendBuild/clientEntryDiscovery.ts
3
+ import path3 from "path";
4
+
5
+ // pkgs/@akanjs/devkit/transforms/barrelAnalyzer.ts
6
+ import path from "path";
7
+ import { Logger } from "akanjs/common";
8
+ var REEXPORT_RE = /(?:^|\n)\s*export\s+(?:type\s+)?(?:(\*)(?:\s+as\s+(\w+))?|\{\s*([^}]*?)\s*\})\s+from\s+(["'])([^"']+)\4;?/g;
9
+ var LOCAL_NAMED_RE = /(?:^|\n)\s*export\s+\{\s*([^}]*?)\s*\}(?!\s*from)/g;
10
+ var CANDIDATE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
11
+
12
+ class BarrelAnalyzer {
13
+ #logger = new Logger("BarrelAnalyzer");
14
+ #opts;
15
+ #cache = new Map;
16
+ #tsTranspiler = new Bun.Transpiler({ loader: "ts" });
17
+ #tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
18
+ constructor(opts) {
19
+ this.#opts = opts;
20
+ }
21
+ analyze(pkgName) {
22
+ const cached = this.#cache.get(pkgName);
23
+ if (cached)
24
+ return cached;
25
+ const promise = this.#analyzeSafe(pkgName);
26
+ this.#cache.set(pkgName, promise);
27
+ return promise;
28
+ }
29
+ async#analyzeSafe(pkgName) {
30
+ try {
31
+ return await this.#analyzeUncached(pkgName);
32
+ } catch (err) {
33
+ this.#logger.error(`analyze failed for ${pkgName}: ${err.message}`);
34
+ return null;
35
+ }
36
+ }
37
+ async#analyzeUncached(pkgName) {
38
+ const pkg = await this.#opts.resolvePackage(pkgName);
39
+ if (!pkg)
40
+ return null;
41
+ const map = new Map;
42
+ const visited = new Set;
43
+ await this.#walk(pkg.entryFile, pkg, map, visited);
44
+ return map;
45
+ }
46
+ async#walk(absFile, pkg, map, visited) {
47
+ if (visited.has(absFile))
48
+ return;
49
+ visited.add(absFile);
50
+ const source = await readIfExists(absFile);
51
+ if (source === null)
52
+ return;
53
+ const currentSubpath = this.#subpathFor(pkg, absFile);
54
+ if (!currentSubpath)
55
+ return;
56
+ const authoritative = this.#scanExports(source, absFile);
57
+ authoritative.delete("default");
58
+ const attributed = new Set;
59
+ REEXPORT_RE.lastIndex = 0;
60
+ let m = REEXPORT_RE.exec(source);
61
+ while (m !== null) {
62
+ const star = m[1];
63
+ const nsAs = m[2];
64
+ const namedList = m[3];
65
+ const spec = m[5] ?? "";
66
+ m = REEXPORT_RE.exec(source);
67
+ if (!isRelative(spec))
68
+ continue;
69
+ if (star) {
70
+ if (nsAs) {
71
+ authoritative.delete(nsAs);
72
+ continue;
73
+ }
74
+ const targetAbs = await this.#resolveRel(absFile, spec);
75
+ if (!targetAbs)
76
+ continue;
77
+ await this.#walk(targetAbs, pkg, map, visited);
78
+ continue;
79
+ }
80
+ if (namedList !== undefined) {
81
+ const targetAbs = await this.#resolveRel(absFile, spec);
82
+ if (!targetAbs)
83
+ continue;
84
+ const targetSubpath = this.#subpathFor(pkg, targetAbs);
85
+ if (!targetSubpath)
86
+ continue;
87
+ for (const item of parseNamedList(namedList)) {
88
+ if (item.isType)
89
+ continue;
90
+ if (item.imported === "default")
91
+ continue;
92
+ if (!authoritative.has(item.local))
93
+ continue;
94
+ attributed.add(item.local);
95
+ if (!map.has(item.local)) {
96
+ map.set(item.local, { subpath: targetSubpath, originalName: item.imported });
97
+ }
98
+ }
99
+ }
100
+ }
101
+ LOCAL_NAMED_RE.lastIndex = 0;
102
+ let n = LOCAL_NAMED_RE.exec(source);
103
+ while (n !== null) {
104
+ const body = n[1] ?? "";
105
+ n = LOCAL_NAMED_RE.exec(source);
106
+ for (const item of parseNamedList(body)) {
107
+ if (item.isType)
108
+ continue;
109
+ if (item.imported === "default")
110
+ continue;
111
+ if (!authoritative.has(item.local))
112
+ continue;
113
+ attributed.add(item.local);
114
+ if (!map.has(item.local)) {
115
+ map.set(item.local, { subpath: currentSubpath, originalName: item.imported });
116
+ }
117
+ }
118
+ }
119
+ for (const name of authoritative) {
120
+ if (attributed.has(name))
121
+ continue;
122
+ if (map.has(name))
123
+ continue;
124
+ map.set(name, { subpath: currentSubpath, originalName: name });
125
+ }
126
+ }
127
+ #scanExports(source, absFile) {
128
+ try {
129
+ const transpiler = [".tsx", ".jsx"].includes(path.extname(absFile)) ? this.#tsxTranspiler : this.#tsTranspiler;
130
+ const { exports } = transpiler.scan(source);
131
+ return new Set(exports);
132
+ } catch (err) {
133
+ this.#logger.error(`scan failed: ${err.message}`);
134
+ return new Set;
135
+ }
136
+ }
137
+ #subpathFor(pkg, absFile) {
138
+ const rel = path.relative(pkg.pkgDir, absFile);
139
+ if (!rel || rel.startsWith("..") || path.isAbsolute(rel))
140
+ return null;
141
+ if (pkg.preserveFilePath)
142
+ return `${pkg.pkgName}/${rel.split(path.sep).join("/")}`;
143
+ const noExt = stripKnownExt(rel);
144
+ const tail = collapseIndex(noExt);
145
+ if (tail === "")
146
+ return pkg.pkgName;
147
+ return `${pkg.pkgName}/${tail.split(path.sep).join("/")}`;
148
+ }
149
+ async#resolveRel(fromFile, relSpec) {
150
+ if (this.#opts.resolveRelative)
151
+ return this.#opts.resolveRelative(fromFile, relSpec);
152
+ return defaultResolveRelative(fromFile, relSpec);
153
+ }
154
+ }
155
+ var parseNamedList = (listBody) => {
156
+ const out = [];
157
+ for (const raw of listBody.split(",")) {
158
+ const s = raw.trim();
159
+ if (!s)
160
+ continue;
161
+ let rest = s;
162
+ let isType = false;
163
+ if (rest.startsWith("type ")) {
164
+ isType = true;
165
+ rest = rest.slice(5).trim();
166
+ }
167
+ const asMatch = /^(\w+)\s+as\s+(\w+)$/.exec(rest);
168
+ if (asMatch) {
169
+ out.push({ imported: asMatch[1] ?? "", local: asMatch[2] ?? "", isType });
170
+ continue;
171
+ }
172
+ if (/^\w+$/.test(rest)) {
173
+ out.push({ imported: rest, local: rest, isType });
174
+ }
175
+ }
176
+ return out;
177
+ };
178
+ var isRelative = (spec) => {
179
+ return spec.startsWith("./") || spec.startsWith("../") || spec === "." || spec === "..";
180
+ };
181
+ var readIfExists = async (absFile) => {
182
+ const file = Bun.file(absFile);
183
+ if (!await file.exists())
184
+ return null;
185
+ return file.text();
186
+ };
187
+ var defaultResolveRelative = async (fromFile, relSpec) => {
188
+ const baseDir = path.dirname(fromFile);
189
+ const joined = path.resolve(baseDir, relSpec);
190
+ if (path.extname(joined)) {
191
+ if (await Bun.file(joined).exists())
192
+ return joined;
193
+ return null;
194
+ }
195
+ for (const ext of CANDIDATE_EXTS) {
196
+ const cand = joined + ext;
197
+ if (await Bun.file(cand).exists())
198
+ return cand;
199
+ }
200
+ for (const ext of CANDIDATE_EXTS) {
201
+ const cand = path.join(joined, `index${ext}`);
202
+ if (await Bun.file(cand).exists())
203
+ return cand;
204
+ }
205
+ return null;
206
+ };
207
+ var stripKnownExt = (relPath) => {
208
+ for (const ext of CANDIDATE_EXTS) {
209
+ if (relPath.endsWith(ext))
210
+ return relPath.slice(0, -ext.length);
211
+ }
212
+ return relPath;
213
+ };
214
+ var collapseIndex = (relPathNoExt) => {
215
+ const parts = relPathNoExt.split(path.sep);
216
+ if (parts[parts.length - 1] === "index")
217
+ parts.pop();
218
+ return parts.join(path.sep);
219
+ };
220
+
221
+ // pkgs/@akanjs/devkit/transforms/barrelImportsPlugin.ts
222
+ import path2 from "path";
223
+ import ts from "typescript";
224
+ var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAfter } = {}) => {
225
+ const akanConfig = await app.getConfig();
226
+ const barrels = [...new Set(akanConfig.barrelImports)].filter(Boolean);
227
+ const analyzer = new BarrelAnalyzer({
228
+ resolvePackage: await createTsconfigPackageResolver(app)
229
+ });
230
+ return {
231
+ name: "barrel-imports",
232
+ setup(build) {
233
+ build.onLoad({
234
+ filter: /^(?:(?!.*[\\/]node_modules[\\/]).*|.*[\\/]node_modules[\\/]akanjs[\\/].*)\.(tsx|ts|jsx|js)(\?v=\d+)?$/
235
+ }, async (args) => {
236
+ const realPath = args.path.replace(/\?v=\d+$/, "");
237
+ const loader = loaderFor(realPath);
238
+ if (skipPath(realPath)) {
239
+ const raw = await Bun.file(realPath).text();
240
+ return { contents: raw, loader };
241
+ }
242
+ let source = await Bun.file(realPath).text();
243
+ const hasMacroAttr = MACRO_ATTR_RE.test(source);
244
+ if (!hasMacroAttr && barrels.length > 0) {
245
+ const rewritten = await rewriteBarrelImports(source, barrels, analyzer);
246
+ if (rewritten !== null)
247
+ source = rewritten;
248
+ }
249
+ if (pipeAfter) {
250
+ const piped = await pipeAfter(source, { path: realPath });
251
+ if (piped !== null)
252
+ source = piped;
253
+ }
254
+ return { contents: source, loader };
255
+ });
256
+ }
257
+ };
258
+ };
259
+ var createTsconfigPackageResolver = async (app) => {
260
+ const tsconfig = await app.getTsConfig();
261
+ const tsconfigPaths = tsconfig.compilerOptions.paths ?? {};
262
+ const wildcardEntries = Object.entries(tsconfigPaths).filter(([k]) => k.endsWith("/*")).map(([k, v]) => ({
263
+ prefix: k.slice(0, -1),
264
+ replacements: v
265
+ })).sort((a, b) => b.prefix.length - a.prefix.length);
266
+ return async (pkgName) => {
267
+ const exact = tsconfigPaths[pkgName];
268
+ if (exact && exact.length > 0) {
269
+ const raw = exact[0];
270
+ if (!raw)
271
+ return null;
272
+ const entryFile = path2.resolve(app.workspace.workspaceRoot, raw);
273
+ if (!await Bun.file(entryFile).exists())
274
+ return null;
275
+ const parsed = path2.parse(entryFile);
276
+ const lastSlash = pkgName.lastIndexOf("/");
277
+ if (parsed.name !== "index" && lastSlash !== -1) {
278
+ const facet = pkgName.slice(lastSlash + 1);
279
+ const parentSpec = pkgName.slice(0, lastSlash);
280
+ if (facet === parsed.name && parentSpec.length > 0) {
281
+ return { pkgName: parentSpec, entryFile, pkgDir: parsed.dir };
282
+ }
283
+ }
284
+ return { pkgName, entryFile, pkgDir: path2.dirname(entryFile) };
285
+ }
286
+ for (const { prefix, replacements } of wildcardEntries) {
287
+ if (!pkgName.startsWith(prefix))
288
+ continue;
289
+ const suffix = pkgName.slice(prefix.length);
290
+ for (const repl of replacements) {
291
+ if (!repl)
292
+ continue;
293
+ const replPath = repl.endsWith("/*") ? repl.slice(0, -1) : repl;
294
+ const candidate = path2.resolve(app.workspace.workspaceRoot, replPath + suffix);
295
+ for (const ext of CANDIDATE_EXTS2) {
296
+ const file = `${candidate}${ext}`;
297
+ if (await Bun.file(file).exists()) {
298
+ const lastSlash = pkgName.lastIndexOf("/");
299
+ if (lastSlash !== -1) {
300
+ const parentSpec = pkgName.slice(0, lastSlash);
301
+ if (parentSpec.length > 0) {
302
+ return { pkgName: parentSpec, entryFile: file, pkgDir: path2.dirname(file) };
303
+ }
304
+ }
305
+ return { pkgName, entryFile: file, pkgDir: path2.dirname(file) };
306
+ }
307
+ }
308
+ for (const ext of CANDIDATE_EXTS2) {
309
+ const file = path2.join(candidate, `index${ext}`);
310
+ if (await Bun.file(file).exists()) {
311
+ return { pkgName, entryFile: file, pkgDir: candidate };
312
+ }
313
+ }
314
+ }
315
+ return null;
316
+ }
317
+ const exported = await resolveNodePackageExport(app.workspace.workspaceRoot, pkgName);
318
+ if (exported)
319
+ return exported;
320
+ const pkgJsonPath = path2.join(app.workspace.workspaceRoot, "node_modules", pkgName, "package.json");
321
+ if (!await Bun.file(pkgJsonPath).exists())
322
+ return null;
323
+ try {
324
+ const pkgJson = JSON.parse(await Bun.file(pkgJsonPath).text());
325
+ const rel = pkgJson.module ?? pkgJson.main ?? "index.js";
326
+ const entryFile = path2.resolve(path2.dirname(pkgJsonPath), rel);
327
+ if (!await Bun.file(entryFile).exists())
328
+ return null;
329
+ return { pkgName, entryFile, pkgDir: path2.dirname(pkgJsonPath) };
330
+ } catch {
331
+ return null;
332
+ }
333
+ };
334
+ };
335
+ var CANDIDATE_EXTS2 = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
336
+ var NODE_MODULES_RE = /[\\/]node_modules[\\/]/;
337
+ var AKANJS_NODE_MODULE_RE = /[\\/]node_modules[\\/]akanjs[\\/]/;
338
+ var defaultSkipPath = (absPath) => NODE_MODULES_RE.test(absPath) && !AKANJS_NODE_MODULE_RE.test(absPath);
339
+ var resolveNodePackageExport = async (workspaceRoot, specifier) => {
340
+ const packageName = getPackageName(specifier);
341
+ if (!packageName)
342
+ return null;
343
+ const pkgJsonPath = path2.join(workspaceRoot, "node_modules", packageName, "package.json");
344
+ if (!await Bun.file(pkgJsonPath).exists())
345
+ return null;
346
+ try {
347
+ const pkgDir = path2.dirname(pkgJsonPath);
348
+ const pkgJson = JSON.parse(await Bun.file(pkgJsonPath).text());
349
+ const subpath = specifier === packageName ? "." : `.${specifier.slice(packageName.length)}`;
350
+ const exported = resolvePackageExport(pkgJson.exports, subpath);
351
+ const rel = exported ?? (subpath === "." ? pkgJson.module ?? pkgJson.main ?? "index.js" : null);
352
+ if (!rel || !rel.startsWith("."))
353
+ return null;
354
+ const entryFile = await resolveFileCandidate(path2.resolve(pkgDir, rel));
355
+ if (!entryFile)
356
+ return null;
357
+ const pkgEntryName = specifier;
358
+ return { pkgName: pkgEntryName, entryFile, pkgDir: path2.dirname(entryFile), preserveFilePath: true };
359
+ } catch {
360
+ return null;
361
+ }
362
+ };
363
+ var resolveExportValue = (value) => {
364
+ if (!value)
365
+ return null;
366
+ if (typeof value === "string")
367
+ return value;
368
+ if (Array.isArray(value)) {
369
+ for (const item of value) {
370
+ const resolved = resolveExportValue(item);
371
+ if (resolved)
372
+ return resolved;
373
+ }
374
+ return null;
375
+ }
376
+ for (const condition of ["source", "import", "default", "types"]) {
377
+ const resolved = resolveExportValue(value[condition]);
378
+ if (resolved)
379
+ return resolved;
380
+ }
381
+ return null;
382
+ };
383
+ var resolvePackageExport = (exportsMap, subpath) => {
384
+ if (!exportsMap)
385
+ return null;
386
+ const exact = resolveExportValue(exportsMap[subpath]);
387
+ if (exact)
388
+ return exact;
389
+ for (const [key, value] of Object.entries(exportsMap)) {
390
+ const starIdx = key.indexOf("*");
391
+ if (starIdx === -1)
392
+ continue;
393
+ const prefix = key.slice(0, starIdx);
394
+ const suffix = key.slice(starIdx + 1);
395
+ if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
396
+ continue;
397
+ const wildcard = subpath.slice(prefix.length, subpath.length - suffix.length);
398
+ const resolved = resolveExportValue(value);
399
+ if (resolved)
400
+ return resolved.replace("*", wildcard);
401
+ }
402
+ return null;
403
+ };
404
+ var getPackageName = (specifier) => {
405
+ const parts = specifier.split("/");
406
+ if (!parts[0])
407
+ return null;
408
+ if (specifier.startsWith("@"))
409
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
410
+ return parts[0];
411
+ };
412
+ var resolveFileCandidate = async (candidate) => {
413
+ if (await Bun.file(candidate).exists())
414
+ return candidate;
415
+ if (path2.extname(candidate))
416
+ return null;
417
+ for (const ext of CANDIDATE_EXTS2) {
418
+ const file = `${candidate}${ext}`;
419
+ if (await Bun.file(file).exists())
420
+ return file;
421
+ }
422
+ for (const ext of CANDIDATE_EXTS2) {
423
+ const file = path2.join(candidate, `index${ext}`);
424
+ if (await Bun.file(file).exists())
425
+ return file;
426
+ }
427
+ return null;
428
+ };
429
+ var MACRO_ATTR_RE = /with\s*\{\s*type\s*:\s*["']macro["']\s*\}/;
430
+ var rewriteBarrelImports = async (source, barrels, analyzer) => {
431
+ if (!barrels.some((barrel) => source.includes(barrel)))
432
+ return null;
433
+ const statements = findImportStatements(source);
434
+ if (statements.length === 0)
435
+ return null;
436
+ let changed = false;
437
+ let out = source;
438
+ for (let i = statements.length - 1;i >= 0; i--) {
439
+ const stmt = statements[i];
440
+ if (!stmt)
441
+ continue;
442
+ if (!barrels.includes(stmt.specifier))
443
+ continue;
444
+ const map = await analyzer.analyze(stmt.specifier);
445
+ if (!map || map.size === 0)
446
+ continue;
447
+ const replacement = rewriteSingleStatement(stmt, map);
448
+ if (replacement === null)
449
+ continue;
450
+ out = out.slice(0, stmt.start) + replacement + out.slice(stmt.end);
451
+ changed = true;
452
+ }
453
+ return changed ? out : null;
454
+ };
455
+ var findImportStatements = (source) => {
456
+ const statements = [];
457
+ const sourceFile = ts.createSourceFile("barrel-imports.tsx", source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TSX);
458
+ for (const statement of sourceFile.statements) {
459
+ if (!ts.isImportDeclaration(statement))
460
+ continue;
461
+ if (!ts.isStringLiteral(statement.moduleSpecifier))
462
+ continue;
463
+ const importClause = statement.importClause;
464
+ if (!importClause)
465
+ continue;
466
+ const statementStart = statement.getStart(sourceFile);
467
+ const statementEnd = statement.end;
468
+ statements.push({
469
+ start: statementStart,
470
+ end: statementEnd,
471
+ clause: source.slice(importClause.getStart(sourceFile), importClause.end).trim(),
472
+ specifier: statement.moduleSpecifier.text,
473
+ trailingSemicolon: source.slice(statement.moduleSpecifier.end, statement.end).includes(";"),
474
+ raw: source.slice(statementStart, statementEnd)
475
+ });
476
+ }
477
+ return statements;
478
+ };
479
+ var parseImportClause = (clause) => {
480
+ let rest = clause.trim();
481
+ let typeOnly = false;
482
+ if (rest.startsWith("type ")) {
483
+ typeOnly = true;
484
+ rest = rest.slice(5).trim();
485
+ }
486
+ const parsed = { typeOnly };
487
+ const commaMatch = /^(\w+)\s*,\s*(.+)$/.exec(rest);
488
+ if (commaMatch) {
489
+ parsed.defaultImport = commaMatch[1];
490
+ rest = commaMatch[2] ?? "";
491
+ } else if (/^\w+$/.test(rest)) {
492
+ parsed.defaultImport = rest;
493
+ return parsed;
494
+ }
495
+ if (rest.startsWith("*")) {
496
+ const ns = /^\*\s+as\s+(\w+)$/.exec(rest);
497
+ if (!ns)
498
+ return null;
499
+ parsed.namespaceImport = ns[1];
500
+ return parsed;
501
+ }
502
+ if (rest.startsWith("{")) {
503
+ const close = rest.indexOf("}");
504
+ if (close === -1)
505
+ return null;
506
+ const inner = rest.slice(1, close);
507
+ parsed.named = parseNamedImportList(inner);
508
+ return parsed;
509
+ }
510
+ return parsed;
511
+ };
512
+ var parseNamedImportList = (body) => {
513
+ const out = [];
514
+ for (const raw of body.split(",")) {
515
+ const s = raw.trim();
516
+ if (!s)
517
+ continue;
518
+ let isType = false;
519
+ let rest = s;
520
+ if (rest.startsWith("type ")) {
521
+ isType = true;
522
+ rest = rest.slice(5).trim();
523
+ }
524
+ const asMatch = /^(\w+)\s+as\s+(\w+)$/.exec(rest);
525
+ if (asMatch) {
526
+ out.push({ imported: asMatch[1] ?? "", local: asMatch[2] ?? "", isType });
527
+ continue;
528
+ }
529
+ if (/^\w+$/.test(rest)) {
530
+ out.push({ imported: rest, local: rest, isType });
531
+ }
532
+ }
533
+ return out;
534
+ };
535
+ var rewriteSingleStatement = (stmt, map) => {
536
+ const clause = parseImportClause(stmt.clause);
537
+ if (!clause)
538
+ return null;
539
+ if (clause.namespaceImport)
540
+ return null;
541
+ if (clause.typeOnly && !clause.defaultImport)
542
+ return null;
543
+ if (!clause.named || clause.named.length === 0) {
544
+ return null;
545
+ }
546
+ const remaining = [];
547
+ const rewrites = new Map;
548
+ for (const item of clause.named) {
549
+ if (item.isType) {
550
+ remaining.push(item);
551
+ continue;
552
+ }
553
+ const target = map.get(item.imported);
554
+ if (!target) {
555
+ remaining.push(item);
556
+ continue;
557
+ }
558
+ const list = rewrites.get(target.subpath) ?? [];
559
+ list.push({ imported: target.originalName, local: item.local, isType: false });
560
+ rewrites.set(target.subpath, list);
561
+ }
562
+ if (rewrites.size === 0)
563
+ return null;
564
+ const lines = [];
565
+ const tail = ";";
566
+ if (shouldPreserveBarrelSideEffects(stmt.specifier)) {
567
+ lines.push(`import "${stmt.specifier}"${tail}`);
568
+ }
569
+ if (clause.defaultImport || remaining.length > 0) {
570
+ const parts = [];
571
+ if (clause.defaultImport)
572
+ parts.push(clause.defaultImport);
573
+ if (remaining.length > 0) {
574
+ parts.push(`{ ${remaining.map(serializeNamedItem).join(", ")} }`);
575
+ }
576
+ lines.push(`import ${parts.join(", ")} from "${stmt.specifier}"${tail}`);
577
+ }
578
+ for (const [subpath, items] of rewrites) {
579
+ lines.push(`import { ${items.map(serializeNamedItem).join(", ")} } from "${subpath}"${tail}`);
580
+ }
581
+ return lines.join(`
582
+ `);
583
+ };
584
+ var shouldPreserveBarrelSideEffects = (specifier) => /^@(apps|libs)\/[^/]+\/client$/.test(specifier);
585
+ var serializeNamedItem = (item) => {
586
+ const prefix = item.isType ? "type " : "";
587
+ if (item.imported === item.local)
588
+ return `${prefix}${item.imported}`;
589
+ return `${prefix}${item.imported} as ${item.local}`;
590
+ };
591
+ var loaderFor = (absPath) => {
592
+ if (absPath.endsWith(".tsx"))
593
+ return "tsx";
594
+ if (absPath.endsWith(".jsx"))
595
+ return "jsx";
596
+ if (absPath.endsWith(".ts"))
597
+ return "ts";
598
+ return "js";
599
+ };
600
+
601
+ // pkgs/@akanjs/devkit/frontendBuild/clientEntryDiscovery.ts
602
+ var USE_CLIENT_RE = /^\s*(?:\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*["']use client["']/;
603
+ var SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
604
+ var NODE_MODULES_RE2 = /[\\/]node_modules[\\/]/;
605
+ var AKANJS_NODE_MODULE_RE2 = /[\\/]node_modules[\\/]akanjs[\\/]/;
606
+ var NON_SOURCE_EXT_RE = /\.(css|scss|sass|less|json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav)$/i;
607
+ var shouldSkipNodeModule = (absPath) => NODE_MODULES_RE2.test(absPath) && !AKANJS_NODE_MODULE_RE2.test(absPath);
608
+
609
+ class GraphClientEntryDiscovery {
610
+ #akanConfig;
611
+ #resolvePackage;
612
+ #analyzer;
613
+ #tsTranspiler = new Bun.Transpiler({ loader: "tsx" });
614
+ #fileExistsCache = new Map;
615
+ #factsCache = new Map;
616
+ #resolvedFileCache = new Map;
617
+ #resolvedSpecifierCache = new Map;
618
+ #reachableEntriesCache = new Map;
619
+ #missingFiles = new Set;
620
+ #unresolvedPaths = new Set;
621
+ #unresolvedSpecifiers = new Set;
622
+ constructor(akanConfig, resolvePackage) {
623
+ this.#akanConfig = akanConfig;
624
+ this.#resolvePackage = resolvePackage;
625
+ this.#analyzer = new BarrelAnalyzer({ resolvePackage });
626
+ }
627
+ static async create(app) {
628
+ return new GraphClientEntryDiscovery(await app.getConfig(), await createTsconfigPackageResolver(app));
629
+ }
630
+ async discover(seeds) {
631
+ const entries = new Set;
632
+ for (const seed of seeds) {
633
+ for (const entry of await this.#discoverFromFile(seed, new Set))
634
+ entries.add(entry);
635
+ }
636
+ return Array.from(entries).sort();
637
+ }
638
+ invalidate(files) {
639
+ for (const file of files) {
640
+ const absPath = path3.resolve(file);
641
+ this.#factsCache.delete(absPath);
642
+ this.#fileExistsCache.delete(absPath);
643
+ this.#reachableEntriesCache.delete(absPath);
644
+ }
645
+ if (files.length === 0)
646
+ return;
647
+ this.#reachableEntriesCache.clear();
648
+ this.#forgetMissing();
649
+ }
650
+ #forgetMissing() {
651
+ for (const key of this.#missingFiles)
652
+ this.#fileExistsCache.delete(key);
653
+ for (const key of this.#unresolvedPaths)
654
+ this.#resolvedFileCache.delete(key);
655
+ for (const key of this.#unresolvedSpecifiers)
656
+ this.#resolvedSpecifierCache.delete(key);
657
+ this.#missingFiles.clear();
658
+ this.#unresolvedPaths.clear();
659
+ this.#unresolvedSpecifiers.clear();
660
+ }
661
+ async#fileExists(p) {
662
+ const absPath = path3.resolve(p);
663
+ let cached = this.#fileExistsCache.get(absPath);
664
+ if (!cached) {
665
+ cached = Bun.file(absPath).exists().then((exists) => {
666
+ if (!exists)
667
+ this.#missingFiles.add(absPath);
668
+ return exists;
669
+ });
670
+ this.#fileExistsCache.set(absPath, cached);
671
+ }
672
+ return cached;
673
+ }
674
+ #facts(file) {
675
+ const absPath = path3.resolve(file);
676
+ let cached = this.#factsCache.get(absPath);
677
+ if (!cached) {
678
+ cached = (async () => {
679
+ const content = await Bun.file(absPath).text().catch(() => null);
680
+ if (content === null)
681
+ return null;
682
+ if (USE_CLIENT_RE.test(content))
683
+ return { isClientEntry: true, imports: [] };
684
+ return { isClientEntry: false, imports: this.#scanImports(await this.#rewrite(content)) };
685
+ })();
686
+ this.#factsCache.set(absPath, cached);
687
+ }
688
+ return cached;
689
+ }
690
+ async#rewrite(content) {
691
+ if (this.#akanConfig.barrelImports.length === 0)
692
+ return content;
693
+ try {
694
+ return await rewriteBarrelImports(content, this.#akanConfig.barrelImports, this.#analyzer) ?? content;
695
+ } catch {
696
+ return content;
697
+ }
698
+ }
699
+ #scanImports(source) {
700
+ try {
701
+ return this.#tsTranspiler.scanImports(source);
702
+ } catch {
703
+ return [];
704
+ }
705
+ }
706
+ async#resolveFileCandidate(absPathNoExt) {
707
+ const cacheKey = path3.resolve(absPathNoExt);
708
+ let cached = this.#resolvedFileCache.get(cacheKey);
709
+ if (cached)
710
+ return cached;
711
+ cached = (async () => {
712
+ if (await this.#fileExists(cacheKey))
713
+ return cacheKey;
714
+ for (const ext of SOURCE_EXTS) {
715
+ const f = `${cacheKey}${ext}`;
716
+ if (await this.#fileExists(f))
717
+ return f;
718
+ }
719
+ for (const ext of SOURCE_EXTS) {
720
+ const f = path3.join(cacheKey, `index${ext}`);
721
+ if (await this.#fileExists(f))
722
+ return f;
723
+ }
724
+ this.#unresolvedPaths.add(cacheKey);
725
+ return null;
726
+ })();
727
+ this.#resolvedFileCache.set(cacheKey, cached);
728
+ return cached;
729
+ }
730
+ async#resolveSpecifier(spec, importerDir) {
731
+ const cacheKey = `${importerDir}\x00${spec}`;
732
+ let cached = this.#resolvedSpecifierCache.get(cacheKey);
733
+ if (cached)
734
+ return cached;
735
+ cached = (async () => {
736
+ if (spec.startsWith(".") || spec.startsWith("/")) {
737
+ const abs = spec.startsWith("/") ? spec : path3.resolve(importerDir, spec);
738
+ return this.#resolveFileCandidate(abs);
739
+ }
740
+ const pkg = await this.#resolvePackage(spec);
741
+ if (pkg)
742
+ return pkg.entryFile;
743
+ this.#unresolvedSpecifiers.add(cacheKey);
744
+ return null;
745
+ })();
746
+ this.#resolvedSpecifierCache.set(cacheKey, cached);
747
+ return cached;
748
+ }
749
+ async#discoverFromFile(file, visiting) {
750
+ const absPath = path3.resolve(file);
751
+ const cached = this.#reachableEntriesCache.get(absPath);
752
+ if (cached)
753
+ return new Set(cached);
754
+ if (visiting.has(absPath) || shouldSkipNodeModule(absPath))
755
+ return new Set;
756
+ visiting.add(absPath);
757
+ const entries = new Set;
758
+ const facts = await this.#facts(absPath);
759
+ if (!facts)
760
+ return this.#finishDiscovery(absPath, visiting, entries);
761
+ if (facts.isClientEntry) {
762
+ entries.add(absPath);
763
+ return this.#finishDiscovery(absPath, visiting, entries);
764
+ }
765
+ const importerDir = path3.dirname(absPath);
766
+ for (const imp of facts.imports) {
767
+ const spec = imp.path;
768
+ if (!spec)
769
+ continue;
770
+ if (NON_SOURCE_EXT_RE.test(spec))
771
+ continue;
772
+ const resolved = await this.#resolveSpecifier(spec, importerDir);
773
+ if (!resolved)
774
+ continue;
775
+ if (shouldSkipNodeModule(resolved))
776
+ continue;
777
+ for (const entry of await this.#discoverFromFile(resolved, visiting))
778
+ entries.add(entry);
779
+ }
780
+ return this.#finishDiscovery(absPath, visiting, entries);
781
+ }
782
+ #finishDiscovery(absPath, visiting, entries) {
783
+ visiting.delete(absPath);
784
+ this.#reachableEntriesCache.set(absPath, entries);
785
+ return new Set(entries);
786
+ }
787
+ }
788
+
789
+ // pkgs/@akanjs/devkit/frontendBuild/routeClientBuilder.ts
790
+ import { mkdir } from "fs/promises";
791
+ import path6 from "path";
792
+
793
+ // pkgs/@akanjs/devkit/transforms/rscUseClientTransform.ts
794
+ import path4 from "path";
795
+ var USE_CLIENT_RE2 = /^\s*(?:\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*["']use client["']/;
796
+ var IMPLICIT_ROOT_LAYOUT_RE = /[/\\]\.akan[/\\]generated[/\\](?:implicit-root-layout|root-layouts[/\\].*__root_layout)\.(tsx|ts|jsx|js)$/;
797
+ function toClientReferencePath(absPath, workspaceRoot) {
798
+ return path4.relative(path4.resolve(workspaceRoot), path4.resolve(absPath)).split(path4.sep).join("/");
799
+ }
800
+ function transformUseClient(source, args) {
801
+ if (!USE_CLIENT_RE2.test(source))
802
+ return null;
803
+ if (IMPLICIT_ROOT_LAYOUT_RE.test(args.path))
804
+ return null;
805
+ const transpiler = new Bun.Transpiler({ loader: loaderFor2(args.path) });
806
+ const { exports } = transpiler.scan(source);
807
+ if (exports.length === 0)
808
+ return null;
809
+ const referencePath = args.workspaceRoot ? toClientReferencePath(args.path, args.workspaceRoot) : args.path;
810
+ const filePathLit = JSON.stringify(referencePath);
811
+ const lines = [`import { registerClientReference } from "react-server-dom-webpack/server.node";`];
812
+ for (const name of exports) {
813
+ const nameLit = JSON.stringify(name);
814
+ const errMsg = JSON.stringify(`Attempted to call '${name}' from '${referencePath}' on the server, but it is a client-only export.`);
815
+ const proxy = `() => { throw new Error(${errMsg}); }`;
816
+ const binding = name === "default" ? `export default registerClientReference(${proxy}, ${filePathLit}, ${nameLit});` : `export const ${name} = registerClientReference(${proxy}, ${filePathLit}, ${nameLit});`;
817
+ lines.push(binding);
818
+ }
819
+ return lines.join(`
820
+ `);
821
+ }
822
+ function loaderFor2(absPath) {
823
+ if (absPath.endsWith(".tsx"))
824
+ return "tsx";
825
+ if (absPath.endsWith(".jsx"))
826
+ return "jsx";
827
+ if (absPath.endsWith(".ts"))
828
+ return "ts";
829
+ return "js";
830
+ }
831
+
832
+ // pkgs/@akanjs/devkit/frontendBuild/clientEntriesBundler.ts
833
+ import fs from "fs";
834
+ import path5 from "path";
835
+
836
+ // pkgs/@akanjs/devkit/frontendBuild/clientBuildTypes.ts
837
+ var CLIENT_BUNDLE_NAMING = {
838
+ entry: "[name]-[hash].[ext]",
839
+ chunk: "chunks/[hash].[ext]",
840
+ asset: "assets/[hash].[ext]"
841
+ };
842
+
843
+ // pkgs/@akanjs/devkit/frontendBuild/clientEntriesBundler.ts
844
+ class ClientEntriesBundler {
845
+ #app;
846
+ #entries;
847
+ #plugins;
848
+ #external;
849
+ #externalSubpaths;
850
+ #externalAliases;
851
+ #command;
852
+ #outputSubdir;
853
+ #reactFastRefresh;
854
+ #artifactDir;
855
+ #outdir;
856
+ #servePrefix;
857
+ #manifest = {};
858
+ #ssrManifest = { moduleLoading: null, moduleMap: {} };
859
+ #entryUrlsByAbsPath = new Map;
860
+ #entryOutputAbsByAbsPath = new Map;
861
+ #entryDepsByAbsPath = new Map;
862
+ #clientReferenceIdByAbsPath = new Map;
863
+ #opaqueEntries = null;
864
+ #metafileOutputsByAbs = new Map;
865
+ constructor(options) {
866
+ this.#app = options.app;
867
+ this.#entries = options.entries;
868
+ this.#plugins = options.plugins ?? [];
869
+ this.#external = options.external ?? [];
870
+ this.#externalSubpaths = options.externalSubpaths ?? [];
871
+ this.#externalAliases = options.externalAliases ?? {};
872
+ this.#command = options.command ?? "start";
873
+ this.#outputSubdir = options.outputSubdir ?? "client";
874
+ this.#reactFastRefresh = options.reactFastRefresh ?? false;
875
+ this.#artifactDir = `${this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath}/.akan/artifact`;
876
+ this.#outdir = `${this.#artifactDir}/${this.#outputSubdir}`;
877
+ this.#servePrefix = `/_akan/${this.#outputSubdir}`;
878
+ }
879
+ async bundle() {
880
+ const akanConfig = await this.#app.getConfig();
881
+ this.#opaqueEntries = await this.#createOpaqueEntryAliases();
882
+ const result = await Bun.build({
883
+ entrypoints: this.#opaqueEntries.entries,
884
+ outdir: this.#outdir,
885
+ splitting: true,
886
+ target: "browser",
887
+ format: "esm",
888
+ naming: CLIENT_BUNDLE_NAMING,
889
+ metafile: true,
890
+ define: this.#getDefine(),
891
+ minify: this.#command === "build",
892
+ optimizeImports: akanConfig.optimizeImports,
893
+ reactFastRefresh: this.#command === "start" && this.#outputSubdir === "client" && this.#reactFastRefresh,
894
+ plugins: this.#external.length > 0 || this.#externalSubpaths.length > 0 || Object.keys(this.#externalAliases).length > 0 ? [...this.#plugins, this.#createExternalSpecifiersPlugin()] : this.#plugins
895
+ });
896
+ if (!result.success)
897
+ throw new AggregateError(result.logs, "[ClientEntriesBundler] Bun.build failed");
898
+ await this.#rewriteExternalImportSpecifiers(result.outputs);
899
+ const metafile = result.metafile;
900
+ if (!metafile)
901
+ throw new Error("[ClientEntriesBundler] metafile is missing");
902
+ this.#metafileOutputsByAbs = new Map(Object.entries(metafile.outputs).map(([k, v]) => [this.#absFromOutdir(k), v]));
903
+ for (const artifact of result.outputs.filter((a) => a.kind === "entry-point"))
904
+ this.#addEntryArtifact(artifact.path);
905
+ return {
906
+ manifest: this.#manifest,
907
+ ssrManifest: this.#ssrManifest,
908
+ entryUrlsByAbsPath: this.#entryUrlsByAbsPath,
909
+ entryOutputAbsByAbsPath: this.#entryOutputAbsByAbsPath,
910
+ entryDepsByAbsPath: this.#entryDepsByAbsPath,
911
+ clientReferenceIdByAbsPath: this.#clientReferenceIdByAbsPath
912
+ };
913
+ }
914
+ #getDefine() {
915
+ const nodeEnv = this.#command === "build" ? "production" : "development";
916
+ return {
917
+ "process.env.NODE_ENV": JSON.stringify(nodeEnv),
918
+ "process.env.AKAN_PUBLIC_RENDER_ENV": JSON.stringify("ssr"),
919
+ ...Object.fromEntries(Object.entries(this.#app.getPublicEnv()).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]))
920
+ };
921
+ }
922
+ async#createOpaqueEntryAliases() {
923
+ const aliasDir = path5.join(this.#app.cwdPath, ".akan", "generated", "client-entry-alias", this.#outputSubdir);
924
+ fs.mkdirSync(aliasDir, { recursive: true });
925
+ const originalByAlias = new Map;
926
+ const aliasedEntries = await Promise.all(this.#entries.map(async (entry) => {
927
+ const absEntry = path5.resolve(entry);
928
+ const hash = Bun.hash(`${this.#app.name}
929
+ ${this.#outputSubdir}
930
+ ${absEntry}`).toString(36);
931
+ const aliasPath = path5.join(aliasDir, `${hash}.tsx`);
932
+ await Bun.write(aliasPath, this.#createOpaqueEntryAliasSource(absEntry, await this.#scanEntryExportNames(absEntry)));
933
+ originalByAlias.set(path5.resolve(aliasPath), absEntry);
934
+ return aliasPath;
935
+ }));
936
+ return { entries: aliasedEntries, originalByAlias, aliasDir };
937
+ }
938
+ #createOpaqueEntryAliasSource(absEntry, exportNames) {
939
+ const entryLit = JSON.stringify(path5.resolve(absEntry));
940
+ if (exportNames.length === 0)
941
+ return `export * from ${entryLit};
942
+ `;
943
+ const namedExports = exportNames.filter((name) => name !== "default");
944
+ const lines = namedExports.length > 0 ? [`export { ${namedExports.join(", ")} } from ${entryLit};`] : [];
945
+ if (exportNames.includes("default"))
946
+ lines.push(`export { default } from ${entryLit};`);
947
+ return `${lines.join(`
948
+ `)}
949
+ `;
950
+ }
951
+ async#scanEntryExportNames(absEntry) {
952
+ const source = await Bun.file(absEntry).text();
953
+ const transpiler = new Bun.Transpiler({ loader: this.#loaderForEntry(absEntry) });
954
+ return transpiler.scan(source).exports;
955
+ }
956
+ #loaderForEntry(absPath) {
957
+ if (absPath.endsWith(".tsx"))
958
+ return "tsx";
959
+ if (absPath.endsWith(".jsx"))
960
+ return "jsx";
961
+ if (absPath.endsWith(".ts"))
962
+ return "ts";
963
+ return "js";
964
+ }
965
+ #createExternalSpecifiersPlugin() {
966
+ const set = new Set(this.#external);
967
+ const subpathSet = new Set(this.#externalSubpaths);
968
+ const aliases = this.#externalAliases;
969
+ const specifiers = [...new Set([...this.#external, ...this.#externalSubpaths, ...Object.keys(aliases)])];
970
+ const escaped = specifiers.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
971
+ const filter = new RegExp(`^(${escaped.join("|")})(?:/.*)?$`);
972
+ return {
973
+ name: "akan-externalize-specifiers",
974
+ setup(build) {
975
+ build.onResolve({ filter }, (args) => {
976
+ const alias = aliases[args.path];
977
+ if (alias)
978
+ return { path: alias, external: true };
979
+ if (!ClientEntriesBundler.#matchesExternalSpecifier(args.path, set, subpathSet))
980
+ return;
981
+ return { path: args.path, external: true };
982
+ });
983
+ }
984
+ };
985
+ }
986
+ static #matchesExternalSpecifier(specifier, exactExternals, subpathExternals) {
987
+ if (exactExternals.has(specifier))
988
+ return true;
989
+ if (subpathExternals.has(specifier))
990
+ return true;
991
+ for (const external of subpathExternals) {
992
+ if (specifier.startsWith(`${external}/`))
993
+ return true;
994
+ }
995
+ return false;
996
+ }
997
+ async#rewriteExternalImportSpecifiers(outputs) {
998
+ if (Object.keys(this.#externalAliases).length === 0)
999
+ return;
1000
+ await Promise.all(outputs.filter((output) => output.path.endsWith(".js")).map(async (output) => {
1001
+ const source = await Bun.file(output.path).text();
1002
+ const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(source, this.#externalAliases);
1003
+ if (rewritten !== source)
1004
+ await Bun.write(output.path, rewritten);
1005
+ }));
1006
+ }
1007
+ static rewriteExternalImportSpecifiers(source, aliases) {
1008
+ let rewritten = source;
1009
+ for (const [specifier, alias] of Object.entries(aliases)) {
1010
+ if (!alias)
1011
+ continue;
1012
+ const escaped = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1013
+ rewritten = rewritten.replace(new RegExp(`(\\bfrom\\s*["'])${escaped}(["'])`, "g"), (_match, prefix, suffix) => {
1014
+ return `${prefix}${alias}${suffix}`;
1015
+ }).replace(new RegExp(`(\\bimport\\s*["'])${escaped}(["'])`, "g"), (_match, prefix, suffix) => {
1016
+ return `${prefix}${alias}${suffix}`;
1017
+ });
1018
+ }
1019
+ return rewritten;
1020
+ }
1021
+ #toServeUrl(absOutPath) {
1022
+ const rel = path5.relative(this.#outdir, absOutPath).split(path5.sep).join("/");
1023
+ return `${this.#servePrefix}/${rel}`;
1024
+ }
1025
+ #absFromOutdir(p) {
1026
+ return path5.isAbsolute(p) ? p : path5.resolve(this.#outdir, p);
1027
+ }
1028
+ #absFromEntryPoint(p) {
1029
+ if (path5.isAbsolute(p))
1030
+ return path5.resolve(p);
1031
+ const candidates = [path5.resolve(process.cwd(), p), path5.resolve(this.#app.cwdPath, p)];
1032
+ return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
1033
+ }
1034
+ #collectChunkUrls(absOutPath, visited = new Set) {
1035
+ if (visited.has(absOutPath))
1036
+ return [];
1037
+ visited.add(absOutPath);
1038
+ const info = this.#metafileOutputsByAbs.get(absOutPath);
1039
+ if (!info)
1040
+ return [this.#toServeUrl(absOutPath)];
1041
+ const urls = [this.#toServeUrl(absOutPath)];
1042
+ for (const imp of info.imports) {
1043
+ if (imp.kind === "dynamic-import")
1044
+ continue;
1045
+ const absImp = this.#absFromOutdir(imp.path);
1046
+ if (!absImp.endsWith(".js"))
1047
+ continue;
1048
+ urls.push(...this.#collectChunkUrls(absImp, visited));
1049
+ }
1050
+ return urls;
1051
+ }
1052
+ #collectClientDeps(absOutPath, visited = new Set) {
1053
+ if (visited.has(absOutPath))
1054
+ return new Set;
1055
+ visited.add(absOutPath);
1056
+ const deps = new Set;
1057
+ const info = this.#metafileOutputsByAbs.get(absOutPath);
1058
+ if (!info)
1059
+ return deps;
1060
+ if (!this.#opaqueEntries)
1061
+ throw new Error("[ClientEntriesBundler] opaque entries are missing");
1062
+ for (const input of Object.keys(info.inputs ?? {})) {
1063
+ const absInput = this.#absFromEntryPoint(input);
1064
+ if (!absInput.includes("node_modules") && !absInput.startsWith(this.#opaqueEntries.aliasDir))
1065
+ deps.add(absInput);
1066
+ }
1067
+ for (const imp of info.imports) {
1068
+ if (imp.kind === "dynamic-import")
1069
+ continue;
1070
+ const absImp = this.#absFromOutdir(imp.path);
1071
+ if (!absImp.endsWith(".js"))
1072
+ continue;
1073
+ for (const dep of this.#collectClientDeps(absImp, visited))
1074
+ deps.add(dep);
1075
+ }
1076
+ return deps;
1077
+ }
1078
+ #addEntryArtifact(absOut) {
1079
+ if (!this.#opaqueEntries)
1080
+ throw new Error("[ClientEntriesBundler] opaque entries are missing");
1081
+ const info = this.#metafileOutputsByAbs.get(absOut);
1082
+ const buildEntryPointAbs = info?.entryPoint ? this.#absFromEntryPoint(info.entryPoint) : null;
1083
+ const entryPointAbs = buildEntryPointAbs ? this.#opaqueEntries.originalByAlias.get(buildEntryPointAbs) ?? buildEntryPointAbs : null;
1084
+ if (!entryPointAbs)
1085
+ return;
1086
+ const chunkUrls = this.#collectChunkUrls(absOut);
1087
+ const entryUrl = chunkUrls[0] ?? this.#toServeUrl(absOut);
1088
+ this.#entryUrlsByAbsPath.set(entryPointAbs, entryUrl);
1089
+ this.#entryOutputAbsByAbsPath.set(entryPointAbs, absOut);
1090
+ this.#entryDepsByAbsPath.set(entryPointAbs, [...this.#collectClientDeps(absOut)].sort());
1091
+ const clientReferenceId = toClientReferencePath(entryPointAbs, this.#app.workspace.workspaceRoot);
1092
+ this.#clientReferenceIdByAbsPath.set(entryPointAbs, clientReferenceId);
1093
+ const flatChunks = [];
1094
+ for (const url of chunkUrls)
1095
+ flatChunks.push(url, url);
1096
+ const exportNames = info?.exports && info.exports.length > 0 ? info.exports : ["default"];
1097
+ const ssrEntriesByName = {};
1098
+ for (const name of exportNames) {
1099
+ const key = `${clientReferenceId}#${name}`;
1100
+ this.#manifest[key] = { id: entryUrl, chunks: flatChunks, name, async: true };
1101
+ ssrEntriesByName[name] = { id: absOut, chunks: [absOut, absOut], name, async: true };
1102
+ }
1103
+ this.#ssrManifest.moduleMap[entryUrl] = ssrEntriesByName;
1104
+ }
1105
+ }
1106
+
1107
+ // pkgs/@akanjs/devkit/frontendBuild/vendorSpecifiers.ts
1108
+ var VENDOR_SPECIFIERS = [
1109
+ "react",
1110
+ "react-dom",
1111
+ "react-dom/client",
1112
+ "react/jsx-runtime",
1113
+ "react/jsx-dev-runtime",
1114
+ "react-refresh/runtime",
1115
+ "scheduler",
1116
+ "react-server-dom-webpack/client.browser",
1117
+ "akanjs/store",
1118
+ "akanjs/base",
1119
+ "akanjs/common",
1120
+ "akanjs/constant"
1121
+ ];
1122
+
1123
+ // pkgs/@akanjs/devkit/frontendBuild/routeClientBuilder.ts
1124
+ var SSR_CLIENT_EXTERNALS = [
1125
+ "react",
1126
+ "react-dom",
1127
+ "react-dom/client",
1128
+ "react/jsx-runtime",
1129
+ "react/jsx-dev-runtime",
1130
+ "akanjs/fetch"
1131
+ ];
1132
+ var SSR_CLIENT_ALIAS_EXTERNALS = [
1133
+ "react",
1134
+ "react-dom",
1135
+ "react-dom/client",
1136
+ "react/jsx-runtime",
1137
+ "react/jsx-dev-runtime"
1138
+ ];
1139
+
1140
+ class RouteClientBuilder {
1141
+ #app;
1142
+ #seeds;
1143
+ #knownEntries;
1144
+ #command;
1145
+ #discovery;
1146
+ constructor(options) {
1147
+ this.#app = options.app;
1148
+ this.#seeds = options.seeds;
1149
+ this.#knownEntries = options.knownEntries ?? new Set;
1150
+ this.#command = options.command ?? "start";
1151
+ this.#discovery = options.discovery;
1152
+ }
1153
+ async build() {
1154
+ const discovery = this.#discovery ?? await GraphClientEntryDiscovery.create(this.#app);
1155
+ const discovered = await discovery.discover(this.#seeds);
1156
+ const entries = discovered.filter((e) => !this.#knownEntries.has(e));
1157
+ if (entries.length === 0)
1158
+ return this.#emptyResult(discovered);
1159
+ const bootstrapEntries = await this.#createBootstrapEntries(entries);
1160
+ const browserBundle = await this.#buildBrowserBundle(bootstrapEntries);
1161
+ const ssrBundle = await this.#buildSsrBundle(bootstrapEntries);
1162
+ const acceptedEntries = new Set(entries);
1163
+ const manifestDelta = {};
1164
+ const ssrModuleMap = {};
1165
+ const clientDeps = new Set;
1166
+ const clientDepsByEntry = {};
1167
+ for (const [key, row] of Object.entries(browserBundle.manifest)) {
1168
+ const manifestEntry = RouteClientBuilder.resolveOriginalManifestEntry(key, bootstrapEntries.originalByBuildEntry, browserBundle.clientReferenceIdByAbsPath, this.#app.workspace.workspaceRoot);
1169
+ if (!manifestEntry)
1170
+ continue;
1171
+ if (!acceptedEntries.has(manifestEntry.originalEntry))
1172
+ continue;
1173
+ manifestDelta[manifestEntry.key] = row;
1174
+ const ssrOutput = ssrBundle.entryOutputAbsByAbsPath.get(manifestEntry.buildEntry);
1175
+ if (!ssrOutput)
1176
+ continue;
1177
+ ssrModuleMap[row.id] ??= {};
1178
+ ssrModuleMap[row.id][row.name] = { id: ssrOutput, chunks: [ssrOutput, ssrOutput], name: row.name, async: true };
1179
+ }
1180
+ for (const entry of bootstrapEntries.buildEntries) {
1181
+ const buildEntry = path6.resolve(entry);
1182
+ const originalEntry = path6.resolve(bootstrapEntries.originalByBuildEntry.get(buildEntry) ?? buildEntry);
1183
+ if (!acceptedEntries.has(originalEntry))
1184
+ continue;
1185
+ const deps = new Set([originalEntry]);
1186
+ for (const dep of browserBundle.entryDepsByAbsPath.get(buildEntry) ?? [])
1187
+ deps.add(path6.resolve(dep));
1188
+ const sortedDeps = [...deps].sort();
1189
+ clientDepsByEntry[originalEntry] = sortedDeps;
1190
+ for (const dep of sortedDeps)
1191
+ clientDeps.add(dep);
1192
+ }
1193
+ return {
1194
+ manifestDelta,
1195
+ ssrManifestDelta: { moduleLoading: null, moduleMap: ssrModuleMap },
1196
+ newEntries: entries,
1197
+ discoveredEntries: discovered,
1198
+ clientDeps: [...clientDeps].sort(),
1199
+ clientDepsByEntry
1200
+ };
1201
+ }
1202
+ #emptyResult(discoveredEntries = []) {
1203
+ return {
1204
+ manifestDelta: {},
1205
+ ssrManifestDelta: { moduleLoading: null, moduleMap: {} },
1206
+ newEntries: [],
1207
+ discoveredEntries,
1208
+ clientDeps: [],
1209
+ clientDepsByEntry: {}
1210
+ };
1211
+ }
1212
+ async#buildBrowserBundle(bootstrapEntries) {
1213
+ const reactFastRefresh = process.env.AKAN_REACT_FAST_REFRESH !== "0";
1214
+ return new ClientEntriesBundler({
1215
+ app: this.#app,
1216
+ entries: bootstrapEntries.buildEntries,
1217
+ plugins: [
1218
+ await createBarrelImportsPlugin(this.#app, {
1219
+ pipeAfter: reactFastRefresh ? RouteClientBuilder.normalizeNamedDefaultFunctionForFastRefresh : undefined
1220
+ })
1221
+ ],
1222
+ external: VENDOR_SPECIFIERS,
1223
+ command: this.#command,
1224
+ reactFastRefresh
1225
+ }).bundle();
1226
+ }
1227
+ async#buildSsrBundle(bootstrapEntries) {
1228
+ const externalOptions = RouteClientBuilder.resolveSsrClientExternalOptions(this.#command);
1229
+ return new ClientEntriesBundler({
1230
+ app: this.#app,
1231
+ entries: bootstrapEntries.buildEntries,
1232
+ plugins: [await createBarrelImportsPlugin(this.#app)],
1233
+ ...externalOptions,
1234
+ outputSubdir: "client-ssr",
1235
+ command: this.#command
1236
+ }).bundle();
1237
+ }
1238
+ async#createBootstrapEntries(entries) {
1239
+ if (!await Bun.file(path6.join(this.#app.cwdPath, "lib", "st.ts")).exists()) {
1240
+ return { buildEntries: entries, originalByBuildEntry: new Map };
1241
+ }
1242
+ const outdir = path6.join(this.#app.cwdPath, ".akan", "generated", "client-entry-bootstrap");
1243
+ await mkdir(outdir, { recursive: true });
1244
+ const originalByBuildEntry = new Map;
1245
+ const buildEntries = await Promise.all(entries.map(async (entry) => {
1246
+ const absEntry = path6.resolve(entry);
1247
+ const hash = Bun.hash(`${this.#app.name}
1248
+ ${absEntry}`).toString(36);
1249
+ const base = path6.basename(absEntry).replace(/[^A-Za-z0-9._-]/g, "_");
1250
+ const wrapperEntry = path6.join(outdir, `${base}-${hash}.tsx`);
1251
+ const exportNames = await this.#scanExportNames(absEntry);
1252
+ await Bun.write(wrapperEntry, RouteClientBuilder.createStoreBootstrapEntrySource({
1253
+ appName: this.#app.name,
1254
+ originalEntry: absEntry,
1255
+ exportNames
1256
+ }));
1257
+ originalByBuildEntry.set(path6.resolve(wrapperEntry), absEntry);
1258
+ return wrapperEntry;
1259
+ }));
1260
+ return { buildEntries, originalByBuildEntry };
1261
+ }
1262
+ async#scanExportNames(absEntry) {
1263
+ const source = await Bun.file(absEntry).text();
1264
+ const transpiler = new Bun.Transpiler({ loader: this.#loaderFor(absEntry) });
1265
+ return transpiler.scan(source).exports;
1266
+ }
1267
+ #loaderFor(absPath) {
1268
+ if (absPath.endsWith(".tsx"))
1269
+ return "tsx";
1270
+ if (absPath.endsWith(".jsx"))
1271
+ return "jsx";
1272
+ if (absPath.endsWith(".ts"))
1273
+ return "ts";
1274
+ return "js";
1275
+ }
1276
+ static normalizeNamedDefaultFunctionForFastRefresh(source) {
1277
+ let changed = false;
1278
+ const defaultNames = [];
1279
+ const next = source.replace(/(^|\n)(\s*)export\s+default\s+(async\s+)?function\s+([A-Za-z_$][\w$]*)(?=\s*(?:<|\())/g, (match, lineStart, indent, asyncKeyword, name) => {
1280
+ changed = true;
1281
+ defaultNames.push(name);
1282
+ return `${lineStart}${indent}${asyncKeyword ?? ""}function ${name}`;
1283
+ });
1284
+ if (!changed)
1285
+ return null;
1286
+ return `${next}
1287
+ ${defaultNames.map((name) => `export default ${name};`).join(`
1288
+ `)}
1289
+ `;
1290
+ }
1291
+ static resolveSsrClientRuntimeAliases() {
1292
+ const serverEntry = RouteClientBuilder.resolveAkanServerEntry();
1293
+ return { [Bun.resolveSync("akanjs/fetch", serverEntry)]: "akanjs/fetch" };
1294
+ }
1295
+ static resolveSsrClientExternalOptions(command) {
1296
+ if (command === "start") {
1297
+ return {
1298
+ external: SSR_CLIENT_EXTERNALS,
1299
+ externalSubpaths: ["akanjs/fetch"],
1300
+ externalAliases: RouteClientBuilder.resolveSsrClientRuntimeAliases()
1301
+ };
1302
+ }
1303
+ return { external: SSR_CLIENT_ALIAS_EXTERNALS };
1304
+ }
1305
+ static resolveAkanServerEntry() {
1306
+ try {
1307
+ return Bun.resolveSync("akanjs/server", import.meta.dir);
1308
+ } catch {
1309
+ return path6.resolve(import.meta.dir, "../../../server/index.ts");
1310
+ }
1311
+ }
1312
+ static createStoreBootstrapEntrySource(args) {
1313
+ const originalEntry = JSON.stringify(path6.resolve(args.originalEntry));
1314
+ const namedExports = args.exportNames.filter((name) => name !== "default");
1315
+ const lines = [
1316
+ `import ${JSON.stringify(`@apps/${args.appName}/client`)};`,
1317
+ ...namedExports.length > 0 ? [`export { ${namedExports.join(", ")} } from ${originalEntry};`] : []
1318
+ ];
1319
+ if (args.exportNames.includes("default"))
1320
+ lines.push(`export { default } from ${originalEntry};`);
1321
+ return `${lines.join(`
1322
+ `)}
1323
+ `;
1324
+ }
1325
+ static resolveOriginalManifestEntry(manifestKey, originalByBuildEntry, clientReferenceIdByBuildEntry = new Map, workspaceRoot = process.cwd()) {
1326
+ const hashIdx = manifestKey.lastIndexOf("#");
1327
+ if (hashIdx < 0)
1328
+ return null;
1329
+ const buildReferenceId = manifestKey.slice(0, hashIdx);
1330
+ const name = manifestKey.slice(hashIdx + 1);
1331
+ const buildEntry = [...clientReferenceIdByBuildEntry.entries()].find(([, referenceId]) => referenceId === buildReferenceId)?.[0] ?? buildReferenceId;
1332
+ const originalEntry = originalByBuildEntry.get(buildEntry) ?? buildEntry;
1333
+ const originalReferenceId = toClientReferencePath(originalEntry, workspaceRoot);
1334
+ return { buildEntry, originalEntry, name, key: `${originalReferenceId}#${name}` };
1335
+ }
1336
+ }
1337
+
1338
+ // pkgs/@akanjs/devkit/frontendBuild/autoImportSync.ts
1339
+ import { readdir, readFile, stat, writeFile } from "fs/promises";
1340
+ import path7 from "path";
1341
+ import ts2 from "typescript";
1342
+ var TEST_FILE_RE = /\.(test|spec)\.(ts|tsx)$/;
1343
+ var AKAN_BASE = {
1344
+ names: ["Int", "Float", "ID", "Any", "Upload", "enumOf", "dayjs"],
1345
+ specifier: "akanjs/base",
1346
+ kind: "named"
1347
+ };
1348
+ var AKAN_BASE_TYPES = { names: ["Dayjs"], specifier: "akanjs/base", kind: "named", typeOnly: true };
1349
+ var CNST_NS = { names: ["cnst"], specifier: "../cnst", kind: "namespace" };
1350
+ var DB_NS = { names: ["db"], specifier: "../db", kind: "namespace" };
1351
+ var SRV_NS = { names: ["srv"], specifier: "../srv", kind: "namespace" };
1352
+ var ERR_DICT = { names: ["Err"], specifier: "../dict", kind: "named" };
1353
+ var RULES = {
1354
+ constant: [AKAN_BASE, AKAN_BASE_TYPES, { names: ["via"], specifier: "akanjs/constant", kind: "named" }],
1355
+ document: [
1356
+ AKAN_BASE,
1357
+ AKAN_BASE_TYPES,
1358
+ CNST_NS,
1359
+ DB_NS,
1360
+ ERR_DICT,
1361
+ {
1362
+ names: ["by", "from", "into", "SchemaOf", "DataInputOf", "DocumentUpdateHelper", "documentQueryHelper", "Mdl"],
1363
+ specifier: "akanjs/document",
1364
+ kind: "named"
1365
+ }
1366
+ ],
1367
+ service: [
1368
+ AKAN_BASE,
1369
+ AKAN_BASE_TYPES,
1370
+ DB_NS,
1371
+ SRV_NS,
1372
+ CNST_NS,
1373
+ ERR_DICT,
1374
+ { names: ["serve"], specifier: "akanjs/service", kind: "named" },
1375
+ {
1376
+ names: ["DataInputOf", "ListQueryOption", "DatabaseRegistry", "getFilterInfoByKey"],
1377
+ specifier: "akanjs/document",
1378
+ kind: "named"
1379
+ }
1380
+ ],
1381
+ signal: [
1382
+ AKAN_BASE,
1383
+ AKAN_BASE_TYPES,
1384
+ SRV_NS,
1385
+ CNST_NS,
1386
+ ERR_DICT,
1387
+ {
1388
+ names: ["endpoint", "internal", "slice", "Public", "Req", "Ws", "None", "Res"],
1389
+ specifier: "akanjs/signal",
1390
+ kind: "named"
1391
+ }
1392
+ ],
1393
+ dictionary: [
1394
+ {
1395
+ names: ["modelDictionary", "scalarDictionary", "serviceDictionary"],
1396
+ specifier: "akanjs/dictionary",
1397
+ kind: "named"
1398
+ }
1399
+ ],
1400
+ srvkit: [
1401
+ AKAN_BASE,
1402
+ AKAN_BASE_TYPES,
1403
+ { names: ["Logger", "HttpClient", "sleep"], specifier: "akanjs/common", kind: "named" },
1404
+ { names: ["adapt"], specifier: "akanjs/service", kind: "named" },
1405
+ { names: ["SignalContext", "Guard", "InternalArg", "Middleware"], specifier: "akanjs/signal", kind: "named" }
1406
+ ],
1407
+ common: [AKAN_BASE, AKAN_BASE_TYPES],
1408
+ store: [
1409
+ CNST_NS,
1410
+ { names: ["store"], specifier: "akanjs/store", kind: "named" },
1411
+ { names: ["fetch", "usePage", "sig"], specifier: "../useClient", kind: "named" },
1412
+ { names: ["st"], specifier: "../st", kind: "named" },
1413
+ { names: ["RootStore"], specifier: "../st", kind: "named", typeOnly: true }
1414
+ ],
1415
+ client: [
1416
+ {
1417
+ names: ["cnst", "fetch", "st", "usePage"],
1418
+ specifier: (ctx) => `@${ctx.scope}/${ctx.project}/client`,
1419
+ kind: "named"
1420
+ }
1421
+ ]
1422
+ };
1423
+ var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
1424
+ var DOMAIN_ROLE_KINDS = {
1425
+ constant: ["constant"],
1426
+ dictionary: ["constant", "document", "signal"],
1427
+ common: ["constant"]
1428
+ };
1429
+ var LIB_BARRELS = {
1430
+ Err: { barrel: "dict", kind: "named" },
1431
+ db: { barrel: "db", kind: "namespace" },
1432
+ cnst: { barrel: "cnst", kind: "namespace" },
1433
+ srv: { barrel: "srv", kind: "namespace" }
1434
+ };
1435
+ var DOMAIN_DENYLIST = new Set([
1436
+ "Map",
1437
+ "Set",
1438
+ "WeakMap",
1439
+ "WeakSet",
1440
+ "Date",
1441
+ "Promise",
1442
+ "Array",
1443
+ "Object",
1444
+ "Error",
1445
+ "TypeError",
1446
+ "RangeError",
1447
+ "RegExp",
1448
+ "Symbol",
1449
+ "Proxy",
1450
+ "Reflect",
1451
+ "JSON",
1452
+ "Math",
1453
+ "Number",
1454
+ "BigInt",
1455
+ "Function",
1456
+ "Boolean",
1457
+ "String",
1458
+ "Record",
1459
+ "Partial",
1460
+ "Required",
1461
+ "Readonly",
1462
+ "Pick",
1463
+ "Omit",
1464
+ "Exclude",
1465
+ "Extract",
1466
+ "NonNullable",
1467
+ "ReturnType",
1468
+ "Parameters",
1469
+ "Awaited",
1470
+ "InstanceType"
1471
+ ]);
1472
+
1473
+ class AutoImportSync {
1474
+ #workspaceRoot;
1475
+ #domainCache = new Map;
1476
+ constructor({ workspaceRoot }) {
1477
+ this.#workspaceRoot = path7.resolve(workspaceRoot);
1478
+ }
1479
+ async syncForBatch(files) {
1480
+ const changedFiles = [];
1481
+ const errors = [];
1482
+ const seen = new Set;
1483
+ for (const file of files) {
1484
+ const pkgRoot = this.#domainPkgRootOf(file);
1485
+ if (pkgRoot)
1486
+ this.#domainCache.delete(pkgRoot);
1487
+ }
1488
+ for (const file of files) {
1489
+ const abs = path7.resolve(file);
1490
+ if (seen.has(abs))
1491
+ continue;
1492
+ seen.add(abs);
1493
+ const ctx = this.#contextFor(abs);
1494
+ if (!ctx)
1495
+ continue;
1496
+ try {
1497
+ const changed = await this.#syncFile(abs, ctx);
1498
+ if (changed)
1499
+ changedFiles.push(abs);
1500
+ } catch (err) {
1501
+ errors.push(`[auto-import] sync failed for ${file}: ${formatError(err)}`);
1502
+ }
1503
+ }
1504
+ return { changedFiles, errors };
1505
+ }
1506
+ #contextFor(abs) {
1507
+ const rel = path7.relative(this.#workspaceRoot, abs);
1508
+ if (rel.startsWith("..") || path7.isAbsolute(rel))
1509
+ return null;
1510
+ const base = path7.basename(abs);
1511
+ if (TEST_FILE_RE.test(base) || base === "index.ts" || base === "index.tsx" || base.endsWith(".d.ts"))
1512
+ return null;
1513
+ const parts = rel.split(path7.sep).filter(Boolean);
1514
+ const [scope, project, facet] = parts;
1515
+ if (scope !== "apps" && scope !== "libs" || !project || !facet)
1516
+ return null;
1517
+ const role = roleFor(facet, base);
1518
+ if (!role)
1519
+ return null;
1520
+ return { role, scope, project };
1521
+ }
1522
+ async#syncFile(abs, ctx) {
1523
+ const stats = await stat(abs).catch(() => null);
1524
+ if (!stats?.isFile())
1525
+ return false;
1526
+ const source = await readFile(abs, "utf8");
1527
+ const resolveExtra = await this.#extraResolverFor(abs, ctx);
1528
+ const next = transformSource(source, abs, ctx, resolveExtra);
1529
+ if (next === null || next === source)
1530
+ return false;
1531
+ await writeFile(abs, next);
1532
+ return true;
1533
+ }
1534
+ async#extraResolverFor(abs, ctx) {
1535
+ const resolvers = [];
1536
+ if (ctx.role === "srvkit" || ctx.role === "common")
1537
+ resolvers.push(await this.#libBarrelResolver(abs, ctx));
1538
+ const kinds = DOMAIN_ROLE_KINDS[ctx.role];
1539
+ if (kinds)
1540
+ resolvers.push(await this.#domainResolver(abs, ctx, kinds));
1541
+ if (resolvers.length === 0)
1542
+ return;
1543
+ return (symbol) => {
1544
+ for (const resolve of resolvers) {
1545
+ const target = resolve(symbol);
1546
+ if (target)
1547
+ return target;
1548
+ }
1549
+ return null;
1550
+ };
1551
+ }
1552
+ async#domainResolver(abs, ctx, kinds) {
1553
+ const index = await this.#domainIndex(path7.join(this.#workspaceRoot, ctx.scope, ctx.project));
1554
+ const fileDir = path7.dirname(abs);
1555
+ return (symbol) => {
1556
+ if (!PASCAL_CASE_RE.test(symbol) || DOMAIN_DENYLIST.has(symbol))
1557
+ return null;
1558
+ const entries = (index.get(symbol) ?? []).filter((entry) => kinds.includes(entry.kind));
1559
+ if (entries.length !== 1)
1560
+ return null;
1561
+ return { specifier: relativeSpecifier(fileDir, entries[0].file), kind: "named" };
1562
+ };
1563
+ }
1564
+ async#libBarrelResolver(abs, ctx) {
1565
+ const fileDir = path7.dirname(abs);
1566
+ const libDir = path7.join(this.#workspaceRoot, ctx.scope, ctx.project, "lib");
1567
+ const available = new Map;
1568
+ for (const [symbol, { barrel, kind }] of Object.entries(LIB_BARRELS)) {
1569
+ if (await fileExists(path7.join(libDir, `${barrel}.ts`)))
1570
+ available.set(symbol, { specifier: relativeSpecifier(fileDir, path7.join(libDir, barrel)), kind });
1571
+ }
1572
+ return (symbol) => available.get(symbol) ?? null;
1573
+ }
1574
+ async#domainIndex(pkgRoot) {
1575
+ const cached = this.#domainCache.get(pkgRoot);
1576
+ if (cached)
1577
+ return cached;
1578
+ const index = await buildDomainIndex(path7.join(pkgRoot, "lib"));
1579
+ this.#domainCache.set(pkgRoot, index);
1580
+ return index;
1581
+ }
1582
+ #domainPkgRootOf(file) {
1583
+ const rel = path7.relative(this.#workspaceRoot, path7.resolve(file));
1584
+ if (rel.startsWith("..") || path7.isAbsolute(rel))
1585
+ return null;
1586
+ const [scope, project, facet] = rel.split(path7.sep).filter(Boolean);
1587
+ if (scope !== "apps" && scope !== "libs" || !project || facet !== "lib")
1588
+ return null;
1589
+ if (!domainKindOf(path7.basename(file)))
1590
+ return null;
1591
+ return path7.join(this.#workspaceRoot, scope, project);
1592
+ }
1593
+ }
1594
+ var roleFor = (facet, base) => {
1595
+ if (facet === "lib") {
1596
+ if (base.endsWith(".constant.ts"))
1597
+ return "constant";
1598
+ if (base.endsWith(".document.ts"))
1599
+ return "document";
1600
+ if (base.endsWith(".service.ts"))
1601
+ return "service";
1602
+ if (base.endsWith(".signal.ts"))
1603
+ return "signal";
1604
+ if (base.endsWith(".dictionary.ts"))
1605
+ return "dictionary";
1606
+ if (base.endsWith(".store.ts"))
1607
+ return "store";
1608
+ if (base.endsWith(".tsx"))
1609
+ return "client";
1610
+ return null;
1611
+ }
1612
+ if (facet === "ui" || facet === "page")
1613
+ return base.endsWith(".tsx") ? "client" : null;
1614
+ if (facet === "webkit")
1615
+ return base.endsWith(".ts") || base.endsWith(".tsx") ? "client" : null;
1616
+ if (facet === "srvkit")
1617
+ return base.endsWith(".ts") ? "srvkit" : null;
1618
+ if (facet === "common")
1619
+ return base.endsWith(".ts") || base.endsWith(".tsx") ? "common" : null;
1620
+ return null;
1621
+ };
1622
+ var targetFor = (symbol, ctx) => {
1623
+ for (const rule of RULES[ctx.role]) {
1624
+ if (!rule.names.includes(symbol))
1625
+ continue;
1626
+ const specifier = typeof rule.specifier === "function" ? rule.specifier(ctx) : rule.specifier;
1627
+ return { specifier, kind: rule.kind, typeOnly: rule.typeOnly };
1628
+ }
1629
+ return null;
1630
+ };
1631
+ var transformSource = (source, fileName, ctx, resolveExtra) => {
1632
+ const sf = ts2.createSourceFile(fileName, source, ts2.ScriptTarget.Latest, true, scriptKindFor(fileName));
1633
+ const bound = collectBoundNames(sf);
1634
+ const used = collectUsedReferences(sf);
1635
+ const namedBySpecifier = new Map;
1636
+ const namespaceImports = [];
1637
+ for (const name of used) {
1638
+ if (bound.has(name))
1639
+ continue;
1640
+ const target = targetFor(name, ctx) ?? resolveExtra?.(name) ?? null;
1641
+ if (!target)
1642
+ continue;
1643
+ if (target.kind === "namespace")
1644
+ namespaceImports.push({ name, specifier: target.specifier });
1645
+ else {
1646
+ const names = namedBySpecifier.get(target.specifier) ?? new Map;
1647
+ names.set(name, target.typeOnly ?? false);
1648
+ namedBySpecifier.set(target.specifier, names);
1649
+ }
1650
+ }
1651
+ if (namedBySpecifier.size === 0 && namespaceImports.length === 0)
1652
+ return null;
1653
+ const importDecls = sf.statements.filter(ts2.isImportDeclaration);
1654
+ const anchor = importDecls.at(-1) ?? null;
1655
+ const namedImportsBySpecifier = collectExistingNamedImports(importDecls);
1656
+ const edits = [];
1657
+ const newStatements = [];
1658
+ for (const [specifier, names] of namedBySpecifier) {
1659
+ const existing = namedImportsBySpecifier.get(specifier);
1660
+ if (existing) {
1661
+ const merged = new Map(existing.names);
1662
+ for (const [name, isType] of names)
1663
+ if (!merged.has(name))
1664
+ merged.set(name, isType);
1665
+ edits.push({
1666
+ start: existing.decl.getStart(sf),
1667
+ end: existing.decl.getEnd(),
1668
+ text: formatNamedImport(specifier, merged)
1669
+ });
1670
+ } else
1671
+ newStatements.push(formatNamedImport(specifier, names));
1672
+ }
1673
+ for (const ns of namespaceImports)
1674
+ newStatements.push(`import * as ${ns.name} from "${ns.specifier}";`);
1675
+ if (newStatements.length > 0) {
1676
+ const anchorEdit = anchor ? edits.find((edit) => edit.start === anchor.getStart(sf)) : undefined;
1677
+ if (anchorEdit)
1678
+ anchorEdit.text = `${anchorEdit.text}
1679
+ ${newStatements.join(`
1680
+ `)}`;
1681
+ else if (anchor)
1682
+ edits.push({ start: anchor.getEnd(), end: anchor.getEnd(), text: `
1683
+ ${newStatements.join(`
1684
+ `)}` });
1685
+ else {
1686
+ const prologueEnd = directivePrologueEnd(sf);
1687
+ if (prologueEnd >= 0)
1688
+ edits.push({ start: prologueEnd, end: prologueEnd, text: `
1689
+ ${newStatements.join(`
1690
+ `)}` });
1691
+ else
1692
+ edits.push({ start: 0, end: 0, text: `${newStatements.join(`
1693
+ `)}
1694
+
1695
+ ` });
1696
+ }
1697
+ }
1698
+ if (edits.length === 0)
1699
+ return null;
1700
+ edits.sort((a, b) => b.start - a.start);
1701
+ let out = source;
1702
+ for (const edit of edits)
1703
+ out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
1704
+ return out === source ? null : out;
1705
+ };
1706
+ var collectBoundNames = (sf) => {
1707
+ const names = new Set;
1708
+ const visit = (node) => {
1709
+ if (ts2.isImportClause(node)) {
1710
+ if (node.name)
1711
+ names.add(node.name.text);
1712
+ const bindings = node.namedBindings;
1713
+ if (bindings && ts2.isNamespaceImport(bindings))
1714
+ names.add(bindings.name.text);
1715
+ if (bindings && ts2.isNamedImports(bindings))
1716
+ for (const el of bindings.elements)
1717
+ names.add(el.name.text);
1718
+ }
1719
+ if ((ts2.isVariableDeclaration(node) || ts2.isFunctionDeclaration(node) || ts2.isClassDeclaration(node) || ts2.isParameter(node) || ts2.isBindingElement(node) || ts2.isEnumDeclaration(node) || ts2.isTypeAliasDeclaration(node) || ts2.isInterfaceDeclaration(node)) && node.name && ts2.isIdentifier(node.name))
1720
+ names.add(node.name.text);
1721
+ ts2.forEachChild(node, visit);
1722
+ };
1723
+ ts2.forEachChild(sf, visit);
1724
+ return names;
1725
+ };
1726
+ var collectUsedReferences = (sf) => {
1727
+ const used = new Set;
1728
+ const visit = (node) => {
1729
+ if (ts2.isIdentifier(node) && isReferencePosition(node))
1730
+ used.add(node.text);
1731
+ ts2.forEachChild(node, visit);
1732
+ };
1733
+ ts2.forEachChild(sf, visit);
1734
+ return used;
1735
+ };
1736
+ var isReferencePosition = (node) => {
1737
+ const parent = node.parent;
1738
+ if (!parent)
1739
+ return true;
1740
+ if (ts2.isPropertyAccessExpression(parent) && parent.name === node)
1741
+ return false;
1742
+ if (ts2.isQualifiedName(parent) && parent.right === node)
1743
+ return false;
1744
+ if (ts2.isPropertyAssignment(parent) && parent.name === node)
1745
+ return false;
1746
+ if (ts2.isPropertySignature(parent) && parent.name === node)
1747
+ return false;
1748
+ if (ts2.isBindingElement(parent) && parent.propertyName === node)
1749
+ return false;
1750
+ if (ts2.isImportSpecifier(parent) || ts2.isExportSpecifier(parent))
1751
+ return false;
1752
+ return true;
1753
+ };
1754
+ var collectExistingNamedImports = (importDecls) => {
1755
+ const map = new Map;
1756
+ for (const decl of importDecls) {
1757
+ const clause = decl.importClause;
1758
+ const bindings = clause?.namedBindings;
1759
+ if (!clause || !bindings || !ts2.isNamedImports(bindings))
1760
+ continue;
1761
+ if (!ts2.isStringLiteral(decl.moduleSpecifier))
1762
+ continue;
1763
+ const specifier = decl.moduleSpecifier.text;
1764
+ if (map.has(specifier))
1765
+ continue;
1766
+ const names = new Map;
1767
+ for (const el of bindings.elements)
1768
+ names.set(el.name.text, clause.isTypeOnly || el.isTypeOnly);
1769
+ map.set(specifier, { decl, names });
1770
+ }
1771
+ return map;
1772
+ };
1773
+ var formatNamedImport = (specifier, names) => {
1774
+ const entries = [...names.entries()].sort((a, b) => compareNames(a[0], b[0]));
1775
+ if (entries.every(([, isType]) => isType))
1776
+ return `import type { ${entries.map(([name]) => name).join(", ")} } from "${specifier}";`;
1777
+ const parts = entries.map(([name, isType]) => isType ? `type ${name}` : name);
1778
+ return `import { ${parts.join(", ")} } from "${specifier}";`;
1779
+ };
1780
+ var directivePrologueEnd = (sf) => {
1781
+ let end = -1;
1782
+ for (const stmt of sf.statements) {
1783
+ if (ts2.isExpressionStatement(stmt) && ts2.isStringLiteral(stmt.expression))
1784
+ end = stmt.getEnd();
1785
+ else
1786
+ break;
1787
+ }
1788
+ return end;
1789
+ };
1790
+ var scriptKindFor = (fileName) => fileName.endsWith(".tsx") ? ts2.ScriptKind.TSX : ts2.ScriptKind.TS;
1791
+ var compareNames = (a, b) => {
1792
+ const [la, lb] = [a.toLowerCase(), b.toLowerCase()];
1793
+ if (la !== lb)
1794
+ return la < lb ? -1 : 1;
1795
+ return a < b ? -1 : a > b ? 1 : 0;
1796
+ };
1797
+ var formatError = (err) => err instanceof Error ? err.message : String(err);
1798
+ var fileExists = async (file) => stat(file).then((s) => s.isFile()).catch(() => false);
1799
+ var domainKindOf = (base) => {
1800
+ if (base.endsWith(".constant.ts"))
1801
+ return "constant";
1802
+ if (base.endsWith(".document.ts"))
1803
+ return "document";
1804
+ if (base.endsWith(".signal.ts"))
1805
+ return "signal";
1806
+ return null;
1807
+ };
1808
+ var buildDomainIndex = async (libDir) => {
1809
+ const index = new Map;
1810
+ for (const file of await collectDomainFiles(libDir)) {
1811
+ const kind = domainKindOf(path7.basename(file));
1812
+ if (!kind)
1813
+ continue;
1814
+ const src = await readFile(file, "utf8").catch(() => null);
1815
+ if (src === null)
1816
+ continue;
1817
+ for (const name of exportedPascalNames(src, file)) {
1818
+ const entries = index.get(name) ?? [];
1819
+ entries.push({ file, kind });
1820
+ index.set(name, entries);
1821
+ }
1822
+ }
1823
+ return index;
1824
+ };
1825
+ var collectDomainFiles = async (dir) => {
1826
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
1827
+ const files = [];
1828
+ for (const entry of entries) {
1829
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
1830
+ continue;
1831
+ const full = path7.join(dir, entry.name);
1832
+ if (entry.isDirectory())
1833
+ files.push(...await collectDomainFiles(full));
1834
+ else if (domainKindOf(entry.name) && !TEST_FILE_RE.test(entry.name))
1835
+ files.push(full);
1836
+ }
1837
+ return files;
1838
+ };
1839
+ var exportedPascalNames = (source, fileName) => {
1840
+ const sf = ts2.createSourceFile(fileName, source, ts2.ScriptTarget.Latest, true);
1841
+ const isExported = (node) => ts2.canHaveModifiers(node) && (ts2.getModifiers(node) ?? []).some((m) => m.kind === ts2.SyntaxKind.ExportKeyword);
1842
+ const names = [];
1843
+ for (const stmt of sf.statements) {
1844
+ if ((ts2.isClassDeclaration(stmt) || ts2.isFunctionDeclaration(stmt) || ts2.isEnumDeclaration(stmt)) && stmt.name) {
1845
+ if (isExported(stmt))
1846
+ names.push(stmt.name.text);
1847
+ } else if (ts2.isVariableStatement(stmt) && isExported(stmt)) {
1848
+ for (const decl of stmt.declarationList.declarations)
1849
+ if (ts2.isIdentifier(decl.name))
1850
+ names.push(decl.name.text);
1851
+ } else if (ts2.isExportDeclaration(stmt) && stmt.exportClause && ts2.isNamedExports(stmt.exportClause)) {
1852
+ for (const el of stmt.exportClause.elements)
1853
+ names.push(el.name.text);
1854
+ }
1855
+ }
1856
+ return names.filter((name) => PASCAL_CASE_RE.test(name));
1857
+ };
1858
+ var relativeSpecifier = (fromDir, toFile) => {
1859
+ const rel = path7.relative(fromDir, toFile).replace(/\.tsx?$/, "").split(path7.sep).join("/");
1860
+ return rel.startsWith(".") ? rel : `./${rel}`;
1861
+ };
1862
+
1863
+ // pkgs/@akanjs/devkit/frontendBuild/devChangePlanner.ts
1864
+ import path8 from "path";
1865
+ var SOURCE_EXTS2 = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
1866
+ var CONFIG_BASENAMES = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
1867
+ var BARREL_FACETS = new Set(["common", "srvkit", "ui", "webkit", "plugin"]);
1868
+ var CLIENT_SUFFIXES = [".Template.tsx", ".Unit.tsx", ".Util.tsx", ".View.tsx", ".Zone.tsx", ".store.ts"];
1869
+ var SHARED_SUFFIXES = [".constant.ts", ".dictionary.ts", ".signal.ts"];
1870
+ var SERVER_SUFFIXES = [".service.ts", ".document.ts"];
1871
+ var RUNTIME_METADATA_BASENAMES = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
1872
+
1873
+ class DevChangePlanner {
1874
+ #workspaceRoot;
1875
+ constructor({ workspaceRoot }) {
1876
+ this.#workspaceRoot = path8.resolve(workspaceRoot);
1877
+ }
1878
+ plan({ generation, files, kinds, generatedFiles = [] }) {
1879
+ const fileList = uniqueResolved([...files, ...generatedFiles]);
1880
+ const generatedSet = new Set(generatedFiles.map((file) => path8.resolve(file)));
1881
+ const kindSet = new Set(kinds);
1882
+ const roles = new Set;
1883
+ const actions = new Set;
1884
+ const reasonByFile = {};
1885
+ for (const kind of kindSet) {
1886
+ if (kind === "css") {
1887
+ roles.add("css");
1888
+ actions.add("rebuild-css");
1889
+ }
1890
+ if (kind === "config") {
1891
+ roles.add("config");
1892
+ actions.add("restart-dev-host");
1893
+ }
1894
+ }
1895
+ for (const file of fileList) {
1896
+ const reasons = new Set;
1897
+ const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path8.resolve(file)), reasons });
1898
+ for (const role of fileRoles)
1899
+ roles.add(role);
1900
+ if (reasons.has("runtime-metadata"))
1901
+ actions.add("restart-builder");
1902
+ if (reasons.size > 0)
1903
+ reasonByFile[path8.resolve(file)] = [...reasons].sort();
1904
+ }
1905
+ if (roles.has("barrel"))
1906
+ actions.add("sync-generated");
1907
+ if (roles.has("server") || roles.has("shared"))
1908
+ actions.add("restart-backend");
1909
+ if (roles.has("client") || roles.has("shared"))
1910
+ actions.add("rebuild-client");
1911
+ if (roles.has("css"))
1912
+ actions.add("rebuild-css");
1913
+ return {
1914
+ generation,
1915
+ files: fileList,
1916
+ generatedFiles: uniqueResolved(generatedFiles),
1917
+ roles: [...roles].sort(),
1918
+ actions: [...actions].sort(),
1919
+ reasonByFile
1920
+ };
1921
+ }
1922
+ #rolesForFile(file, { isGenerated, reasons }) {
1923
+ const roles = new Set;
1924
+ const abs = path8.resolve(file);
1925
+ const base = path8.basename(abs);
1926
+ const ext = path8.extname(abs).toLowerCase();
1927
+ const isSource = SOURCE_EXTS2.has(ext);
1928
+ const rel = path8.relative(this.#workspaceRoot, abs);
1929
+ const parts = rel.split(path8.sep).filter(Boolean);
1930
+ if (CONFIG_BASENAMES.has(base)) {
1931
+ roles.add("config");
1932
+ reasons.add("config-file");
1933
+ }
1934
+ if (ext === ".css") {
1935
+ roles.add("css");
1936
+ reasons.add("css-file");
1937
+ }
1938
+ if (isGenerated || isSource && this.#isBarrelFacetChild(parts)) {
1939
+ roles.add("barrel");
1940
+ reasons.add(isGenerated ? "generated-index" : "barrel-facet-child");
1941
+ }
1942
+ if (isSource && this.#isServerBiased(abs, parts)) {
1943
+ roles.add("server");
1944
+ reasons.add("server-path");
1945
+ }
1946
+ if (isSource && this.#isClientBiased(abs, parts)) {
1947
+ roles.add("client");
1948
+ reasons.add("client-path");
1949
+ }
1950
+ if (isSource && this.#isSharedBiased(abs, parts)) {
1951
+ roles.add("shared");
1952
+ reasons.add("shared-path");
1953
+ }
1954
+ if (isSource && this.#isRuntimeMetadataFile(parts, base)) {
1955
+ reasons.add("runtime-metadata");
1956
+ }
1957
+ if (roles.has("server") && roles.has("client")) {
1958
+ roles.delete("server");
1959
+ roles.delete("client");
1960
+ roles.add("shared");
1961
+ reasons.add("server-client-overlap");
1962
+ }
1963
+ if (roles.size === 0 && SOURCE_EXTS2.has(ext) && this.#isWorkspaceSource(rel)) {
1964
+ roles.add("shared");
1965
+ reasons.add("workspace-source-fallback");
1966
+ }
1967
+ return roles;
1968
+ }
1969
+ #isServerBiased(abs, parts) {
1970
+ const base = path8.basename(abs);
1971
+ return parts.includes("srvkit") || SERVER_SUFFIXES.some((suffix) => base.endsWith(suffix)) || base === "main.ts" || base === "server.ts";
1972
+ }
1973
+ #isClientBiased(abs, parts) {
1974
+ const base = path8.basename(abs);
1975
+ return parts.includes("ui") || parts.includes("webkit") || parts.includes("page") || CLIENT_SUFFIXES.some((suffix) => base.endsWith(suffix));
1976
+ }
1977
+ #isSharedBiased(abs, parts) {
1978
+ const base = path8.basename(abs);
1979
+ return parts.includes("common") || SHARED_SUFFIXES.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES.has(base);
1980
+ }
1981
+ #isRuntimeMetadataFile(parts, base) {
1982
+ const parent = parts.at(-2);
1983
+ if (parent === "lib" && RUNTIME_METADATA_BASENAMES.has(base))
1984
+ return true;
1985
+ const libIndex = parts.lastIndexOf("lib");
1986
+ if (libIndex < 0 || parts.length <= libIndex + 1)
1987
+ return false;
1988
+ return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
1989
+ }
1990
+ #isBarrelFacetChild(parts) {
1991
+ if (parts.length < 4)
1992
+ return false;
1993
+ const [scope, , facet, child] = parts;
1994
+ if (scope !== "apps" && scope !== "libs")
1995
+ return false;
1996
+ if (!facet || !BARREL_FACETS.has(facet))
1997
+ return false;
1998
+ if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
1999
+ return false;
2000
+ return true;
2001
+ }
2002
+ #isWorkspaceSource(rel) {
2003
+ if (!rel || rel.startsWith("..") || path8.isAbsolute(rel))
2004
+ return false;
2005
+ const [scope] = rel.split(path8.sep);
2006
+ return scope === "apps" || scope === "libs" || scope === "pkgs";
2007
+ }
2008
+ }
2009
+ var uniqueResolved = (files) => [...new Set(files.map((file) => path8.resolve(file)))].sort();
2010
+
2011
+ // pkgs/@akanjs/devkit/frontendBuild/devGeneratedIndexSync.ts
2012
+ import { mkdir as mkdir2, readdir as readdir2, readFile as readFile2, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
2013
+ import path9 from "path";
2014
+ var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit", "plugin"]);
2015
+ var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
2016
+ var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
2017
+ var FACET_PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
2018
+ var FACET_CAMEL_CASE_RE = /^[a-z][A-Za-z0-9]*$/;
2019
+ var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
2020
+ var SERVICE_UI_TYPES = ["Util", "Zone"];
2021
+ var SCALAR_UI_TYPES = ["Template", "Unit"];
2022
+
2023
+ class DevGeneratedIndexSync {
2024
+ #workspaceRoot;
2025
+ constructor({ workspaceRoot }) {
2026
+ this.#workspaceRoot = path9.resolve(workspaceRoot);
2027
+ }
2028
+ async syncForBatch(files) {
2029
+ const indexPaths = new Set;
2030
+ const errors = [];
2031
+ for (const file of files) {
2032
+ const facetIndex = this.#facetIndexFor(file);
2033
+ if (facetIndex)
2034
+ indexPaths.add(facetIndex);
2035
+ const moduleIndex = await this.#moduleIndexForDirectoryEvent(file).catch((err) => {
2036
+ errors.push(`[generated-index] module detection failed for ${file}: ${formatError2(err)}`);
2037
+ return null;
2038
+ });
2039
+ if (moduleIndex)
2040
+ indexPaths.add(moduleIndex);
2041
+ }
2042
+ const changedFiles = [];
2043
+ for (const indexPath of [...indexPaths].sort()) {
2044
+ try {
2045
+ const changed = await this.#syncIndex(indexPath);
2046
+ if (changed)
2047
+ changedFiles.push(indexPath);
2048
+ } catch (err) {
2049
+ errors.push(`[generated-index] sync failed for ${indexPath}: ${formatError2(err)}`);
2050
+ }
2051
+ }
2052
+ return { changedFiles, errors };
2053
+ }
2054
+ #facetIndexFor(file) {
2055
+ const abs = path9.resolve(file);
2056
+ const rel = path9.relative(this.#workspaceRoot, abs);
2057
+ if (rel.startsWith("..") || path9.isAbsolute(rel))
2058
+ return null;
2059
+ const parts = rel.split(path9.sep).filter(Boolean);
2060
+ if (parts.length < 4)
2061
+ return null;
2062
+ const [scope, project, facet, child] = parts;
2063
+ if (scope !== "apps" && scope !== "libs" || !project || !facet || !BARREL_FACETS2.has(facet))
2064
+ return null;
2065
+ if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
2066
+ return null;
2067
+ return path9.join(this.#workspaceRoot, scope, project, facet, "index.ts");
2068
+ }
2069
+ async#moduleIndexForDirectoryEvent(file) {
2070
+ const abs = path9.resolve(file);
2071
+ const rel = path9.relative(this.#workspaceRoot, abs);
2072
+ if (rel.startsWith("..") || path9.isAbsolute(rel))
2073
+ return null;
2074
+ const parts = rel.split(path9.sep).filter(Boolean);
2075
+ if (parts.length !== 4 && parts.length !== 5)
2076
+ return null;
2077
+ const [scope, project, libSegment, moduleSegment, scalarSegment] = parts;
2078
+ if (scope !== "apps" && scope !== "libs" || !project || libSegment !== "lib")
2079
+ return null;
2080
+ const isExistingDirectory = await stat2(abs).then((s) => s.isDirectory()).catch(() => false);
2081
+ const looksLikeDeletedDirectory = !path9.extname(abs);
2082
+ if (!isExistingDirectory && !looksLikeDeletedDirectory)
2083
+ return null;
2084
+ if (moduleSegment === "__scalar" && scalarSegment) {
2085
+ return path9.join(this.#workspaceRoot, scope, project, "lib", "__scalar", scalarSegment, "index.ts");
2086
+ }
2087
+ if (!moduleSegment || moduleSegment === "__scalar")
2088
+ return null;
2089
+ return path9.join(this.#workspaceRoot, scope, project, "lib", moduleSegment, "index.ts");
2090
+ }
2091
+ async#syncIndex(indexPath) {
2092
+ const dir = path9.dirname(indexPath);
2093
+ const content = await this.#contentForIndex(indexPath);
2094
+ if (content === null) {
2095
+ if (!await exists(indexPath))
2096
+ return false;
2097
+ await rm(indexPath, { force: true });
2098
+ return true;
2099
+ }
2100
+ const current = await readFile2(indexPath, "utf8").catch(() => null);
2101
+ if (current === content)
2102
+ return false;
2103
+ await mkdir2(dir, { recursive: true });
2104
+ await writeFile2(indexPath, content);
2105
+ return true;
2106
+ }
2107
+ async#contentForIndex(indexPath) {
2108
+ const parts = path9.relative(this.#workspaceRoot, indexPath).split(path9.sep).filter(Boolean);
2109
+ const facet = parts.at(-2);
2110
+ if (facet && BARREL_FACETS2.has(facet))
2111
+ return this.#facetContent(path9.dirname(indexPath));
2112
+ return this.#moduleContent(path9.dirname(indexPath));
2113
+ }
2114
+ async#facetContent(dir) {
2115
+ const nameCasePattern = path9.basename(dir) === "ui" ? FACET_PASCAL_CASE_RE : FACET_CAMEL_CASE_RE;
2116
+ const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
2117
+ const exportNames = entries.flatMap((entry) => {
2118
+ const name = entry.name;
2119
+ if (name.startsWith("."))
2120
+ return [];
2121
+ if (entry.isDirectory())
2122
+ return nameCasePattern.test(name) ? [name] : [];
2123
+ if (!entry.isFile())
2124
+ return [];
2125
+ if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
2126
+ return [];
2127
+ const exportName = name.replace(FACET_SOURCE_FILE_RE, "");
2128
+ return nameCasePattern.test(exportName) ? [exportName] : [];
2129
+ }).sort();
2130
+ if (exportNames.length === 0)
2131
+ return null;
2132
+ return `${exportNames.map((name) => `export * from "./${name}";`).join(`
2133
+ `)}
2134
+ `;
2135
+ }
2136
+ async#moduleContent(dir) {
2137
+ const rel = path9.relative(this.#workspaceRoot, dir);
2138
+ const parts = rel.split(path9.sep).filter(Boolean);
2139
+ const moduleSegment = parts.at(-1);
2140
+ if (!moduleSegment)
2141
+ return null;
2142
+ const isScalar = parts.at(-2) === "__scalar";
2143
+ const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
2144
+ if (!rawModel)
2145
+ return null;
2146
+ const modelName = capitalize(rawModel);
2147
+ const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
2148
+ const fileTypes = [];
2149
+ for (const type of allowedTypes) {
2150
+ if (await exists(path9.join(dir, `${modelName}.${type}.tsx`)))
2151
+ fileTypes.push(type);
2152
+ }
2153
+ if (fileTypes.length === 0)
2154
+ return null;
2155
+ return `
2156
+ ${fileTypes.map((type) => `import * as ${type} from "./${modelName}.${type}";`).join(`
2157
+ `)}
2158
+
2159
+ export const ${modelName} = { ${fileTypes.join(", ")} };`;
2160
+ }
2161
+ }
2162
+ var exists = async (file) => stat2(file).then(() => true).catch(() => false);
2163
+ var capitalize = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
2164
+ var formatError2 = (err) => err instanceof Error ? err.message : String(err);
2165
+
2166
+ export { BarrelAnalyzer, createBarrelImportsPlugin, createTsconfigPackageResolver, rewriteBarrelImports, GraphClientEntryDiscovery, transformUseClient, ClientEntriesBundler, VENDOR_SPECIFIERS, RouteClientBuilder, AutoImportSync, DevChangePlanner, DevGeneratedIndexSync };