@glasstrace/sdk 0.14.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -1
- package/dist/adapters/drizzle.js +2 -5
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/{chunk-PD2SKFQQ.js → chunk-55FBXXER.js} +4 -8
- package/dist/{chunk-PD2SKFQQ.js.map → chunk-55FBXXER.js.map} +1 -1
- package/dist/chunk-5C2TJFLB.js +851 -0
- package/dist/chunk-5C2TJFLB.js.map +1 -0
- package/dist/{chunk-YMEXDDTA.js → chunk-7JBKXSBU.js} +3 -99
- package/dist/chunk-7JBKXSBU.js.map +1 -0
- package/dist/{chunk-2LDBR3F3.js → chunk-BANTDXUT.js} +15 -74
- package/dist/chunk-BANTDXUT.js.map +1 -0
- package/dist/{chunk-WV3NIPWJ.js → chunk-CTJI2YKA.js} +23 -288
- package/dist/chunk-CTJI2YKA.js.map +1 -0
- package/dist/{chunk-WK7MPK2T.js → chunk-DQ25VOKK.js} +1 -89
- package/dist/chunk-DQ25VOKK.js.map +1 -0
- package/dist/{chunk-BL3YDC6V.js → chunk-DXRZKKSO.js} +1 -6
- package/dist/{chunk-BL3YDC6V.js.map → chunk-DXRZKKSO.js.map} +1 -1
- package/dist/{chunk-BGZ7J74D.js → chunk-NSBPE2FW.js} +2 -16
- package/dist/chunk-O63DJKIJ.js +460 -0
- package/dist/chunk-O63DJKIJ.js.map +1 -0
- package/dist/{chunk-ECEN724Y.js → chunk-TM5NKZTO.js} +4 -8
- package/dist/{chunk-ECEN724Y.js.map → chunk-TM5NKZTO.js.map} +1 -1
- package/dist/chunk-VUZCLMIX.js +57 -0
- package/dist/chunk-VUZCLMIX.js.map +1 -0
- package/dist/{chunk-OSXIUKD5.js → chunk-WZXVS2EO.js} +1 -6
- package/dist/{chunk-OSXIUKD5.js.map → chunk-WZXVS2EO.js.map} +1 -1
- package/dist/{chunk-ARAOZCZT.js → chunk-XNDHQN4S.js} +122 -24
- package/dist/chunk-XNDHQN4S.js.map +1 -0
- package/dist/cli/init.cjs +1110 -255
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +86 -1
- package/dist/cli/init.d.ts +86 -1
- package/dist/cli/init.js +277 -66
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +16 -16
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +12 -13
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/status.cjs +2 -2
- package/dist/cli/status.js +4 -7
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/uninit.cjs +138 -20
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.d.cts +38 -8
- package/dist/cli/uninit.d.ts +38 -8
- package/dist/cli/uninit.js +8 -5
- package/dist/cli/validate.cjs +135 -0
- package/dist/cli/validate.cjs.map +1 -0
- package/dist/cli/validate.d.cts +60 -0
- package/dist/cli/validate.d.ts +60 -0
- package/dist/cli/validate.js +100 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/{esm-MDK7CZID.js → esm-KBPHCVB4.js} +3 -3
- package/dist/{getMachineId-bsd-4NIRBWME.js → getMachineId-bsd-345PYXFX.js} +4 -7
- package/dist/{getMachineId-bsd-4NIRBWME.js.map → getMachineId-bsd-345PYXFX.js.map} +1 -1
- package/dist/{getMachineId-darwin-2XNOCCJQ.js → getMachineId-darwin-5L2D25AD.js} +4 -7
- package/dist/{getMachineId-darwin-2XNOCCJQ.js.map → getMachineId-darwin-5L2D25AD.js.map} +1 -1
- package/dist/{getMachineId-linux-V6YSQEY7.js → getMachineId-linux-KJR4P5HN.js} +3 -6
- package/dist/{getMachineId-linux-V6YSQEY7.js.map → getMachineId-linux-KJR4P5HN.js.map} +1 -1
- package/dist/{getMachineId-unsupported-4FKBJNVO.js → getMachineId-unsupported-NDNXDYDY.js} +3 -6
- package/dist/{getMachineId-unsupported-4FKBJNVO.js.map → getMachineId-unsupported-NDNXDYDY.js.map} +1 -1
- package/dist/{getMachineId-win-WLRZBKVG.js → getMachineId-win-T7PJNJXG.js} +4 -7
- package/dist/{getMachineId-win-WLRZBKVG.js.map → getMachineId-win-T7PJNJXG.js.map} +1 -1
- package/dist/index.cjs +519 -494
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -6
- package/dist/index.d.ts +47 -6
- package/dist/index.js +250 -719
- package/dist/index.js.map +1 -1
- package/dist/{monorepo-YILKGQXQ.js → monorepo-N5Z63XP7.js} +4 -4
- package/dist/{source-map-uploader-3GWUQDTS.js → source-map-uploader-MUZPI2S5.js} +5 -4
- package/dist/source-map-uploader-MUZPI2S5.js.map +1 -0
- package/package.json +6 -4
- package/dist/chunk-2LDBR3F3.js.map +0 -1
- package/dist/chunk-ARAOZCZT.js.map +0 -1
- package/dist/chunk-BGZ7J74D.js.map +0 -1
- package/dist/chunk-UPS5BGER.js +0 -182
- package/dist/chunk-UPS5BGER.js.map +0 -1
- package/dist/chunk-WK7MPK2T.js.map +0 -1
- package/dist/chunk-WV3NIPWJ.js.map +0 -1
- package/dist/chunk-YMEXDDTA.js.map +0 -1
- /package/dist/{esm-MDK7CZID.js.map → chunk-NSBPE2FW.js.map} +0 -0
- /package/dist/{monorepo-YILKGQXQ.js.map → esm-KBPHCVB4.js.map} +0 -0
- /package/dist/{source-map-uploader-3GWUQDTS.js.map → monorepo-N5Z63XP7.js.map} +0 -0
package/README.md
CHANGED
|
@@ -6,7 +6,90 @@ them to coding agents through an MCP server and live dashboard.
|
|
|
6
6
|
|
|
7
7
|
> **Status: Pre-release** -- not yet published to npm.
|
|
8
8
|
|
|
9
|
-
See the [monorepo README](../../README.md) for the
|
|
9
|
+
See the [monorepo README](../../README.md) for the full API overview,
|
|
10
|
+
including the [Coexistence with Other OTel Tools](../../README.md#coexistence-with-other-otel-tools)
|
|
11
|
+
section which documents automatic span-processor attachment onto a
|
|
12
|
+
pre-registered OTel provider (Sentry, Datadog, Next.js 16 production)
|
|
13
|
+
and manual integration via `createGlasstraceSpanProcessor()`.
|
|
14
|
+
|
|
15
|
+
## Initialize
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx glasstrace init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The `init` command scaffolds the files Glasstrace needs and merges into
|
|
22
|
+
your existing setup rather than overwriting.
|
|
23
|
+
|
|
24
|
+
### Instrumentation file precedence
|
|
25
|
+
|
|
26
|
+
Init picks the first matching location:
|
|
27
|
+
|
|
28
|
+
1. An existing `src/instrumentation.{ts,js,mjs}` — the user has already
|
|
29
|
+
committed to this location, so merge there.
|
|
30
|
+
2. An existing `instrumentation.{ts,js,mjs}` at the project root — same
|
|
31
|
+
rationale.
|
|
32
|
+
3. A new `src/instrumentation.ts` when the project contains a `src/`
|
|
33
|
+
directory at its root (the common Next.js convention).
|
|
34
|
+
4. A new `instrumentation.ts` at the project root.
|
|
35
|
+
|
|
36
|
+
Next.js only loads instrumentation from one of the two locations —
|
|
37
|
+
scaffolding to the wrong one silently prevents the SDK from starting,
|
|
38
|
+
so the layout is resolved automatically.
|
|
39
|
+
|
|
40
|
+
### Merge into existing instrumentation
|
|
41
|
+
|
|
42
|
+
When an instrumentation file already exists, init merges instead of
|
|
43
|
+
overwriting:
|
|
44
|
+
|
|
45
|
+
- If the file exports a `register()` function, init inserts
|
|
46
|
+
`registerGlasstrace()` as the first statement of the existing body
|
|
47
|
+
and imports `registerGlasstrace` at the top of the file.
|
|
48
|
+
- If the file has no `register()` function (for example, it only
|
|
49
|
+
contains a top-level Sentry import), init appends a new
|
|
50
|
+
`export async function register()` that calls `registerGlasstrace()`.
|
|
51
|
+
- If `registerGlasstrace()` is already present, init is a no-op.
|
|
52
|
+
|
|
53
|
+
Before modifying an existing file, init prompts for confirmation. Pass
|
|
54
|
+
`--force` (or `--yes`) to skip the prompt in automated environments.
|
|
55
|
+
|
|
56
|
+
### Both-layout conflict
|
|
57
|
+
|
|
58
|
+
If both `instrumentation.ts` (root) and `src/instrumentation.ts` exist,
|
|
59
|
+
init exits non-zero without modifying either file. Next.js's loader
|
|
60
|
+
behavior is undefined when both are present — it loads one and ignores
|
|
61
|
+
the other. Merge your code into `src/instrumentation.ts`, delete the
|
|
62
|
+
root file, then re-run init.
|
|
63
|
+
|
|
64
|
+
## Init & Verification
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx glasstrace init
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`glasstrace init` scaffolds instrumentation, configures MCP, and
|
|
71
|
+
verifies server-side registration of the anonymous key before
|
|
72
|
+
reporting success. The verification step uses `node:https` directly —
|
|
73
|
+
bypassing any `fetch` patching introduced by Next.js 16 — so a silent
|
|
74
|
+
init-hang cannot leave your installation in a broken state.
|
|
75
|
+
|
|
76
|
+
| Exit code | Meaning |
|
|
77
|
+
|-----------|---------|
|
|
78
|
+
| `0` | Scaffolding succeeded AND the server confirmed the anon key. |
|
|
79
|
+
| `1` | Scaffolding failed. No verification attempted. |
|
|
80
|
+
| `2` | Scaffolding succeeded but server verification failed. Safe to re-run. |
|
|
81
|
+
|
|
82
|
+
On a non-zero verification exit, the error message distinguishes three
|
|
83
|
+
classes so you can act on them:
|
|
84
|
+
|
|
85
|
+
- `fetch failed: <reason>` — transport error (DNS, TCP, TLS, timeout).
|
|
86
|
+
- `server rejected the key (HTTP <status>)` — 4xx/5xx status.
|
|
87
|
+
- `server returned malformed response` — 2xx with unparseable body.
|
|
88
|
+
|
|
89
|
+
Transport errors are retried twice (500 ms + 1500 ms backoff, 20-second
|
|
90
|
+
total cap). HTTP 4xx/5xx and malformed responses are surfaced
|
|
91
|
+
immediately. Set `GLASSTRACE_SKIP_INIT_VERIFY=1` to skip verification
|
|
92
|
+
for offline installs.
|
|
10
93
|
|
|
11
94
|
## License
|
|
12
95
|
|
package/dist/adapters/drizzle.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SpanKind,
|
|
3
3
|
trace
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import
|
|
6
|
-
init_esm_shims
|
|
7
|
-
} from "../chunk-BGZ7J74D.js";
|
|
4
|
+
} from "../chunk-DQ25VOKK.js";
|
|
5
|
+
import "../chunk-NSBPE2FW.js";
|
|
8
6
|
|
|
9
7
|
// src/adapters/drizzle.ts
|
|
10
|
-
init_esm_shims();
|
|
11
8
|
function extractOperation(query) {
|
|
12
9
|
const trimmed = query.trimStart().toUpperCase();
|
|
13
10
|
if (trimmed.startsWith("SELECT")) return "SELECT";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/drizzle.ts"],"sourcesContent":["import { trace, SpanKind, type Tracer } from \"@opentelemetry/api\";\n\n/**\n * Options for the Glasstrace Drizzle logger.\n */\nexport interface GlasstraceDrizzleLoggerOptions {\n /** Whether to capture query parameters. Defaults to false (safe default). */\n captureParams?: boolean;\n}\n\n/**\n * Extracts the SQL operation (SELECT, INSERT, UPDATE, DELETE) from a query.\n * Returns 'unknown' if the operation cannot be determined.\n */\nfunction extractOperation(query: string): string {\n const trimmed = query.trimStart().toUpperCase();\n if (trimmed.startsWith(\"SELECT\")) return \"SELECT\";\n if (trimmed.startsWith(\"INSERT\")) return \"INSERT\";\n if (trimmed.startsWith(\"UPDATE\")) return \"UPDATE\";\n if (trimmed.startsWith(\"DELETE\")) return \"DELETE\";\n return \"unknown\";\n}\n\n/**\n * Extracts the table name from a SQL query using best-effort regex.\n * Returns undefined if the table cannot be determined.\n */\nfunction extractTable(query: string): string | undefined {\n // FROM table_name (SELECT, DELETE)\n const fromMatch = /\\bFROM\\s+[\"'`]?(\\w+)[\"'`]?/i.exec(query);\n if (fromMatch) return fromMatch[1];\n\n // INSERT INTO table_name\n const insertMatch = /\\bINSERT\\s+INTO\\s+[\"'`]?(\\w+)[\"'`]?/i.exec(query);\n if (insertMatch) return insertMatch[1];\n\n // UPDATE table_name\n const updateMatch = /\\bUPDATE\\s+[\"'`]?(\\w+)[\"'`]?/i.exec(query);\n if (updateMatch) return updateMatch[1];\n\n return undefined;\n}\n\n/**\n * Implements Drizzle's Logger interface to create OTel spans for Drizzle queries.\n *\n * Exported via `@glasstrace/sdk/drizzle` subpath to avoid bundling Drizzle\n * for Prisma-only users.\n *\n * When OTel is not initialized, tracer.startSpan() returns a no-op span\n * and the logger still executes without errors.\n */\nexport class GlasstraceDrizzleLogger {\n private readonly tracer: Tracer;\n private readonly captureParams: boolean;\n\n constructor(options?: GlasstraceDrizzleLoggerOptions) {\n this.tracer = trace.getTracer(\"glasstrace-drizzle\");\n this.captureParams = options?.captureParams ?? false;\n }\n\n /**\n * Called by Drizzle ORM for each query execution.\n * Creates an OTel span with query metadata.\n */\n logQuery(query: string, params: unknown[]): void {\n const operation = extractOperation(query);\n const spanName =\n operation === \"unknown\" ? \"drizzle.query\" : `drizzle.${operation}`;\n\n const span = this.tracer.startSpan(spanName, {\n kind: SpanKind.CLIENT,\n attributes: {\n \"db.system\": \"drizzle\",\n \"db.statement\": query,\n \"db.operation\": operation,\n \"glasstrace.orm.provider\": \"drizzle\",\n },\n });\n\n // Table extraction\n const table = extractTable(query);\n if (table !== undefined) {\n span.setAttribute(\"db.sql.table\", table);\n }\n\n // Param handling\n if (this.captureParams) {\n try {\n span.setAttribute(\"db.sql.params\", JSON.stringify(params));\n } catch {\n span.setAttribute(\"db.sql.params\", \"[serialization_error]\");\n }\n } else {\n span.setAttribute(\"db.sql.params\", \"[REDACTED]\");\n }\n\n span.end();\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/drizzle.ts"],"sourcesContent":["import { trace, SpanKind, type Tracer } from \"@opentelemetry/api\";\n\n/**\n * Options for the Glasstrace Drizzle logger.\n */\nexport interface GlasstraceDrizzleLoggerOptions {\n /** Whether to capture query parameters. Defaults to false (safe default). */\n captureParams?: boolean;\n}\n\n/**\n * Extracts the SQL operation (SELECT, INSERT, UPDATE, DELETE) from a query.\n * Returns 'unknown' if the operation cannot be determined.\n */\nfunction extractOperation(query: string): string {\n const trimmed = query.trimStart().toUpperCase();\n if (trimmed.startsWith(\"SELECT\")) return \"SELECT\";\n if (trimmed.startsWith(\"INSERT\")) return \"INSERT\";\n if (trimmed.startsWith(\"UPDATE\")) return \"UPDATE\";\n if (trimmed.startsWith(\"DELETE\")) return \"DELETE\";\n return \"unknown\";\n}\n\n/**\n * Extracts the table name from a SQL query using best-effort regex.\n * Returns undefined if the table cannot be determined.\n */\nfunction extractTable(query: string): string | undefined {\n // FROM table_name (SELECT, DELETE)\n const fromMatch = /\\bFROM\\s+[\"'`]?(\\w+)[\"'`]?/i.exec(query);\n if (fromMatch) return fromMatch[1];\n\n // INSERT INTO table_name\n const insertMatch = /\\bINSERT\\s+INTO\\s+[\"'`]?(\\w+)[\"'`]?/i.exec(query);\n if (insertMatch) return insertMatch[1];\n\n // UPDATE table_name\n const updateMatch = /\\bUPDATE\\s+[\"'`]?(\\w+)[\"'`]?/i.exec(query);\n if (updateMatch) return updateMatch[1];\n\n return undefined;\n}\n\n/**\n * Implements Drizzle's Logger interface to create OTel spans for Drizzle queries.\n *\n * Exported via `@glasstrace/sdk/drizzle` subpath to avoid bundling Drizzle\n * for Prisma-only users.\n *\n * When OTel is not initialized, tracer.startSpan() returns a no-op span\n * and the logger still executes without errors.\n */\nexport class GlasstraceDrizzleLogger {\n private readonly tracer: Tracer;\n private readonly captureParams: boolean;\n\n constructor(options?: GlasstraceDrizzleLoggerOptions) {\n this.tracer = trace.getTracer(\"glasstrace-drizzle\");\n this.captureParams = options?.captureParams ?? false;\n }\n\n /**\n * Called by Drizzle ORM for each query execution.\n * Creates an OTel span with query metadata.\n */\n logQuery(query: string, params: unknown[]): void {\n const operation = extractOperation(query);\n const spanName =\n operation === \"unknown\" ? \"drizzle.query\" : `drizzle.${operation}`;\n\n const span = this.tracer.startSpan(spanName, {\n kind: SpanKind.CLIENT,\n attributes: {\n \"db.system\": \"drizzle\",\n \"db.statement\": query,\n \"db.operation\": operation,\n \"glasstrace.orm.provider\": \"drizzle\",\n },\n });\n\n // Table extraction\n const table = extractTable(query);\n if (table !== undefined) {\n span.setAttribute(\"db.sql.table\", table);\n }\n\n // Param handling\n if (this.captureParams) {\n try {\n span.setAttribute(\"db.sql.params\", JSON.stringify(params));\n } catch {\n span.setAttribute(\"db.sql.params\", \"[serialization_error]\");\n }\n } else {\n span.setAttribute(\"db.sql.params\", \"[REDACTED]\");\n }\n\n span.end();\n }\n}\n"],"mappings":";;;;;;;AAcA,SAAS,iBAAiB,OAAuB;AAC/C,QAAM,UAAU,MAAM,UAAU,EAAE,YAAY;AAC9C,MAAI,QAAQ,WAAW,QAAQ,EAAG,QAAO;AACzC,MAAI,QAAQ,WAAW,QAAQ,EAAG,QAAO;AACzC,MAAI,QAAQ,WAAW,QAAQ,EAAG,QAAO;AACzC,MAAI,QAAQ,WAAW,QAAQ,EAAG,QAAO;AACzC,SAAO;AACT;AAMA,SAAS,aAAa,OAAmC;AAEvD,QAAM,YAAY,8BAA8B,KAAK,KAAK;AAC1D,MAAI,UAAW,QAAO,UAAU,CAAC;AAGjC,QAAM,cAAc,uCAAuC,KAAK,KAAK;AACrE,MAAI,YAAa,QAAO,YAAY,CAAC;AAGrC,QAAM,cAAc,gCAAgC,KAAK,KAAK;AAC9D,MAAI,YAAa,QAAO,YAAY,CAAC;AAErC,SAAO;AACT;AAWO,IAAM,0BAAN,MAA8B;AAAA,EAClB;AAAA,EACA;AAAA,EAEjB,YAAY,SAA0C;AACpD,SAAK,SAAS,MAAM,UAAU,oBAAoB;AAClD,SAAK,gBAAgB,SAAS,iBAAiB;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAAe,QAAyB;AAC/C,UAAM,YAAY,iBAAiB,KAAK;AACxC,UAAM,WACJ,cAAc,YAAY,kBAAkB,WAAW,SAAS;AAElE,UAAM,OAAO,KAAK,OAAO,UAAU,UAAU;AAAA,MAC3C,MAAM,SAAS;AAAA,MACf,YAAY;AAAA,QACV,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,gBAAgB;AAAA,QAChB,2BAA2B;AAAA,MAC7B;AAAA,IACF,CAAC;AAGD,UAAM,QAAQ,aAAa,KAAK;AAChC,QAAI,UAAU,QAAW;AACvB,WAAK,aAAa,gBAAgB,KAAK;AAAA,IACzC;AAGA,QAAI,KAAK,eAAe;AACtB,UAAI;AACF,aAAK,aAAa,iBAAiB,KAAK,UAAU,MAAM,CAAC;AAAA,MAC3D,QAAQ;AACN,aAAK,aAAa,iBAAiB,uBAAuB;AAAA,MAC5D;AAAA,IACF,OAAO;AACL,WAAK,aAAa,iBAAiB,YAAY;AAAA,IACjD;AAEA,SAAK,IAAI;AAAA,EACX;AACF;","names":[]}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NEXT_CONFIG_NAMES
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
init_esm_shims
|
|
6
|
-
} from "./chunk-BGZ7J74D.js";
|
|
3
|
+
} from "./chunk-DXRZKKSO.js";
|
|
7
4
|
|
|
8
5
|
// src/cli/monorepo.ts
|
|
9
|
-
|
|
10
|
-
import * as
|
|
11
|
-
import * as path from "path";
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as path from "node:path";
|
|
12
8
|
function resolveProjectRoot(cwd) {
|
|
13
9
|
if (hasNextConfig(cwd)) {
|
|
14
10
|
return { projectRoot: cwd, isMonorepo: false };
|
|
@@ -243,4 +239,4 @@ export {
|
|
|
243
239
|
findNextJsApps,
|
|
244
240
|
parsePnpmWorkspaceYaml
|
|
245
241
|
};
|
|
246
|
-
//# sourceMappingURL=chunk-
|
|
242
|
+
//# sourceMappingURL=chunk-55FBXXER.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/monorepo.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { NEXT_CONFIG_NAMES } from \"./constants.js\";\n\n/** Result of classifying the project root directory. */\nexport interface ProjectClassification {\n /** The directory to scaffold into (may differ from cwd for monorepos). */\n projectRoot: string;\n /** Whether this was auto-resolved from a monorepo root. */\n isMonorepo: boolean;\n /** If monorepo, the relative path from cwd to the resolved app. */\n appRelativePath?: string;\n}\n\n/**\n * Classifies the current directory and resolves the target project root.\n *\n * Classification logic:\n * 1. If the directory contains a Next.js config file, it is a Next.js app\n * directory. Returns it directly.\n * 1b. If no config file exists but package.json lists \"next\" as a dependency,\n * it is still a Next.js app (config files are optional since Next.js 12).\n * 2. If the directory contains monorepo markers (pnpm-workspace.yaml,\n * turbo.json, lerna.json, or a workspaces field in package.json),\n * scans workspace packages for Next.js apps.\n * 3. Otherwise, fails with a user-facing error.\n *\n * @param cwd - The current working directory\n * @returns The resolved project classification\n * @throws Error with a user-facing message if the location is invalid\n */\nexport function resolveProjectRoot(cwd: string): ProjectClassification {\n // Step 1: Check if cwd is a Next.js app directory (config file)\n if (hasNextConfig(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 1b: Check if cwd has \"next\" as a dependency (config is optional)\n if (hasNextDependency(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 2: Check for monorepo markers\n if (isMonorepoRoot(cwd)) {\n // findNextJsApps throws if no workspace globs are found (e.g., turbo.json\n // exists but no pnpm-workspace.yaml or workspaces in package.json)\n const apps = findNextJsApps(cwd);\n\n if (apps.length === 0) {\n throw new Error(\n \"This is a monorepo but no Next.js apps were found in workspace packages.\",\n );\n }\n\n if (apps.length === 1) {\n const appDir = apps[0];\n const relativePath = path.relative(cwd, appDir);\n return {\n projectRoot: appDir,\n isMonorepo: true,\n appRelativePath: relativePath,\n };\n }\n\n // Multiple apps found — cannot auto-resolve\n const appList = apps\n .map((app) => ` - ${path.relative(cwd, app)}`)\n .join(\"\\n\");\n throw new Error(\n `Found multiple Next.js apps:\\n${appList}\\nRun init from the specific app directory you want to instrument.`,\n );\n }\n\n // Step 3: Neither Next.js app nor monorepo\n throw new Error(\n \"No Next.js project found in the current directory.\\n\" +\n \"Run this command from your Next.js app directory, or from a monorepo root.\",\n );\n}\n\n/**\n * Checks whether the given directory contains a Next.js config file.\n */\nfunction hasNextConfig(dir: string): boolean {\n return NEXT_CONFIG_NAMES.some((name) =>\n fs.existsSync(path.join(dir, name)),\n );\n}\n\n/**\n * Checks whether the given directory's package.json lists \"next\" as a\n * dependency or devDependency. This handles the case where a Next.js app\n * has no explicit config file (config files are optional since Next.js 12).\n */\nfunction hasNextDependency(dir: string): boolean {\n const packageJsonPath = path.join(dir, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) return false;\n\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n const deps = pkg[\"dependencies\"];\n const devDeps = pkg[\"devDependencies\"];\n\n if (typeof deps === \"object\" && deps !== null && \"next\" in deps) return true;\n if (typeof devDeps === \"object\" && devDeps !== null && \"next\" in devDeps) return true;\n } catch {\n // Invalid JSON — not a Next.js indicator\n }\n\n return false;\n}\n\n/**\n * Detects monorepo markers in the given directory.\n *\n * Checks for:\n * - pnpm-workspace.yaml\n * - turbo.json\n * - lerna.json\n * - \"workspaces\" field in package.json\n */\nexport function isMonorepoRoot(dir: string): boolean {\n // Check for standalone monorepo marker files\n if (fs.existsSync(path.join(dir, \"pnpm-workspace.yaml\"))) return true;\n if (fs.existsSync(path.join(dir, \"turbo.json\"))) return true;\n if (fs.existsSync(path.join(dir, \"lerna.json\"))) return true;\n\n // Check for \"workspaces\" field in package.json\n const packageJsonPath = path.join(dir, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n if (pkg[\"workspaces\"] !== undefined) return true;\n } catch {\n // Invalid JSON — not a monorepo indicator\n }\n }\n\n return false;\n}\n\n/**\n * Finds Next.js apps in workspace packages.\n *\n * Parses workspace globs from:\n * - pnpm-workspace.yaml (packages array)\n * - package.json workspaces field (string[] or { packages: string[] })\n * - lerna.json packages field (string[])\n *\n * Expands the workspace globs using filesystem traversal and returns\n * absolute paths of directories that contain a Next.js config file or\n * have \"next\" as a dependency in package.json.\n *\n * @param monorepoRoot - Absolute path to the monorepo root directory\n * @returns Sorted array of absolute paths to Next.js app directories\n */\nexport function findNextJsApps(monorepoRoot: string): string[] {\n const { includeGlobs, negationPatterns } = collectWorkspaceGlobs(monorepoRoot);\n\n if (includeGlobs.length === 0) {\n throw new Error(\n \"Monorepo detected but no workspace configuration found.\\n\" +\n 'Add a \"workspaces\" field to package.json or create pnpm-workspace.yaml.',\n );\n }\n\n const workspaceDirs = expandGlobs(monorepoRoot, includeGlobs);\n\n // Apply negation patterns: filter out directories matching any exclusion\n const excludedDirs = expandGlobs(monorepoRoot, negationPatterns);\n const excludedSet = new Set(excludedDirs);\n\n // Deduplicate and filter for Next.js apps\n const seen = new Set<string>();\n const nextApps: string[] = [];\n\n for (const dir of workspaceDirs) {\n if (seen.has(dir)) continue;\n seen.add(dir);\n if (excludedSet.has(dir)) continue;\n if (hasNextConfig(dir) || hasNextDependency(dir)) {\n nextApps.push(dir);\n }\n }\n\n return nextApps.sort();\n}\n\n/** Workspace globs split into include and negation patterns. */\nexport interface WorkspaceGlobs {\n includeGlobs: string[];\n negationPatterns: string[];\n}\n\n/**\n * Collects workspace globs from all supported monorepo config sources.\n * Returns deduplicated include globs and negation patterns separately.\n */\nfunction collectWorkspaceGlobs(root: string): WorkspaceGlobs {\n const globs: string[] = [];\n const negations: string[] = [];\n\n // 1. pnpm-workspace.yaml\n const pnpmPath = path.join(root, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmPath)) {\n const content = fs.readFileSync(pnpmPath, \"utf-8\");\n const parsed = parsePnpmWorkspaceYaml(content);\n globs.push(...parsed.includeGlobs);\n negations.push(...parsed.negationPatterns);\n }\n\n // 2. package.json workspaces\n const packageJsonPath = path.join(root, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n globs.push(...parsePackageJsonWorkspaces(pkg));\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // 3. lerna.json packages\n const lernaPath = path.join(root, \"lerna.json\");\n if (fs.existsSync(lernaPath)) {\n try {\n const content = fs.readFileSync(lernaPath, \"utf-8\");\n const lerna = JSON.parse(content) as Record<string, unknown>;\n const packages = lerna[\"packages\"];\n if (Array.isArray(packages)) {\n for (const pkg of packages) {\n if (typeof pkg === \"string\") {\n globs.push(pkg);\n }\n }\n }\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // Deduplicate\n return {\n includeGlobs: [...new Set(globs)],\n negationPatterns: [...new Set(negations)],\n };\n}\n\n/**\n * Parses pnpm-workspace.yaml to extract workspace package globs.\n *\n * The format is simple enough to parse with string processing:\n * ```yaml\n * packages:\n * - \"apps/*\"\n * - packages/*\n * - '!packages/internal'\n * ```\n *\n * Handles both quoted and unquoted values. Negation patterns (lines\n * starting with !) are returned separately so callers can apply them\n * as exclusions after expanding include globs.\n *\n * @internal Exported for unit testing only.\n */\nexport function parsePnpmWorkspaceYaml(content: string): WorkspaceGlobs {\n const lines = content.split(\"\\n\");\n const includeGlobs: string[] = [];\n const negationPatterns: string[] = [];\n let inPackages = false;\n\n for (const rawLine of lines) {\n const trimmed = rawLine.trim();\n\n // Detect the `packages:` key\n if (/^packages\\s*:/.test(trimmed)) {\n inPackages = true;\n continue;\n }\n\n // Stop when we hit another top-level key (no leading whitespace before key)\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith(\"-\") && !rawLine.startsWith(\" \") && !rawLine.startsWith(\"\\t\")) {\n inPackages = false;\n continue;\n }\n\n if (!inPackages) continue;\n\n // Parse list items: ` - \"glob\"` or ` - glob` or ` - 'glob'`\n const itemMatch = /^\\s*-\\s+(.+)$/.exec(rawLine);\n if (!itemMatch) continue;\n\n // Strip surrounding quotes (single or double)\n const value = itemMatch[1].trim().replace(/^[\"']|[\"']$/g, \"\");\n\n // Skip empty values\n if (value.length === 0) continue;\n\n // Collect negation patterns separately (strip the leading !)\n if (value.startsWith(\"!\")) {\n negationPatterns.push(value.slice(1));\n continue;\n }\n\n includeGlobs.push(value);\n }\n\n return { includeGlobs, negationPatterns };\n}\n\n/**\n * Extracts workspace globs from a parsed package.json object.\n *\n * Handles both forms:\n * - `\"workspaces\": [\"packages/*\", \"apps/*\"]`\n * - `\"workspaces\": { \"packages\": [\"packages/*\", \"apps/*\"] }`\n */\nfunction parsePackageJsonWorkspaces(pkg: Record<string, unknown>): string[] {\n const workspaces = pkg[\"workspaces\"];\n if (workspaces === undefined || workspaces === null) return [];\n\n // Array form: string[]\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === \"string\");\n }\n\n // Object form: { packages: string[] }\n if (typeof workspaces === \"object\") {\n const obj = workspaces as Record<string, unknown>;\n const packages = obj[\"packages\"];\n if (Array.isArray(packages)) {\n return packages.filter((p): p is string => typeof p === \"string\");\n }\n }\n\n return [];\n}\n\n/**\n * Expands workspace globs into actual directory paths.\n *\n * Supports:\n * - `packages/*` — matches one level of directories under packages/\n * - `apps/*` — matches one level of directories under apps/\n * - `packages/foo` — matches a specific directory (literal path)\n * - `packages/**` — recursively walks for directories with package.json\n *\n * @param root - The monorepo root directory\n * @param globs - Workspace glob patterns to expand\n * @returns Array of absolute paths to matched directories\n */\nfunction expandGlobs(root: string, globs: string[]): string[] {\n const dirs: string[] = [];\n\n for (const glob of globs) {\n // Remove trailing slash if present\n const cleanGlob = glob.replace(/\\/+$/, \"\");\n\n if (cleanGlob.includes(\"**\")) {\n // Recursive glob — walk the directory tree\n const prefix = cleanGlob.split(\"**\")[0].replace(/\\/+$/, \"\");\n const baseDir = path.join(root, prefix);\n if (fs.existsSync(baseDir)) {\n dirs.push(...walkDirectories(baseDir));\n }\n } else if (cleanGlob.includes(\"*\")) {\n // Single-level wildcard — expand one directory level\n const parts = cleanGlob.split(\"*\");\n // For \"packages/*\", parts = [\"packages/\", \"\"]\n const baseDir = path.join(root, parts[0].replace(/\\/+$/, \"\"));\n const suffix = parts.slice(1).join(\"*\"); // Anything after the wildcard\n\n if (!fs.existsSync(baseDir)) continue;\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // If there is a suffix pattern, the entry name must end with it\n if (suffix && !entry.name.endsWith(suffix)) continue;\n dirs.push(path.join(baseDir, entry.name));\n }\n } else {\n // Literal path — no wildcards\n const targetDir = path.join(root, cleanGlob);\n if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {\n dirs.push(targetDir);\n }\n }\n }\n\n return dirs;\n}\n\n/**\n * Recursively walks a directory tree and returns all subdirectories\n * that contain a package.json (indicating they are workspace packages).\n * Skips node_modules and hidden directories.\n */\nfunction walkDirectories(baseDir: string): string[] {\n const result: string[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n return result;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // Skip node_modules and hidden directories\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n\n const fullPath = path.join(baseDir, entry.name);\n\n // A workspace package should have a package.json\n if (fs.existsSync(path.join(fullPath, \"package.json\"))) {\n result.push(fullPath);\n }\n\n // Continue recursing for nested workspaces\n result.push(...walkDirectories(fullPath));\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/monorepo.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { NEXT_CONFIG_NAMES } from \"./constants.js\";\n\n/** Result of classifying the project root directory. */\nexport interface ProjectClassification {\n /** The directory to scaffold into (may differ from cwd for monorepos). */\n projectRoot: string;\n /** Whether this was auto-resolved from a monorepo root. */\n isMonorepo: boolean;\n /** If monorepo, the relative path from cwd to the resolved app. */\n appRelativePath?: string;\n}\n\n/**\n * Classifies the current directory and resolves the target project root.\n *\n * Classification logic:\n * 1. If the directory contains a Next.js config file, it is a Next.js app\n * directory. Returns it directly.\n * 1b. If no config file exists but package.json lists \"next\" as a dependency,\n * it is still a Next.js app (config files are optional since Next.js 12).\n * 2. If the directory contains monorepo markers (pnpm-workspace.yaml,\n * turbo.json, lerna.json, or a workspaces field in package.json),\n * scans workspace packages for Next.js apps.\n * 3. Otherwise, fails with a user-facing error.\n *\n * @param cwd - The current working directory\n * @returns The resolved project classification\n * @throws Error with a user-facing message if the location is invalid\n */\nexport function resolveProjectRoot(cwd: string): ProjectClassification {\n // Step 1: Check if cwd is a Next.js app directory (config file)\n if (hasNextConfig(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 1b: Check if cwd has \"next\" as a dependency (config is optional)\n if (hasNextDependency(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 2: Check for monorepo markers\n if (isMonorepoRoot(cwd)) {\n // findNextJsApps throws if no workspace globs are found (e.g., turbo.json\n // exists but no pnpm-workspace.yaml or workspaces in package.json)\n const apps = findNextJsApps(cwd);\n\n if (apps.length === 0) {\n throw new Error(\n \"This is a monorepo but no Next.js apps were found in workspace packages.\",\n );\n }\n\n if (apps.length === 1) {\n const appDir = apps[0];\n const relativePath = path.relative(cwd, appDir);\n return {\n projectRoot: appDir,\n isMonorepo: true,\n appRelativePath: relativePath,\n };\n }\n\n // Multiple apps found — cannot auto-resolve\n const appList = apps\n .map((app) => ` - ${path.relative(cwd, app)}`)\n .join(\"\\n\");\n throw new Error(\n `Found multiple Next.js apps:\\n${appList}\\nRun init from the specific app directory you want to instrument.`,\n );\n }\n\n // Step 3: Neither Next.js app nor monorepo\n throw new Error(\n \"No Next.js project found in the current directory.\\n\" +\n \"Run this command from your Next.js app directory, or from a monorepo root.\",\n );\n}\n\n/**\n * Checks whether the given directory contains a Next.js config file.\n */\nfunction hasNextConfig(dir: string): boolean {\n return NEXT_CONFIG_NAMES.some((name) =>\n fs.existsSync(path.join(dir, name)),\n );\n}\n\n/**\n * Checks whether the given directory's package.json lists \"next\" as a\n * dependency or devDependency. This handles the case where a Next.js app\n * has no explicit config file (config files are optional since Next.js 12).\n */\nfunction hasNextDependency(dir: string): boolean {\n const packageJsonPath = path.join(dir, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) return false;\n\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n const deps = pkg[\"dependencies\"];\n const devDeps = pkg[\"devDependencies\"];\n\n if (typeof deps === \"object\" && deps !== null && \"next\" in deps) return true;\n if (typeof devDeps === \"object\" && devDeps !== null && \"next\" in devDeps) return true;\n } catch {\n // Invalid JSON — not a Next.js indicator\n }\n\n return false;\n}\n\n/**\n * Detects monorepo markers in the given directory.\n *\n * Checks for:\n * - pnpm-workspace.yaml\n * - turbo.json\n * - lerna.json\n * - \"workspaces\" field in package.json\n */\nexport function isMonorepoRoot(dir: string): boolean {\n // Check for standalone monorepo marker files\n if (fs.existsSync(path.join(dir, \"pnpm-workspace.yaml\"))) return true;\n if (fs.existsSync(path.join(dir, \"turbo.json\"))) return true;\n if (fs.existsSync(path.join(dir, \"lerna.json\"))) return true;\n\n // Check for \"workspaces\" field in package.json\n const packageJsonPath = path.join(dir, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n if (pkg[\"workspaces\"] !== undefined) return true;\n } catch {\n // Invalid JSON — not a monorepo indicator\n }\n }\n\n return false;\n}\n\n/**\n * Finds Next.js apps in workspace packages.\n *\n * Parses workspace globs from:\n * - pnpm-workspace.yaml (packages array)\n * - package.json workspaces field (string[] or { packages: string[] })\n * - lerna.json packages field (string[])\n *\n * Expands the workspace globs using filesystem traversal and returns\n * absolute paths of directories that contain a Next.js config file or\n * have \"next\" as a dependency in package.json.\n *\n * @param monorepoRoot - Absolute path to the monorepo root directory\n * @returns Sorted array of absolute paths to Next.js app directories\n */\nexport function findNextJsApps(monorepoRoot: string): string[] {\n const { includeGlobs, negationPatterns } = collectWorkspaceGlobs(monorepoRoot);\n\n if (includeGlobs.length === 0) {\n throw new Error(\n \"Monorepo detected but no workspace configuration found.\\n\" +\n 'Add a \"workspaces\" field to package.json or create pnpm-workspace.yaml.',\n );\n }\n\n const workspaceDirs = expandGlobs(monorepoRoot, includeGlobs);\n\n // Apply negation patterns: filter out directories matching any exclusion\n const excludedDirs = expandGlobs(monorepoRoot, negationPatterns);\n const excludedSet = new Set(excludedDirs);\n\n // Deduplicate and filter for Next.js apps\n const seen = new Set<string>();\n const nextApps: string[] = [];\n\n for (const dir of workspaceDirs) {\n if (seen.has(dir)) continue;\n seen.add(dir);\n if (excludedSet.has(dir)) continue;\n if (hasNextConfig(dir) || hasNextDependency(dir)) {\n nextApps.push(dir);\n }\n }\n\n return nextApps.sort();\n}\n\n/** Workspace globs split into include and negation patterns. */\nexport interface WorkspaceGlobs {\n includeGlobs: string[];\n negationPatterns: string[];\n}\n\n/**\n * Collects workspace globs from all supported monorepo config sources.\n * Returns deduplicated include globs and negation patterns separately.\n */\nfunction collectWorkspaceGlobs(root: string): WorkspaceGlobs {\n const globs: string[] = [];\n const negations: string[] = [];\n\n // 1. pnpm-workspace.yaml\n const pnpmPath = path.join(root, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmPath)) {\n const content = fs.readFileSync(pnpmPath, \"utf-8\");\n const parsed = parsePnpmWorkspaceYaml(content);\n globs.push(...parsed.includeGlobs);\n negations.push(...parsed.negationPatterns);\n }\n\n // 2. package.json workspaces\n const packageJsonPath = path.join(root, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n globs.push(...parsePackageJsonWorkspaces(pkg));\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // 3. lerna.json packages\n const lernaPath = path.join(root, \"lerna.json\");\n if (fs.existsSync(lernaPath)) {\n try {\n const content = fs.readFileSync(lernaPath, \"utf-8\");\n const lerna = JSON.parse(content) as Record<string, unknown>;\n const packages = lerna[\"packages\"];\n if (Array.isArray(packages)) {\n for (const pkg of packages) {\n if (typeof pkg === \"string\") {\n globs.push(pkg);\n }\n }\n }\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // Deduplicate\n return {\n includeGlobs: [...new Set(globs)],\n negationPatterns: [...new Set(negations)],\n };\n}\n\n/**\n * Parses pnpm-workspace.yaml to extract workspace package globs.\n *\n * The format is simple enough to parse with string processing:\n * ```yaml\n * packages:\n * - \"apps/*\"\n * - packages/*\n * - '!packages/internal'\n * ```\n *\n * Handles both quoted and unquoted values. Negation patterns (lines\n * starting with !) are returned separately so callers can apply them\n * as exclusions after expanding include globs.\n *\n * @internal Exported for unit testing only.\n */\nexport function parsePnpmWorkspaceYaml(content: string): WorkspaceGlobs {\n const lines = content.split(\"\\n\");\n const includeGlobs: string[] = [];\n const negationPatterns: string[] = [];\n let inPackages = false;\n\n for (const rawLine of lines) {\n const trimmed = rawLine.trim();\n\n // Detect the `packages:` key\n if (/^packages\\s*:/.test(trimmed)) {\n inPackages = true;\n continue;\n }\n\n // Stop when we hit another top-level key (no leading whitespace before key)\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith(\"-\") && !rawLine.startsWith(\" \") && !rawLine.startsWith(\"\\t\")) {\n inPackages = false;\n continue;\n }\n\n if (!inPackages) continue;\n\n // Parse list items: ` - \"glob\"` or ` - glob` or ` - 'glob'`\n const itemMatch = /^\\s*-\\s+(.+)$/.exec(rawLine);\n if (!itemMatch) continue;\n\n // Strip surrounding quotes (single or double)\n const value = itemMatch[1].trim().replace(/^[\"']|[\"']$/g, \"\");\n\n // Skip empty values\n if (value.length === 0) continue;\n\n // Collect negation patterns separately (strip the leading !)\n if (value.startsWith(\"!\")) {\n negationPatterns.push(value.slice(1));\n continue;\n }\n\n includeGlobs.push(value);\n }\n\n return { includeGlobs, negationPatterns };\n}\n\n/**\n * Extracts workspace globs from a parsed package.json object.\n *\n * Handles both forms:\n * - `\"workspaces\": [\"packages/*\", \"apps/*\"]`\n * - `\"workspaces\": { \"packages\": [\"packages/*\", \"apps/*\"] }`\n */\nfunction parsePackageJsonWorkspaces(pkg: Record<string, unknown>): string[] {\n const workspaces = pkg[\"workspaces\"];\n if (workspaces === undefined || workspaces === null) return [];\n\n // Array form: string[]\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === \"string\");\n }\n\n // Object form: { packages: string[] }\n if (typeof workspaces === \"object\") {\n const obj = workspaces as Record<string, unknown>;\n const packages = obj[\"packages\"];\n if (Array.isArray(packages)) {\n return packages.filter((p): p is string => typeof p === \"string\");\n }\n }\n\n return [];\n}\n\n/**\n * Expands workspace globs into actual directory paths.\n *\n * Supports:\n * - `packages/*` — matches one level of directories under packages/\n * - `apps/*` — matches one level of directories under apps/\n * - `packages/foo` — matches a specific directory (literal path)\n * - `packages/**` — recursively walks for directories with package.json\n *\n * @param root - The monorepo root directory\n * @param globs - Workspace glob patterns to expand\n * @returns Array of absolute paths to matched directories\n */\nfunction expandGlobs(root: string, globs: string[]): string[] {\n const dirs: string[] = [];\n\n for (const glob of globs) {\n // Remove trailing slash if present\n const cleanGlob = glob.replace(/\\/+$/, \"\");\n\n if (cleanGlob.includes(\"**\")) {\n // Recursive glob — walk the directory tree\n const prefix = cleanGlob.split(\"**\")[0].replace(/\\/+$/, \"\");\n const baseDir = path.join(root, prefix);\n if (fs.existsSync(baseDir)) {\n dirs.push(...walkDirectories(baseDir));\n }\n } else if (cleanGlob.includes(\"*\")) {\n // Single-level wildcard — expand one directory level\n const parts = cleanGlob.split(\"*\");\n // For \"packages/*\", parts = [\"packages/\", \"\"]\n const baseDir = path.join(root, parts[0].replace(/\\/+$/, \"\"));\n const suffix = parts.slice(1).join(\"*\"); // Anything after the wildcard\n\n if (!fs.existsSync(baseDir)) continue;\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // If there is a suffix pattern, the entry name must end with it\n if (suffix && !entry.name.endsWith(suffix)) continue;\n dirs.push(path.join(baseDir, entry.name));\n }\n } else {\n // Literal path — no wildcards\n const targetDir = path.join(root, cleanGlob);\n if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {\n dirs.push(targetDir);\n }\n }\n }\n\n return dirs;\n}\n\n/**\n * Recursively walks a directory tree and returns all subdirectories\n * that contain a package.json (indicating they are workspace packages).\n * Skips node_modules and hidden directories.\n */\nfunction walkDirectories(baseDir: string): string[] {\n const result: string[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n return result;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // Skip node_modules and hidden directories\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n\n const fullPath = path.join(baseDir, entry.name);\n\n // A workspace package should have a package.json\n if (fs.existsSync(path.join(fullPath, \"package.json\"))) {\n result.push(fullPath);\n }\n\n // Continue recursing for nested workspaces\n result.push(...walkDirectories(fullPath));\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
|