@maker-or/opencms 0.1.6 → 0.1.8
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 +10 -1
- package/dist/index.js +144 -56
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -23,6 +23,15 @@ npx @maker-or/opencms deploy
|
|
|
23
23
|
|
|
24
24
|
The generated `cms/schema.json` file defines the project's content types and allowed blocks. The CLI syncs it to the development environment when `dev` or `deploy` runs.
|
|
25
25
|
|
|
26
|
+
`opencms deploy` promotes the development schema and only published development pages as one production snapshot. Draft pages stay out of production and production pages no longer present in the published development snapshot are removed. When `VERCEL_TOKEN` is set, the command also deploys the application with its project ID, API origin, and production CMS environment supplied to Vercel at build time and runtime.
|
|
27
|
+
|
|
26
28
|
The CLI stores its local login configuration in `~/.config/opencms/config.json` (or `$XDG_CONFIG_HOME/opencms/config.json` when configured).
|
|
27
29
|
|
|
28
|
-
The CLI
|
|
30
|
+
The CLI uses the OpenCMS control-plane origin from `OPENCMS_URL`. Set it to the dashboard/API origin for your hosted, local, or self-hosted instance. `OPENCMS_API_URL` and `OPENCMS_DASHBOARD_URL` remain supported as separate legacy overrides, but there is no baked-in deployment URL.
|
|
31
|
+
|
|
32
|
+
For example:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
export OPENCMS_URL=https://your-opencms-domain.example
|
|
36
|
+
npx @maker-or/opencms login
|
|
37
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { createServer } from "node:http";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
7
|
import { join, resolve } from "node:path";
|
|
7
|
-
import { access, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
8
|
+
import { access, chmod, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
8
9
|
import { constants } from "node:fs";
|
|
9
10
|
import { spawn } from "node:child_process";
|
|
10
11
|
import { createInterface } from "node:readline/promises";
|
|
@@ -67,10 +68,7 @@ class OpenCmsApiError extends Error {
|
|
|
67
68
|
function createSdk(options = {}) {
|
|
68
69
|
const defaultBaseUrl = typeof window === "undefined" ? undefined : window.location.origin;
|
|
69
70
|
const configuredBaseUrl = options.baseUrl ?? defaultBaseUrl;
|
|
70
|
-
|
|
71
|
-
throw new Error("baseUrl is required when using the OpenCMS SDK outside a browser.");
|
|
72
|
-
}
|
|
73
|
-
const baseUrl = configuredBaseUrl.replace(/\/$/, "");
|
|
71
|
+
const baseUrl = configuredBaseUrl?.replace(/\/$/, "") ?? "";
|
|
74
72
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
75
73
|
const projectId = options.projectId;
|
|
76
74
|
const environment = options.environment ?? "development";
|
|
@@ -81,6 +79,9 @@ function createSdk(options = {}) {
|
|
|
81
79
|
if (token) {
|
|
82
80
|
headers.set("Authorization", `Bearer ${token}`);
|
|
83
81
|
}
|
|
82
|
+
if (!baseUrl && typeof window === "undefined") {
|
|
83
|
+
throw new Error("baseUrl is required when making OpenCMS SDK requests outside a browser.");
|
|
84
|
+
}
|
|
84
85
|
const response = await fetcher(`${baseUrl}${path}`, {
|
|
85
86
|
...init,
|
|
86
87
|
headers
|
|
@@ -103,18 +104,21 @@ function createSdk(options = {}) {
|
|
|
103
104
|
method: "POST",
|
|
104
105
|
headers: { "Content-Type": "application/json" },
|
|
105
106
|
body: JSON.stringify(input)
|
|
107
|
+
}),
|
|
108
|
+
delete: (targetProjectId) => request(`/api/projects/${targetProjectId}`, {
|
|
109
|
+
method: "DELETE"
|
|
106
110
|
})
|
|
107
111
|
},
|
|
108
112
|
schema: {
|
|
109
113
|
get: () => {
|
|
110
114
|
if (!projectId)
|
|
111
115
|
throw new Error("projectId is required to get the schema");
|
|
112
|
-
return request(`/api/projects/${projectId}/schema`);
|
|
116
|
+
return request(`/api/projects/${projectId}/schema?environment=${environment}`);
|
|
113
117
|
},
|
|
114
118
|
update: (schema) => {
|
|
115
119
|
if (!projectId)
|
|
116
120
|
throw new Error("projectId is required to update the schema");
|
|
117
|
-
return request(`/api/projects/${projectId}/schema`, {
|
|
121
|
+
return request(`/api/projects/${projectId}/schema?environment=${environment}`, {
|
|
118
122
|
method: "PUT",
|
|
119
123
|
headers: { "Content-Type": "application/json" },
|
|
120
124
|
body: JSON.stringify(schema)
|
|
@@ -205,13 +209,30 @@ function createSdk(options = {}) {
|
|
|
205
209
|
};
|
|
206
210
|
}
|
|
207
211
|
|
|
212
|
+
// src/vercel.ts
|
|
213
|
+
function vercelDeploymentArgs({
|
|
214
|
+
apiUrl,
|
|
215
|
+
projectId,
|
|
216
|
+
token
|
|
217
|
+
}) {
|
|
218
|
+
const connectionVariables = [
|
|
219
|
+
`NEXT_PUBLIC_OPENCMS_PROJECT_ID=${projectId}`,
|
|
220
|
+
`OPENCMS_API_URL=${apiUrl}`,
|
|
221
|
+
"OPENCMS_ENVIRONMENT=production"
|
|
222
|
+
];
|
|
223
|
+
return [
|
|
224
|
+
"vercel",
|
|
225
|
+
"--prod",
|
|
226
|
+
"--yes",
|
|
227
|
+
"--token",
|
|
228
|
+
token,
|
|
229
|
+
...connectionVariables.flatMap((value) => ["--build-env", value, "--env", value])
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
|
|
208
233
|
// src/index.ts
|
|
209
|
-
var hostedUrl = "https://web-eta-ten-16.vercel.app";
|
|
210
|
-
var dashboardUrl = process.env.OPENCMS_DASHBOARD_URL ?? hostedUrl;
|
|
211
|
-
var apiUrl = process.env.OPENCMS_API_URL ?? hostedUrl;
|
|
212
234
|
var configRoot = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
|
|
213
235
|
var configPath = join(configRoot, "opencms", "config.json");
|
|
214
|
-
var legacyLocalApiUrl = "http://localhost:3000";
|
|
215
236
|
async function readConfig() {
|
|
216
237
|
if (!await fileExists(configPath))
|
|
217
238
|
return {};
|
|
@@ -222,19 +243,30 @@ async function readConfig() {
|
|
|
222
243
|
}
|
|
223
244
|
}
|
|
224
245
|
async function writeConfig(config) {
|
|
225
|
-
|
|
246
|
+
const directory = join(configRoot, "opencms");
|
|
247
|
+
await mkdir(directory, { recursive: true, mode: 448 });
|
|
226
248
|
await writeFile(configPath, `${JSON.stringify(config, null, 2)}
|
|
227
|
-
`, "utf8");
|
|
249
|
+
`, { encoding: "utf8", mode: 384 });
|
|
250
|
+
if (process.platform !== "win32") {
|
|
251
|
+
await chmod(directory, 448);
|
|
252
|
+
await chmod(configPath, 384);
|
|
253
|
+
}
|
|
228
254
|
}
|
|
229
255
|
function tokenFor(config) {
|
|
230
256
|
return process.env.OPENCMS_CLERK_TOKEN ?? config.token ?? null;
|
|
231
257
|
}
|
|
258
|
+
function requireEndpoint(endpoint) {
|
|
259
|
+
const normalized = endpoint?.trim().replace(/\/$/, "");
|
|
260
|
+
if (!normalized) {
|
|
261
|
+
throw new Error("OpenCMS endpoint is not configured. Set OPENCMS_URL to your OpenCMS dashboard/API origin and try again.");
|
|
262
|
+
}
|
|
263
|
+
return normalized;
|
|
264
|
+
}
|
|
232
265
|
function apiUrlFor(config) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return apiUrl;
|
|
266
|
+
return requireEndpoint(process.env.OPENCMS_URL ?? process.env.OPENCMS_API_URL ?? config?.apiUrl ?? process.env.OPENCMS_DASHBOARD_URL);
|
|
267
|
+
}
|
|
268
|
+
function dashboardUrlFor(config) {
|
|
269
|
+
return requireEndpoint(process.env.OPENCMS_URL ?? process.env.OPENCMS_DASHBOARD_URL ?? process.env.OPENCMS_API_URL ?? config?.apiUrl);
|
|
238
270
|
}
|
|
239
271
|
function sdk(config, projectId) {
|
|
240
272
|
return createSdk({
|
|
@@ -269,7 +301,8 @@ async function openBrowser(url) {
|
|
|
269
301
|
const command = process.platform === "darwin" ? ["open", url] : process.platform === "win32" ? ["cmd", "/c", "start", "", url] : ["xdg-open", url];
|
|
270
302
|
await runCommand(command[0], command.slice(1));
|
|
271
303
|
}
|
|
272
|
-
async function browserLogin() {
|
|
304
|
+
async function browserLogin(config) {
|
|
305
|
+
const loginState = randomUUID();
|
|
273
306
|
let resolveToken = () => {
|
|
274
307
|
return;
|
|
275
308
|
};
|
|
@@ -287,14 +320,24 @@ async function browserLogin() {
|
|
|
287
320
|
response.end("Waiting for opencms login.");
|
|
288
321
|
return;
|
|
289
322
|
}
|
|
323
|
+
if (url.searchParams.get("state") !== loginState) {
|
|
324
|
+
response.writeHead(400, { "Content-Type": "text/plain", "Cache-Control": "no-store" });
|
|
325
|
+
response.end("Invalid login state.");
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
290
328
|
const token = url.searchParams.get("token");
|
|
291
329
|
if (!token) {
|
|
292
|
-
response.writeHead(400, { "Content-Type": "text/plain" });
|
|
330
|
+
response.writeHead(400, { "Content-Type": "text/plain", "Cache-Control": "no-store" });
|
|
293
331
|
response.end("Missing login token.");
|
|
294
332
|
return;
|
|
295
333
|
}
|
|
296
334
|
resolveToken(token);
|
|
297
|
-
response.writeHead(200, {
|
|
335
|
+
response.writeHead(200, {
|
|
336
|
+
"Content-Type": "text/html",
|
|
337
|
+
"Cache-Control": "no-store",
|
|
338
|
+
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'",
|
|
339
|
+
"X-Content-Type-Options": "nosniff"
|
|
340
|
+
});
|
|
298
341
|
response.end("<h1>OpenCMS login complete</h1><p>You can close this window.</p>");
|
|
299
342
|
});
|
|
300
343
|
await new Promise((resolve2, reject) => {
|
|
@@ -305,7 +348,7 @@ async function browserLogin() {
|
|
|
305
348
|
if (!address || typeof address === "string")
|
|
306
349
|
throw new Error("Unable to start the login callback server.");
|
|
307
350
|
const callback = `http://127.0.0.1:${address.port}/callback`;
|
|
308
|
-
const loginUrl = `${
|
|
351
|
+
const loginUrl = `${dashboardUrlFor(config)}/cli/login?redirect_uri=${encodeURIComponent(callback)}&state=${encodeURIComponent(loginState)}`;
|
|
309
352
|
console.log(`Opening ${loginUrl}`);
|
|
310
353
|
try {
|
|
311
354
|
await openBrowser(loginUrl);
|
|
@@ -324,7 +367,7 @@ async function ensureToken(config) {
|
|
|
324
367
|
const token = tokenFor(config);
|
|
325
368
|
if (token)
|
|
326
369
|
return token;
|
|
327
|
-
const loggedInToken = await browserLogin();
|
|
370
|
+
const loggedInToken = await browserLogin(config);
|
|
328
371
|
await writeConfig({ ...config, token: loggedInToken, apiUrl: apiUrlFor(config) });
|
|
329
372
|
return loggedInToken;
|
|
330
373
|
}
|
|
@@ -333,10 +376,22 @@ async function reauthenticate(config) {
|
|
|
333
376
|
throw new Error("OPENCMS_CLERK_TOKEN was rejected or expired. Provide a fresh token or unset the variable to use browser login.");
|
|
334
377
|
}
|
|
335
378
|
console.log("Your OpenCMS session has expired. Opening browser login…");
|
|
336
|
-
const loggedInToken = await browserLogin();
|
|
379
|
+
const loggedInToken = await browserLogin(config);
|
|
337
380
|
await writeConfig({ ...config, token: loggedInToken, apiUrl: apiUrlFor(config) });
|
|
338
381
|
return loggedInToken;
|
|
339
382
|
}
|
|
383
|
+
async function withReauthentication(operation) {
|
|
384
|
+
let config = await readConfig();
|
|
385
|
+
try {
|
|
386
|
+
return await operation(config);
|
|
387
|
+
} catch (error) {
|
|
388
|
+
if (!(error instanceof OpenCmsApiError) || error.status !== 401)
|
|
389
|
+
throw error;
|
|
390
|
+
await reauthenticate(config);
|
|
391
|
+
config = await readConfig();
|
|
392
|
+
return operation(config);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
340
395
|
async function login() {
|
|
341
396
|
const config = await readConfig();
|
|
342
397
|
if (process.env.OPENCMS_CLERK_TOKEN) {
|
|
@@ -344,12 +399,24 @@ async function login() {
|
|
|
344
399
|
console.log("Saved OPENCMS_CLERK_TOKEN for local CLI use.");
|
|
345
400
|
return;
|
|
346
401
|
}
|
|
347
|
-
const loggedInToken = await browserLogin();
|
|
402
|
+
const loggedInToken = await browserLogin(config);
|
|
348
403
|
await writeConfig({ ...config, token: loggedInToken, apiUrl: apiUrlFor(config) });
|
|
349
404
|
console.log("Logged in to OpenCMS.");
|
|
350
405
|
}
|
|
351
406
|
async function logout() {
|
|
352
407
|
const config = await readConfig();
|
|
408
|
+
if (config.token?.startsWith("ocms_")) {
|
|
409
|
+
try {
|
|
410
|
+
const response = await fetch(`${apiUrlFor(config)}/api/cli/tokens/current`, {
|
|
411
|
+
method: "DELETE",
|
|
412
|
+
headers: { Authorization: `Bearer ${config.token}` }
|
|
413
|
+
});
|
|
414
|
+
if (!response.ok)
|
|
415
|
+
console.warn("OpenCMS: The remote CLI session could not be revoked.");
|
|
416
|
+
} catch {
|
|
417
|
+
console.warn("OpenCMS: The remote CLI session could not be revoked.");
|
|
418
|
+
}
|
|
419
|
+
}
|
|
353
420
|
const { token: _token, ...withoutToken } = config;
|
|
354
421
|
await writeConfig(withoutToken);
|
|
355
422
|
console.log("Logged out of OpenCMS.");
|
|
@@ -381,8 +448,8 @@ async function ensureCmsDirectory(destination, project, baseUrl) {
|
|
|
381
448
|
const configFile = join(cmsDirectory, "opencms.ts");
|
|
382
449
|
if (!await fileExists(configFile)) {
|
|
383
450
|
await writeFile(configFile, `export const opencms = {
|
|
384
|
-
projectId: process.env.NEXT_PUBLIC_OPENCMS_PROJECT_ID ?? "
|
|
385
|
-
apiUrl: process.env.OPENCMS_API_URL ?? "
|
|
451
|
+
projectId: process.env.NEXT_PUBLIC_OPENCMS_PROJECT_ID ?? "",
|
|
452
|
+
apiUrl: process.env.OPENCMS_API_URL ?? "",
|
|
386
453
|
environment: process.env.OPENCMS_ENVIRONMENT ?? "development",
|
|
387
454
|
} as const;
|
|
388
455
|
`, "utf8");
|
|
@@ -428,6 +495,9 @@ async function createProject() {
|
|
|
428
495
|
const name = await ask("Project name: ");
|
|
429
496
|
if (!name)
|
|
430
497
|
throw new Error("A project name is required.");
|
|
498
|
+
const destination = resolve(process.cwd(), slugify(name));
|
|
499
|
+
if (await fileExists(destination))
|
|
500
|
+
throw new Error(`Destination already exists: ${destination}`);
|
|
431
501
|
let currentConfig = await readConfig();
|
|
432
502
|
let client = sdk(currentConfig);
|
|
433
503
|
let project;
|
|
@@ -441,17 +511,26 @@ async function createProject() {
|
|
|
441
511
|
client = sdk(currentConfig);
|
|
442
512
|
project = await client.projects.create({ name });
|
|
443
513
|
}
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
514
|
+
const baseUrl = apiUrlFor(currentConfig);
|
|
515
|
+
try {
|
|
516
|
+
await pullTemplate(destination);
|
|
517
|
+
await writeProjectEnv(destination, project, baseUrl);
|
|
518
|
+
await ensureCmsDirectory(destination, project, baseUrl);
|
|
519
|
+
await installDependencies(destination);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
await rm(destination, { recursive: true, force: true });
|
|
522
|
+
try {
|
|
523
|
+
await client.projects.delete(project.id);
|
|
524
|
+
} catch {
|
|
525
|
+
console.error(`OpenCMS: Local setup failed and cloud rollback also failed. Project ID: ${project.id}`);
|
|
526
|
+
}
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
450
529
|
await writeConfig({ ...await readConfig(), projectId: project.id, apiUrl: baseUrl });
|
|
451
530
|
console.log(`
|
|
452
531
|
Created ${project.name}.`);
|
|
453
532
|
console.log(`Project ID: ${project.id}`);
|
|
454
|
-
console.log(`Dashboard: ${
|
|
533
|
+
console.log(`Dashboard: ${dashboardUrlFor(await readConfig())}/dashboard/${project.id}`);
|
|
455
534
|
console.log(`
|
|
456
535
|
Next steps:
|
|
457
536
|
cd ${slugify(project.name)}
|
|
@@ -461,16 +540,11 @@ Next steps:
|
|
|
461
540
|
async function runDev() {
|
|
462
541
|
const config = await readConfig();
|
|
463
542
|
await ensureToken(config);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
} catch (error) {
|
|
467
|
-
if (!(error instanceof OpenCmsApiError) || error.status !== 401)
|
|
468
|
-
throw error;
|
|
469
|
-
await reauthenticate(await readConfig());
|
|
470
|
-
await syncLocalSchema(await projectIdFromEnv(), await readConfig());
|
|
471
|
-
}
|
|
543
|
+
const projectId = await projectIdFromEnv();
|
|
544
|
+
await withReauthentication((currentConfig) => syncLocalSchema(projectId, currentConfig));
|
|
472
545
|
const manager = await packageManager(process.cwd());
|
|
473
|
-
const
|
|
546
|
+
const script = await nextDevScript(process.cwd());
|
|
547
|
+
const command = manager[0] === "npm" ? ["npm", "run", script] : manager[0] === "pnpm" ? ["pnpm", script] : manager[0] === "yarn" ? ["yarn", script] : ["bun", "run", script];
|
|
474
548
|
process.exit(await runCommand(command[0], command.slice(1), {
|
|
475
549
|
cwd: process.cwd(),
|
|
476
550
|
env: {
|
|
@@ -481,6 +555,24 @@ async function runDev() {
|
|
|
481
555
|
inherit: true
|
|
482
556
|
}));
|
|
483
557
|
}
|
|
558
|
+
async function nextDevScript(destination) {
|
|
559
|
+
const packagePath = join(destination, "package.json");
|
|
560
|
+
if (!await fileExists(packagePath))
|
|
561
|
+
return "dev";
|
|
562
|
+
try {
|
|
563
|
+
const packageJson = JSON.parse(await readFile(packagePath, "utf8"));
|
|
564
|
+
if (typeof packageJson.scripts?.["dev:next"] === "string")
|
|
565
|
+
return "dev:next";
|
|
566
|
+
if (typeof packageJson.scripts?.dev === "string" && /opencms(?:@[^\s]+)?\s+dev/.test(packageJson.scripts.dev)) {
|
|
567
|
+
throw new Error("This OpenCMS template is missing its dev:next script. Update the template before running opencms dev.");
|
|
568
|
+
}
|
|
569
|
+
} catch (error) {
|
|
570
|
+
if (error instanceof SyntaxError)
|
|
571
|
+
throw new Error("package.json is not valid JSON.");
|
|
572
|
+
throw error;
|
|
573
|
+
}
|
|
574
|
+
return "dev";
|
|
575
|
+
}
|
|
484
576
|
async function syncLocalSchema(projectId, config) {
|
|
485
577
|
if (!projectId)
|
|
486
578
|
return;
|
|
@@ -511,24 +603,20 @@ async function deploy() {
|
|
|
511
603
|
const projectId = await projectIdFromEnv() ?? config.projectId;
|
|
512
604
|
if (!projectId)
|
|
513
605
|
throw new Error("No OpenCMS project is configured in this directory.");
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
await syncLocalSchema(projectId, await readConfig());
|
|
517
|
-
deployment = await sdk(await readConfig()).deploy(projectId);
|
|
518
|
-
} catch (error) {
|
|
519
|
-
if (!(error instanceof OpenCmsApiError) || error.status !== 401)
|
|
520
|
-
throw error;
|
|
521
|
-
await reauthenticate(await readConfig());
|
|
522
|
-
await syncLocalSchema(projectId, await readConfig());
|
|
523
|
-
deployment = await sdk(await readConfig()).deploy(projectId);
|
|
524
|
-
}
|
|
525
|
-
console.log(`Deployed ${deployment.sourceEnvironment} content to ${deployment.targetEnvironment}.`);
|
|
606
|
+
await withReauthentication((currentConfig) => syncLocalSchema(projectId, currentConfig));
|
|
526
607
|
if (process.env.VERCEL_TOKEN) {
|
|
527
608
|
console.log("Deploying the application to Vercel…");
|
|
528
|
-
|
|
609
|
+
const args = vercelDeploymentArgs({
|
|
610
|
+
apiUrl: apiUrlFor(await readConfig()),
|
|
611
|
+
projectId,
|
|
612
|
+
token: process.env.VERCEL_TOKEN
|
|
613
|
+
});
|
|
614
|
+
if (await runCommand("npx", args, { cwd: process.cwd(), inherit: true }) !== 0) {
|
|
529
615
|
throw new Error("Vercel deployment failed.");
|
|
530
616
|
}
|
|
531
617
|
}
|
|
618
|
+
const deployment = await withReauthentication((currentConfig) => sdk(currentConfig).deploy(projectId));
|
|
619
|
+
console.log(`Deployed ${deployment.sourceEnvironment} content to ${deployment.targetEnvironment}.`);
|
|
532
620
|
}
|
|
533
621
|
function printHelp() {
|
|
534
622
|
console.log(`OpenCMS CLI
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maker-or/opencms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "The developer-first CLI for OpenCMS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"build": "bun build src/index.ts --outfile dist/index.js --target=node --format=esm",
|
|
29
29
|
"dev": "bun --watch src/index.ts",
|
|
30
30
|
"start": "node dist/index.js",
|
|
31
|
+
"test": "bun run build && bun test",
|
|
31
32
|
"typecheck": "tsc --noEmit",
|
|
32
33
|
"prepack": "bun run build"
|
|
33
34
|
},
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"node": ">=20"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
39
|
+
"@types/bun": "latest",
|
|
38
40
|
"@types/node": "^22.13.10",
|
|
39
41
|
"typescript": "^5.8.3"
|
|
40
42
|
}
|