@checkstack/scripts 0.1.1 → 0.2.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.
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env bun
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import { spawnSync } from "node:child_process";
6
+ import { create as tarCreate } from "tar";
7
+ import {
8
+ installPackageMetadataSchema,
9
+ pluginBundleManifestSchema,
10
+ type InstallPackageMetadata,
11
+ type PluginBundleManifest,
12
+ } from "@checkstack/common";
13
+
14
+ /**
15
+ * `plugin-pack` CLI.
16
+ *
17
+ * Two modes:
18
+ * - default (per-package): packs the cwd into a single `.tgz` matching what
19
+ * `bun pm pack` would produce, with extra metadata validation. This is
20
+ * what gets uploaded to npm.
21
+ * - `--bundle`: packs the cwd's primary package + every sibling listed in
22
+ * `package.json#checkstack.bundle`, then wraps them into an outer tarball
23
+ * containing `bundle.json` + `packages/<name>-<version>.tgz`. This is the
24
+ * artifact attached to a GitHub release / uploaded directly via the
25
+ * plugin manager UI.
26
+ *
27
+ * Workspace-resolution: `workspace:*` deps in any sibling's `package.json` are
28
+ * rewritten to the matching package's actual version before packing. Resolution
29
+ * walks the nearest ancestor `package.json` containing a `workspaces` field;
30
+ * if none is found we treat the cwd as standalone (no rewriting needed).
31
+ */
32
+
33
+ interface Args {
34
+ bundle: boolean;
35
+ outDir: string;
36
+ cwd: string;
37
+ validateOnly: boolean;
38
+ }
39
+
40
+ export async function runPluginPack(rawArgs: string[]): Promise<number> {
41
+ const args = parseArgs(rawArgs);
42
+
43
+ const pkg = readJson<InstallPackageMetadata>(
44
+ path.join(args.cwd, "package.json"),
45
+ );
46
+
47
+ // Validate metadata ABOVE pack so the user sees errors immediately.
48
+ const validation = installPackageMetadataSchema.safeParse(pkg);
49
+ if (!validation.success) {
50
+ console.error("❌ package.json failed validation:");
51
+ for (const issue of validation.error.issues) {
52
+ console.error(` - ${issue.path.join(".")}: ${issue.message}`);
53
+ }
54
+ return 1;
55
+ }
56
+
57
+ if (args.validateOnly) {
58
+ console.log("✅ Metadata valid.");
59
+ return 0;
60
+ }
61
+
62
+ // Pre-pack hooks
63
+ if (!args.bundle) {
64
+ runScriptIfPresent({ cwd: args.cwd, script: "typecheck" });
65
+ runScriptIfPresent({ cwd: args.cwd, script: "lint" });
66
+ }
67
+
68
+ fs.mkdirSync(args.outDir, { recursive: true });
69
+
70
+ if (args.bundle) {
71
+ const bundleNames = pkg.checkstack.bundle ?? [];
72
+ if (bundleNames.length === 0) {
73
+ console.error(
74
+ "❌ --bundle requires `checkstack.bundle` to be set in package.json.",
75
+ );
76
+ return 1;
77
+ }
78
+
79
+ const workspaceMap = buildWorkspaceMap(args.cwd);
80
+
81
+ const primaryTarball = await packPackage({
82
+ pkgDir: args.cwd,
83
+ outDir: args.outDir,
84
+ workspaceMap,
85
+ });
86
+
87
+ const siblings: { name: string; version: string; tarball: string }[] = [
88
+ {
89
+ name: pkg.name,
90
+ version: pkg.version,
91
+ tarball: `packages/${path.basename(primaryTarball)}`,
92
+ },
93
+ ];
94
+
95
+ for (const sibName of bundleNames) {
96
+ if (sibName === pkg.name) continue;
97
+ const sibDir = workspaceMap.get(sibName);
98
+ if (!sibDir) {
99
+ console.error(
100
+ `❌ Bundle sibling '${sibName}' not found in workspace. ` +
101
+ `Make sure it's a peer of the primary package.`,
102
+ );
103
+ return 1;
104
+ }
105
+ const sibTarball = await packPackage({
106
+ pkgDir: sibDir,
107
+ outDir: args.outDir,
108
+ workspaceMap,
109
+ });
110
+ const sibPkg = readJson<InstallPackageMetadata>(
111
+ path.join(sibDir, "package.json"),
112
+ );
113
+ siblings.push({
114
+ name: sibPkg.name,
115
+ version: sibPkg.version,
116
+ tarball: `packages/${path.basename(sibTarball)}`,
117
+ });
118
+ }
119
+
120
+ const manifest: PluginBundleManifest = pluginBundleManifestSchema.parse({
121
+ bundleVersion: 1,
122
+ primary: pkg.name,
123
+ packages: siblings,
124
+ });
125
+
126
+ const outerTarball = path.join(
127
+ args.outDir,
128
+ `${safeFileName(pkg.name)}-${pkg.version}-bundle.tgz`,
129
+ );
130
+
131
+ // Lay out files in a tmpdir then `tar.create`.
132
+ const stage = fs.mkdtempSync(path.join(os.tmpdir(), "checkstack-bundle-"));
133
+ try {
134
+ fs.mkdirSync(path.join(stage, "packages"), { recursive: true });
135
+ for (const sib of siblings) {
136
+ fs.copyFileSync(
137
+ path.join(args.outDir, path.basename(sib.tarball)),
138
+ path.join(stage, sib.tarball),
139
+ );
140
+ }
141
+ fs.writeFileSync(
142
+ path.join(stage, "bundle.json"),
143
+ JSON.stringify(manifest, undefined, 2),
144
+ );
145
+ await tarCreate(
146
+ {
147
+ gzip: true,
148
+ file: outerTarball,
149
+ cwd: stage,
150
+ },
151
+ ["bundle.json", "packages"],
152
+ );
153
+ } finally {
154
+ fs.rmSync(stage, { recursive: true, force: true });
155
+ }
156
+
157
+ console.log(`✅ Bundle tarball: ${outerTarball}`);
158
+ return 0;
159
+ }
160
+
161
+ // Default mode — single per-package tarball
162
+ const workspaceMap = buildWorkspaceMap(args.cwd);
163
+ const tarball = await packPackage({
164
+ pkgDir: args.cwd,
165
+ outDir: args.outDir,
166
+ workspaceMap,
167
+ });
168
+ console.log(`✅ Tarball: ${tarball}`);
169
+ return 0;
170
+ }
171
+
172
+ function parseArgs(raw: string[]): Args {
173
+ const args: Args = {
174
+ bundle: false,
175
+ outDir: path.resolve("dist"),
176
+ cwd: process.cwd(),
177
+ validateOnly: false,
178
+ };
179
+ for (let i = 0; i < raw.length; i++) {
180
+ const a = raw[i];
181
+ switch (a) {
182
+ case "--bundle": {
183
+ args.bundle = true;
184
+ break;
185
+ }
186
+ case "--validate-only": {
187
+ args.validateOnly = true;
188
+ break;
189
+ }
190
+ case "--out-dir": {
191
+ args.outDir = path.resolve(raw[++i]);
192
+ break;
193
+ }
194
+ case "--cwd": {
195
+ args.cwd = path.resolve(raw[++i]);
196
+ break;
197
+ }
198
+ case "--help":
199
+ case "-h": {
200
+ printHelp();
201
+ process.exit(0);
202
+
203
+ break;
204
+ }
205
+ // No default
206
+ }
207
+ }
208
+ return args;
209
+ }
210
+
211
+ function printHelp(): void {
212
+ console.log(`Usage: checkstack-scripts plugin-pack [options]
213
+
214
+ Packs a Checkstack plugin into a .tgz, validating package.json metadata
215
+ along the way. Run from the directory containing the plugin's package.json.
216
+
217
+ Options:
218
+ --bundle Pack the primary plus every sibling declared in
219
+ package.json#checkstack.bundle into a single outer
220
+ tarball with a bundle.json manifest.
221
+ --out-dir <dir> Output directory (default: ./dist)
222
+ --validate-only Only validate metadata; do not pack.
223
+ --cwd <dir> Run as if invoked from <dir> (default: process.cwd())
224
+ --help, -h Show this message.
225
+ `);
226
+ }
227
+
228
+ function runScriptIfPresent({
229
+ cwd,
230
+ script,
231
+ }: {
232
+ cwd: string;
233
+ script: string;
234
+ }): void {
235
+ const pkg = readJson<{ scripts?: Record<string, string> }>(
236
+ path.join(cwd, "package.json"),
237
+ );
238
+ if (!pkg.scripts?.[script]) return;
239
+ console.log(`▶ bun run ${script}`);
240
+ const r = spawnSync("bun", ["run", script], { cwd, stdio: "inherit" });
241
+ if (r.status !== 0) {
242
+ throw new Error(`bun run ${script} exited with status ${r.status}`);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Walk up from `cwd` to find the workspace root (`package.json` with a
248
+ * `workspaces` field), then scan every workspace package and build a
249
+ * `name -> dir` map. Returns an empty map when no workspace ancestor
250
+ * exists (standalone plugin repo).
251
+ */
252
+ function buildWorkspaceMap(cwd: string): Map<string, string> {
253
+ const result = new Map<string, string>();
254
+ const root = findWorkspaceRoot(cwd);
255
+ if (!root) return result;
256
+
257
+ const rootPkg = readJson<{ workspaces?: string[] | { packages?: string[] } }>(
258
+ path.join(root, "package.json"),
259
+ );
260
+ const patterns = Array.isArray(rootPkg.workspaces)
261
+ ? rootPkg.workspaces
262
+ : (rootPkg.workspaces?.packages ?? []);
263
+
264
+ for (const pattern of patterns) {
265
+ // Bun/npm globs are typically `core/*` or `plugins/*`. We resolve the
266
+ // direct child layer; deeper nesting is uncommon and not supported here.
267
+ const baseDir = pattern.replace(/\/\*$/, "");
268
+ const absBase = path.join(root, baseDir);
269
+ if (!fs.existsSync(absBase)) continue;
270
+ for (const entry of fs.readdirSync(absBase, { withFileTypes: true })) {
271
+ if (!entry.isDirectory()) continue;
272
+ const dir = path.join(absBase, entry.name);
273
+ const pkgJsonPath = path.join(dir, "package.json");
274
+ if (!fs.existsSync(pkgJsonPath)) continue;
275
+ const pkg = readJson<{ name?: string }>(pkgJsonPath);
276
+ if (pkg.name) result.set(pkg.name, dir);
277
+ }
278
+ }
279
+
280
+ return result;
281
+ }
282
+
283
+ function findWorkspaceRoot(start: string): string | undefined {
284
+ let dir = start;
285
+ while (dir !== path.dirname(dir)) {
286
+ const pkgJsonPath = path.join(dir, "package.json");
287
+ if (fs.existsSync(pkgJsonPath)) {
288
+ const pkg = readJson<{ workspaces?: unknown }>(pkgJsonPath);
289
+ if (pkg.workspaces) return dir;
290
+ }
291
+ dir = path.dirname(dir);
292
+ }
293
+ return undefined;
294
+ }
295
+
296
+ /**
297
+ * Pack a single package into a tarball under `outDir`.
298
+ *
299
+ * Steps:
300
+ * 1. Read package.json
301
+ * 2. Replace `workspace:*` deps with concrete versions from `workspaceMap`
302
+ * 3. Run `bun pm pack` (which produces a tarball using the rewritten
303
+ * package.json)
304
+ * 4. Restore the original package.json on disk so the developer's source
305
+ * tree is unchanged
306
+ */
307
+ async function packPackage({
308
+ pkgDir,
309
+ outDir,
310
+ workspaceMap,
311
+ }: {
312
+ pkgDir: string;
313
+ outDir: string;
314
+ workspaceMap: Map<string, string>;
315
+ }): Promise<string> {
316
+ const pkgJsonPath = path.join(pkgDir, "package.json");
317
+ const original = fs.readFileSync(pkgJsonPath, "utf8");
318
+ const pkg = JSON.parse(original) as InstallPackageMetadata & {
319
+ devDependencies?: Record<string, string>;
320
+ peerDependencies?: Record<string, string>;
321
+ };
322
+
323
+ let rewritten = false;
324
+ for (const section of [
325
+ "dependencies",
326
+ "devDependencies",
327
+ "peerDependencies",
328
+ ] as const) {
329
+ const block = pkg[section];
330
+ if (!block) continue;
331
+ for (const [name, range] of Object.entries(block)) {
332
+ if (range.startsWith("workspace:")) {
333
+ const targetDir = workspaceMap.get(name);
334
+ if (!targetDir) {
335
+ throw new Error(
336
+ `Cannot resolve workspace dep '${name}' (declared in '${pkg.name}'). ` +
337
+ `Either move the package into the workspace or replace the workspace range with a concrete version.`,
338
+ );
339
+ }
340
+ const targetPkg = readJson<{ version: string }>(
341
+ path.join(targetDir, "package.json"),
342
+ );
343
+ block[name] = `^${targetPkg.version}`;
344
+ rewritten = true;
345
+ }
346
+ }
347
+ }
348
+
349
+ try {
350
+ if (rewritten) {
351
+ fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, undefined, 2));
352
+ }
353
+ const r = spawnSync(
354
+ "bun",
355
+ ["pm", "pack", "--destination", outDir],
356
+ { cwd: pkgDir, stdio: "inherit" },
357
+ );
358
+ if (r.status !== 0) {
359
+ throw new Error(`bun pm pack exited with status ${r.status}`);
360
+ }
361
+ } finally {
362
+ if (rewritten) fs.writeFileSync(pkgJsonPath, original);
363
+ }
364
+
365
+ // `bun pm pack` writes `<scope>-<name>-<version>.tgz` (scope-prefix collapsed).
366
+ const expectedName = `${safeFileName(pkg.name)}-${pkg.version}.tgz`;
367
+ const expected = path.join(outDir, expectedName);
368
+ if (!fs.existsSync(expected)) {
369
+ // Fall back: pick the most recently modified tgz in outDir.
370
+ const all = fs
371
+ .readdirSync(outDir)
372
+ .filter((f) => f.endsWith(".tgz"))
373
+ .map((f) => ({
374
+ name: f,
375
+ mtime: fs.statSync(path.join(outDir, f)).mtime.getTime(),
376
+ }))
377
+ .toSorted((a, b) => b.mtime - a.mtime);
378
+ if (all.length === 0) {
379
+ throw new Error(`bun pm pack produced no tarball in ${outDir}`);
380
+ }
381
+ return path.join(outDir, all[0].name);
382
+ }
383
+ return expected;
384
+ }
385
+
386
+ function safeFileName(name: string): string {
387
+ return name.replace(/^@/, "").replaceAll('/', "-");
388
+ }
389
+
390
+ function readJson<T>(p: string): T {
391
+ return JSON.parse(fs.readFileSync(p, "utf8")) as T;
392
+ }
393
+
394
+ if (import.meta.main) {
395
+ const code = await runPluginPack(process.argv.slice(2));
396
+ process.exit(code);
397
+ }
@@ -1,11 +1,39 @@
1
- { "name": "@checkstack/{{pluginName}}", "version": "0.0.1",
2
- "description": "{{pluginDescription}}", "type": "module", "exports": { ".": {
3
- "import": "./src/index.ts" } }, "scripts": { "typecheck": "tsc --noEmit",
4
- "lint": "bun run lint:code", "lint:code": "eslint . --max-warnings 0", "test":
5
- "bun test" }, "dependencies": { "@checkstack/backend-api": "workspace:*",
6
- "@checkstack/common": "workspace:*", "@checkstack/{{pluginBaseName}}-common":
7
- "workspace:*", "@orpc/server": "^1.13.2", "drizzle-orm": "^0.45.1" },
8
- "devDependencies": { "@checkstack/tsconfig": "workspace:*",
9
- "drizzle-kit": "^0.28.1", "@checkstack/drizzle-helper": "workspace:*",
10
- "@checkstack/test-utils-backend": "workspace:*", "typescript": "^5.7.2" }
11
- }
1
+ {
2
+ "name": "@checkstack/{{pluginName}}",
3
+ "version": "0.0.1",
4
+ "description": "{{pluginDescription}}",
5
+ "author": "Checkstack contributors",
6
+ "license": "Elastic-2.0",
7
+ "type": "module",
8
+ "main": "src/index.ts",
9
+ "exports": {
10
+ ".": { "import": "./src/index.ts" }
11
+ },
12
+ "checkstack": {
13
+ "type": "backend",
14
+ "pluginId": "{{pluginBaseName}}"
15
+ },
16
+ "scripts": {
17
+ "dev": "bunx @checkstack/scripts dev",
18
+ "pack": "bunx @checkstack/scripts plugin-pack",
19
+ "typecheck": "tsgo -b",
20
+ "lint": "bun run lint:code",
21
+ "lint:code": "eslint . --max-warnings 0",
22
+ "test": "bun test"
23
+ },
24
+ "dependencies": {
25
+ "@checkstack/backend-api": "workspace:*",
26
+ "@checkstack/common": "workspace:*",
27
+ "@checkstack/{{pluginBaseName}}-common": "workspace:*",
28
+ "@orpc/server": "^1.13.2",
29
+ "drizzle-orm": "^0.45.1"
30
+ },
31
+ "devDependencies": {
32
+ "@checkstack/scripts": "workspace:*",
33
+ "@checkstack/tsconfig": "workspace:*",
34
+ "@checkstack/drizzle-helper": "workspace:*",
35
+ "@checkstack/test-utils-backend": "workspace:*",
36
+ "drizzle-kit": "^0.28.1",
37
+ "typescript": "^5.7.2"
38
+ }
39
+ }
@@ -1,7 +1,32 @@
1
- { "name": "@checkstack/{{pluginName}}", "version": "0.0.1",
2
- "description": "{{pluginDescription}}", "type": "module", "exports": { ".": {
3
- "import": "./src/index.ts" } }, "scripts": { "typecheck": "tsc --noEmit",
4
- "lint": "bun run lint:code", "lint:code": "eslint . --max-warnings 0" },
5
- "dependencies": { "@checkstack/common": "workspace:*", "@orpc/contract":
6
- "^1.13.2", "zod": "^3.23.0" }, "devDependencies": {
7
- "@checkstack/tsconfig": "workspace:*", "typescript": "^5.7.2" } }
1
+ {
2
+ "name": "@checkstack/{{pluginName}}",
3
+ "version": "0.0.1",
4
+ "description": "{{pluginDescription}}",
5
+ "author": "Checkstack contributors",
6
+ "license": "Elastic-2.0",
7
+ "type": "module",
8
+ "main": "src/index.ts",
9
+ "exports": {
10
+ ".": { "import": "./src/index.ts" }
11
+ },
12
+ "checkstack": {
13
+ "type": "common",
14
+ "pluginId": "{{pluginBaseName}}"
15
+ },
16
+ "scripts": {
17
+ "pack": "bunx @checkstack/scripts plugin-pack",
18
+ "typecheck": "tsgo -b",
19
+ "lint": "bun run lint:code",
20
+ "lint:code": "eslint . --max-warnings 0"
21
+ },
22
+ "dependencies": {
23
+ "@checkstack/common": "workspace:*",
24
+ "@orpc/contract": "^1.13.2",
25
+ "zod": "^3.23.0"
26
+ },
27
+ "devDependencies": {
28
+ "@checkstack/scripts": "workspace:*",
29
+ "@checkstack/tsconfig": "workspace:*",
30
+ "typescript": "^5.7.2"
31
+ }
32
+ }
@@ -1,12 +1,41 @@
1
- { "name": "@checkstack/{{pluginName}}", "version": "0.0.1",
2
- "description": "{{pluginDescription}}", "type": "module", "exports": { ".": {
3
- "import": "./src/index.tsx" } }, "scripts": { "typecheck": "tsc --noEmit",
4
- "lint": "bun run lint:code", "lint:code": "eslint . --max-warnings 0",
5
- "test:e2e": "bunx playwright test" }, "dependencies": {
6
- "@checkstack/frontend-api": "workspace:*", "@checkstack/common":
7
- "workspace:*", "@checkstack/{{pluginBaseName}}-common": "workspace:*",
8
- "@checkstack/ui": "workspace:*", "react": "^18.3.1", "react-router-dom":
9
- "^7.1.1", "lucide-react": "^0.469.0" }, "devDependencies": {
10
- "@checkstack/tsconfig": "workspace:*", "@types/react": "^18.3.1",
11
- "@playwright/test": "^1.49.0", "@checkstack/test-utils-frontend":
12
- "workspace:*", "typescript": "^5.7.2" } }
1
+ {
2
+ "name": "@checkstack/{{pluginName}}",
3
+ "version": "0.0.1",
4
+ "description": "{{pluginDescription}}",
5
+ "author": "Checkstack contributors",
6
+ "license": "Elastic-2.0",
7
+ "type": "module",
8
+ "main": "src/index.tsx",
9
+ "exports": {
10
+ ".": { "import": "./src/index.tsx" }
11
+ },
12
+ "checkstack": {
13
+ "type": "frontend",
14
+ "pluginId": "{{pluginBaseName}}"
15
+ },
16
+ "scripts": {
17
+ "dev": "bunx @checkstack/scripts dev",
18
+ "pack": "bunx @checkstack/scripts plugin-pack",
19
+ "typecheck": "tsgo -b",
20
+ "lint": "bun run lint:code",
21
+ "lint:code": "eslint . --max-warnings 0",
22
+ "test:e2e": "bunx playwright test"
23
+ },
24
+ "dependencies": {
25
+ "@checkstack/frontend-api": "workspace:*",
26
+ "@checkstack/common": "workspace:*",
27
+ "@checkstack/{{pluginBaseName}}-common": "workspace:*",
28
+ "@checkstack/ui": "workspace:*",
29
+ "react": "^18.3.1",
30
+ "react-router-dom": "^7.1.1",
31
+ "lucide-react": "^0.469.0"
32
+ },
33
+ "devDependencies": {
34
+ "@checkstack/scripts": "workspace:*",
35
+ "@checkstack/tsconfig": "workspace:*",
36
+ "@checkstack/test-utils-frontend": "workspace:*",
37
+ "@types/react": "^18.3.1",
38
+ "@playwright/test": "^1.49.0",
39
+ "typescript": "^5.7.2"
40
+ }
41
+ }
@@ -4,8 +4,9 @@ import {
4
4
  prepareTemplateData,
5
5
  registerHelpers,
6
6
  } from "./utils/template";
7
+ import { validatePluginPackageJson } from "./commands/dev-internals";
7
8
  import { execSync } from "node:child_process";
8
- import { rmSync, existsSync, mkdirSync } from "node:fs";
9
+ import { rmSync, existsSync, mkdirSync, readFileSync } from "node:fs";
9
10
  import path from "node:path";
10
11
  import { fileURLToPath } from "node:url";
11
12
 
@@ -129,6 +130,52 @@ describe("CLI Template Scaffolding", () => {
129
130
  },
130
131
  { timeout: 30_000 }
131
132
  );
133
+
134
+ // ────────────────────────────────────────────────────────────
135
+ // Dev-server compatibility — one-click `bun run dev` works
136
+ // out of the box. These tests run synchronously against the
137
+ // rendered package.json; no further `bun install` needed
138
+ // because they only inspect static metadata.
139
+ // ────────────────────────────────────────────────────────────
140
+
141
+ it("renders a package.json that the dev-server's validator accepts", () => {
142
+ const result = validatePluginPackageJson({ cwd: targetDir });
143
+ if (!result.ok) {
144
+ throw new Error(
145
+ `Generated package.json fails dev-server validation:\n${
146
+ "issues" in result
147
+ ? result.issues.join("\n")
148
+ : "package.json missing"
149
+ }`,
150
+ );
151
+ }
152
+ // Sanity-check the metadata shape so a later schema change
153
+ // surfaces here too.
154
+ expect(result.metadata.name).toBe(`@checkstack/${pluginName}`);
155
+ expect(result.metadata.checkstack.type).toBe(pluginType);
156
+ expect(result.metadata.checkstack.pluginId).toBe(TEST_BASE_NAME);
157
+ expect(result.metadata.author).toBeDefined();
158
+ expect(result.metadata.license).toBe("Elastic-2.0");
159
+ });
160
+
161
+ it("includes the @checkstack/scripts devDependency so bunx resolves the dev/pack commands", () => {
162
+ const pkg = JSON.parse(
163
+ readFileSync(path.join(targetDir, "package.json"), "utf8"),
164
+ ) as { devDependencies?: Record<string, string> };
165
+ expect(pkg.devDependencies?.["@checkstack/scripts"]).toBeDefined();
166
+ });
167
+
168
+ it("includes the `pack` script (and the `dev` script for backend/frontend)", () => {
169
+ const pkg = JSON.parse(
170
+ readFileSync(path.join(targetDir, "package.json"), "utf8"),
171
+ ) as { scripts?: Record<string, string> };
172
+ expect(pkg.scripts?.pack).toBe("bunx @checkstack/scripts plugin-pack");
173
+ if (pluginType !== "common") {
174
+ // Common packages have no runtime, so a dev server makes no
175
+ // sense for them — but backend/frontend must be one-click.
176
+ expect(pkg.scripts?.dev).toBe("bunx @checkstack/scripts dev");
177
+ }
178
+ });
132
179
  });
133
180
  }
134
181
  });
package/CHANGELOG.md DELETED
@@ -1,83 +0,0 @@
1
- # @checkstack/scripts
2
-
3
- ## 0.1.1
4
-
5
- ### Patch Changes
6
-
7
- - 0b9fc58: Fix workspace:\* protocol resolution in published packages
8
-
9
- Published packages now correctly have resolved dependency versions instead of `workspace:*` references. This is achieved by using `bun publish` which properly resolves workspace protocol references.
10
-
11
- ## 0.1.0
12
-
13
- ### Minor Changes
14
-
15
- - 9faec1f: # Unified AccessRule Terminology Refactoring
16
-
17
- This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
18
-
19
- ## Changes
20
-
21
- ### Core Infrastructure (`@checkstack/common`)
22
-
23
- - Introduced `AccessRule` interface as the primary access control type
24
- - Added `accessPair()` helper for creating read/manage access rule pairs
25
- - Added `access()` builder for individual access rules
26
- - Replaced `Permission` type with `AccessRule` throughout
27
-
28
- ### API Changes
29
-
30
- - `env.registerPermissions()` → `env.registerAccessRules()`
31
- - `meta.permissions` → `meta.access` in RPC contracts
32
- - `usePermission()` → `useAccess()` in frontend hooks
33
- - Route `permission:` field → `accessRule:` field
34
-
35
- ### UI Changes
36
-
37
- - "Roles & Permissions" tab → "Roles & Access Rules"
38
- - "You don't have permission..." → "You don't have access..."
39
- - All permission-related UI text updated
40
-
41
- ### Documentation & Templates
42
-
43
- - Updated 18 documentation files with AccessRule terminology
44
- - Updated 7 scaffolding templates with `accessPair()` pattern
45
- - All code examples use new AccessRule API
46
-
47
- ## Migration Guide
48
-
49
- ### Backend Plugins
50
-
51
- ```diff
52
- - import { permissionList } from "./permissions";
53
- - env.registerPermissions(permissionList);
54
- + import { accessRules } from "./access";
55
- + env.registerAccessRules(accessRules);
56
- ```
57
-
58
- ### RPC Contracts
59
-
60
- ```diff
61
- - .meta({ userType: "user", permissions: [permissions.read.id] })
62
- + .meta({ userType: "user", access: [access.read] })
63
- ```
64
-
65
- ### Frontend Hooks
66
-
67
- ```diff
68
- - const canRead = accessApi.usePermission(permissions.read.id);
69
- + const canRead = accessApi.useAccess(access.read);
70
- ```
71
-
72
- ### Routes
73
-
74
- ```diff
75
- - permission: permissions.entityRead.id,
76
- + accessRule: access.read,
77
- ```
78
-
79
- ## 0.0.2
80
-
81
- ### Patch Changes
82
-
83
- - d20d274: Initial release of all @checkstack packages. Rebranded from Checkmate to Checkstack with new npm organization @checkstack and domain checkstack.dev.