@classytic/arc 2.3.0 → 2.4.2
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 +187 -18
- package/bin/arc.js +11 -3
- package/dist/BaseController-CkM5dUh_.mjs +1031 -0
- package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
- package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
- package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
- package/dist/adapters/index.d.mts +3 -5
- package/dist/adapters/index.mjs +2 -3
- package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
- package/dist/audit/index.d.mts +4 -7
- package/dist/audit/index.mjs +2 -29
- package/dist/audit/mongodb.d.mts +1 -4
- package/dist/audit/mongodb.mjs +2 -3
- package/dist/auth/index.d.mts +7 -9
- package/dist/auth/index.mjs +65 -63
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/auth/redis-session.mjs +1 -2
- package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
- package/dist/cache/index.d.mts +23 -23
- package/dist/cache/index.mjs +4 -6
- package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
- package/dist/chunk-BpYLSNr0.mjs +14 -0
- package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
- package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
- package/dist/cli/commands/describe.mjs +24 -7
- package/dist/cli/commands/docs.mjs +6 -7
- package/dist/cli/commands/doctor.d.mts +10 -0
- package/dist/cli/commands/doctor.mjs +156 -0
- package/dist/cli/commands/generate.mjs +66 -17
- package/dist/cli/commands/init.mjs +315 -45
- package/dist/cli/commands/introspect.mjs +2 -4
- package/dist/cli/index.d.mts +1 -10
- package/dist/cli/index.mjs +4 -153
- package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
- package/dist/core/index.d.mts +3 -5
- package/dist/core/index.mjs +5 -4
- package/dist/core-C1XCMtqM.mjs +185 -0
- package/dist/{createApp-CgKOPhA4.mjs → createApp-ByWNRsZj.mjs} +64 -35
- package/dist/{defineResource-DWbpJYtm.mjs → defineResource-D9aY5Cy6.mjs} +108 -1157
- package/dist/discovery/index.mjs +37 -5
- package/dist/docs/index.d.mts +6 -9
- package/dist/docs/index.mjs +3 -21
- package/dist/dynamic/index.d.mts +93 -0
- package/dist/dynamic/index.mjs +122 -0
- package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
- package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
- package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
- package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
- package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
- package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
- package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
- package/dist/events/index.d.mts +72 -7
- package/dist/events/index.mjs +216 -4
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +19 -7
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +3 -4
- package/dist/factory/index.d.mts +23 -9
- package/dist/factory/index.mjs +48 -3
- package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
- package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
- package/dist/hooks/index.d.mts +1 -3
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +5 -5
- package/dist/idempotency/index.mjs +3 -7
- package/dist/idempotency/mongodb.d.mts +1 -1
- package/dist/idempotency/mongodb.mjs +4 -5
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +2 -5
- package/dist/{fastifyAdapter-6b_eRDBw.d.mts → index-BL8CaQih.d.mts} +56 -57
- package/dist/index-Diqcm14c.d.mts +369 -0
- package/dist/{prisma-Dy5S5F5i.d.mts → index-yhxyjqNb.d.mts} +4 -5
- package/dist/index.d.mts +100 -105
- package/dist/index.mjs +85 -58
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +8 -4
- package/dist/integrations/index.d.mts +4 -2
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs.d.mts +2 -2
- package/dist/integrations/jobs.mjs +63 -14
- package/dist/integrations/mcp/index.d.mts +219 -0
- package/dist/integrations/mcp/index.mjs +572 -0
- package/dist/integrations/mcp/testing.d.mts +53 -0
- package/dist/integrations/mcp/testing.mjs +104 -0
- package/dist/integrations/streamline.mjs +39 -19
- package/dist/integrations/webhooks.d.mts +56 -0
- package/dist/integrations/webhooks.mjs +139 -0
- package/dist/integrations/websocket-redis.d.mts +46 -0
- package/dist/integrations/websocket-redis.mjs +50 -0
- package/dist/integrations/websocket.d.mts +68 -2
- package/dist/integrations/websocket.mjs +96 -13
- package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
- package/dist/interface-DGmPxakH.d.mts +2213 -0
- package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
- package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
- package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
- package/dist/metrics-Csh4nsvv.mjs +224 -0
- package/dist/migrations/index.d.mts +113 -44
- package/dist/migrations/index.mjs +84 -102
- package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
- package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
- package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
- package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
- package/dist/org/index.d.mts +12 -14
- package/dist/org/index.mjs +92 -119
- package/dist/org/types.d.mts +2 -2
- package/dist/org/types.mjs +1 -1
- package/dist/permissions/index.d.mts +4 -278
- package/dist/permissions/index.mjs +4 -579
- package/dist/permissions-CA5zg0yK.mjs +751 -0
- package/dist/plugins/index.d.mts +104 -107
- package/dist/plugins/index.mjs +203 -313
- package/dist/plugins/response-cache.mjs +4 -69
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +24 -11
- package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
- package/dist/policies/index.d.mts +2 -2
- package/dist/policies/index.mjs +80 -83
- package/dist/presets/index.d.mts +26 -19
- package/dist/presets/index.mjs +2 -142
- package/dist/presets/multiTenant.d.mts +1 -4
- package/dist/presets/multiTenant.mjs +4 -6
- package/dist/presets-C9QXJV1u.mjs +422 -0
- package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
- package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
- package/dist/queryParser-CgCtsjti.mjs +352 -0
- package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
- package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
- package/dist/registry/index.d.mts +1 -4
- package/dist/registry/index.mjs +3 -4
- package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
- package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
- package/dist/resourceToTools-PMFE8HIv.mjs +533 -0
- package/dist/rpc/index.d.mts +90 -0
- package/dist/rpc/index.mjs +248 -0
- package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
- package/dist/schemas/index.d.mts +30 -30
- package/dist/schemas/index.mjs +2 -4
- package/dist/scope/index.d.mts +13 -2
- package/dist/scope/index.mjs +18 -5
- package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
- package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
- package/dist/testing/index.d.mts +551 -567
- package/dist/testing/index.mjs +1744 -1799
- package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
- package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
- package/dist/types/index.d.mts +4 -946
- package/dist/types/index.mjs +2 -4
- package/dist/types-BJmgxNbF.d.mts +275 -0
- package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
- package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
- package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
- package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
- package/dist/utils/index.d.mts +254 -351
- package/dist/utils/index.mjs +7 -6
- package/dist/utils-Dc0WhlIl.mjs +594 -0
- package/dist/versioning-BzfeHmhj.mjs +37 -0
- package/package.json +44 -10
- package/skills/arc/SKILL.md +518 -0
- package/skills/arc/references/auth.md +250 -0
- package/skills/arc/references/events.md +272 -0
- package/skills/arc/references/integrations.md +385 -0
- package/skills/arc/references/mcp.md +431 -0
- package/skills/arc/references/production.md +610 -0
- package/skills/arc/references/testing.md +183 -0
- package/dist/audited-CGdLiSlE.mjs +0 -140
- package/dist/chunk-C7Uep-_p.mjs +0 -20
- package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
- package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
- package/dist/interface-BtdYtQUA.d.mts +0 -1114
- package/dist/presets-BTeYbw7h.d.mts +0 -57
- package/dist/presets-CeFtfDR8.mjs +0 -119
- /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
- /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
- /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { accessSync } from "node:fs";
|
|
2
1
|
import * as path from "node:path";
|
|
2
|
+
import { accessSync } from "node:fs";
|
|
3
|
+
import { execSync, spawn } from "node:child_process";
|
|
3
4
|
import * as fs from "node:fs/promises";
|
|
4
5
|
import * as readline from "node:readline";
|
|
5
|
-
import { execSync, spawn } from "node:child_process";
|
|
6
|
-
|
|
7
6
|
//#region src/cli/commands/init.ts
|
|
8
7
|
/**
|
|
9
8
|
* Arc CLI - Init Command
|
|
@@ -166,6 +165,20 @@ async function gatherConfig(options) {
|
|
|
166
165
|
if (options.typescript === void 0 && !nonInteractive) typescript = await question("Language [1=TypeScript (recommended), 2=JavaScript]: ") !== "2";
|
|
167
166
|
let edge = options.edge ?? false;
|
|
168
167
|
if (options.edge === void 0 && !nonInteractive) edge = await question("Deployment target [1=Node.js Server (default), 2=Edge/Serverless]: ") === "2";
|
|
168
|
+
if (edge && adapter === "mongokit" && !nonInteractive) {
|
|
169
|
+
console.log("");
|
|
170
|
+
console.log(" ⚠ Edge + MongoKit: Mongoose does NOT work on Cloudflare Workers.");
|
|
171
|
+
console.log(" MongoDB Atlas works with the raw driver (mongodb 6.15+ with nodejs_compat_v2),");
|
|
172
|
+
console.log(" but MongoKit depends on Mongoose. Options:");
|
|
173
|
+
console.log(" 1. Use AWS Lambda / Vercel Serverless (Node.js) — Mongoose works normally");
|
|
174
|
+
console.log(" 2. Use Cloudflare Hyperdrive + PostgreSQL (switch to Prisma/Drizzle adapter)");
|
|
175
|
+
console.log(" 3. Continue with MongoKit — works on Lambda/Vercel, NOT on Cloudflare Workers");
|
|
176
|
+
console.log("");
|
|
177
|
+
if ((await question("Continue with MongoKit? [y/N]: ")).toLowerCase() !== "y") {
|
|
178
|
+
adapter = "custom";
|
|
179
|
+
console.log(" Switched to custom adapter. You can wire Drizzle, Prisma, or the raw MongoDB driver.");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
169
182
|
return {
|
|
170
183
|
name,
|
|
171
184
|
adapter,
|
|
@@ -234,12 +247,18 @@ async function createProjectStructure(projectPath, config) {
|
|
|
234
247
|
files[`src/resources/example/example.schemas.${ext}`] = exampleSchemasTemplate(config);
|
|
235
248
|
files[`tests/example.test.${ext}`] = exampleTestTemplate(config);
|
|
236
249
|
if (config.auth === "jwt") files[`tests/auth.test.${ext}`] = authTestTemplate(config);
|
|
237
|
-
|
|
250
|
+
if (!config.edge) {
|
|
251
|
+
files.Dockerfile = dockerfileTemplate(config);
|
|
252
|
+
files[".dockerignore"] = dockerignoreTemplate();
|
|
253
|
+
files["docker-compose.yml"] = dockerComposeTemplate(config);
|
|
254
|
+
}
|
|
255
|
+
if (config.edge) files["wrangler.toml"] = wranglerTemplate(config);
|
|
256
|
+
files[".arcrc"] = `${JSON.stringify({
|
|
238
257
|
adapter: config.adapter,
|
|
239
258
|
auth: config.auth,
|
|
240
259
|
tenant: config.tenant,
|
|
241
260
|
typescript: config.typescript
|
|
242
|
-
}, null, 2)
|
|
261
|
+
}, null, 2)}\n`;
|
|
243
262
|
for (const [filePath, content] of Object.entries(files)) {
|
|
244
263
|
const fullPath = path.join(projectPath, filePath);
|
|
245
264
|
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
@@ -248,12 +267,27 @@ async function createProjectStructure(projectPath, config) {
|
|
|
248
267
|
}
|
|
249
268
|
}
|
|
250
269
|
function packageJsonTemplate(config) {
|
|
251
|
-
const scripts = config.typescript ? {
|
|
270
|
+
const scripts = config.typescript ? config.edge ? {
|
|
271
|
+
dev: "tsx watch src/index.ts",
|
|
272
|
+
build: "tsc",
|
|
273
|
+
start: "node dist/index.js",
|
|
274
|
+
deploy: "wrangler deploy",
|
|
275
|
+
"deploy:dev": "wrangler dev",
|
|
276
|
+
test: "vitest run",
|
|
277
|
+
"test:watch": "vitest"
|
|
278
|
+
} : {
|
|
252
279
|
dev: "tsx watch src/index.ts",
|
|
253
280
|
build: "tsc",
|
|
254
281
|
start: "node dist/index.js",
|
|
255
282
|
test: "vitest run",
|
|
256
283
|
"test:watch": "vitest"
|
|
284
|
+
} : config.edge ? {
|
|
285
|
+
dev: "node --watch src/index.js",
|
|
286
|
+
start: "node src/index.js",
|
|
287
|
+
deploy: "wrangler deploy",
|
|
288
|
+
"deploy:dev": "wrangler dev",
|
|
289
|
+
test: "vitest run",
|
|
290
|
+
"test:watch": "vitest"
|
|
257
291
|
} : {
|
|
258
292
|
dev: "node --watch src/index.js",
|
|
259
293
|
start: "node src/index.js",
|
|
@@ -335,10 +369,11 @@ node_modules/
|
|
|
335
369
|
dist/
|
|
336
370
|
*.js.map
|
|
337
371
|
|
|
338
|
-
# Environment
|
|
339
|
-
.env
|
|
372
|
+
# Environment (local overrides — never commit secrets)
|
|
340
373
|
.env.local
|
|
341
374
|
.env.*.local
|
|
375
|
+
# Uncomment if your .env contains secrets:
|
|
376
|
+
# .env
|
|
342
377
|
|
|
343
378
|
# IDE
|
|
344
379
|
.vscode/
|
|
@@ -359,7 +394,15 @@ coverage/
|
|
|
359
394
|
`;
|
|
360
395
|
}
|
|
361
396
|
function envExampleTemplate(config) {
|
|
362
|
-
let content = `#
|
|
397
|
+
let content = `# Environment Files (Next.js-style priority):
|
|
398
|
+
# .env.local → machine-specific overrides (gitignored)
|
|
399
|
+
# .env.production → production defaults
|
|
400
|
+
# .env.development → development defaults (or .env.dev)
|
|
401
|
+
# .env → shared defaults (fallback)
|
|
402
|
+
#
|
|
403
|
+
# Tip: Copy this file to .env.local for local development
|
|
404
|
+
|
|
405
|
+
# Server
|
|
363
406
|
PORT=8040
|
|
364
407
|
HOST=0.0.0.0
|
|
365
408
|
NODE_ENV=development
|
|
@@ -443,7 +486,7 @@ tests/
|
|
|
443
486
|
|
|
444
487
|
### Entry Points
|
|
445
488
|
|
|
446
|
-
- **\`src/index.${ext}\`** - HTTP server entry point
|
|
489
|
+
- **\`src/index.${ext}\`** - ${config.edge ? "Edge/serverless fetch handler (Cloudflare Workers, Lambda, Vercel)" : "HTTP server entry point"}
|
|
447
490
|
- **\`src/app.${ext}\`** - App factory (import for workers/tests)
|
|
448
491
|
|
|
449
492
|
\`\`\`${config.typescript ? "typescript" : "javascript"}
|
|
@@ -501,12 +544,14 @@ arc introspect
|
|
|
501
544
|
arc docs
|
|
502
545
|
\`\`\`
|
|
503
546
|
|
|
504
|
-
## Environment Files
|
|
547
|
+
## Environment Files (Next.js-style)
|
|
548
|
+
|
|
549
|
+
Priority (first loaded wins):
|
|
550
|
+
1. \`.env.local\` — Machine-specific overrides (gitignored)
|
|
551
|
+
2. \`.env.{environment}\` — e.g., \`.env.production\`, \`.env.development\`, \`.env.test\`
|
|
552
|
+
3. \`.env\` — Shared defaults (fallback)
|
|
505
553
|
|
|
506
|
-
|
|
507
|
-
- \`.env.test\` / \`.env.qa\` - Testing / QA
|
|
508
|
-
- \`.env.production\` / \`.env.prod\` - Production
|
|
509
|
-
- \`.env\` - Fallback
|
|
554
|
+
Short forms also supported: \`.env.prod\`, \`.env.dev\`, \`.env.test\`
|
|
510
555
|
|
|
511
556
|
## API Documentation
|
|
512
557
|
|
|
@@ -526,10 +571,29 @@ API documentation is available via Scalar UI:
|
|
|
526
571
|
| POST | /examples | Create |
|
|
527
572
|
| PATCH | /examples/:id | Update |
|
|
528
573
|
| DELETE | /examples/:id | Delete |
|
|
574
|
+
|
|
575
|
+
## Docker Deployment
|
|
576
|
+
|
|
577
|
+
This project comes ready for containerization:
|
|
578
|
+
|
|
579
|
+
\`\`\`bash
|
|
580
|
+
# Build the production image
|
|
581
|
+
docker build -t ${config.name} .
|
|
582
|
+
|
|
583
|
+
# Run the container
|
|
584
|
+
docker run -p 8040:8040 --env-file .env ${config.name}
|
|
585
|
+
\`\`\`
|
|
586
|
+
|
|
587
|
+
If you're using a database (like MongoDB), you can use Docker Compose to spin up the full stack locally:
|
|
588
|
+
|
|
589
|
+
\`\`\`bash
|
|
590
|
+
docker-compose up -d
|
|
591
|
+
\`\`\`
|
|
529
592
|
`;
|
|
530
593
|
}
|
|
531
594
|
function indexTemplate(config) {
|
|
532
595
|
const ts = config.typescript;
|
|
596
|
+
if (config.edge) return edgeIndexTemplate(config);
|
|
533
597
|
return `/**
|
|
534
598
|
* ${config.name} - Server Entry Point
|
|
535
599
|
* Generated by Arc CLI
|
|
@@ -566,6 +630,48 @@ main().catch((err) => {
|
|
|
566
630
|
});
|
|
567
631
|
`;
|
|
568
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Edge/serverless entry point — exports a Web Standards fetch handler.
|
|
635
|
+
* Works on Cloudflare Workers, AWS Lambda, Vercel Serverless, etc.
|
|
636
|
+
*/
|
|
637
|
+
function edgeIndexTemplate(config) {
|
|
638
|
+
const ts = config.typescript;
|
|
639
|
+
const dbNote = config.adapter === "mongokit" ? ` *\n * NOTE: Mongoose does NOT work on Cloudflare Workers. This entry point\n * works on AWS Lambda and Vercel Serverless (Node.js runtime) where\n * Mongoose/MongoKit works normally. For Cloudflare Workers, switch to\n * Drizzle + Hyperdrive (PostgreSQL) or the raw mongodb driver.` : "";
|
|
640
|
+
return `/**
|
|
641
|
+
* ${config.name} - Edge/Serverless Entry Point
|
|
642
|
+
* Generated by Arc CLI
|
|
643
|
+
*
|
|
644
|
+
* Exports a Web Standards fetch handler that works on:
|
|
645
|
+
* - Cloudflare Workers (enable nodejs_compat in wrangler.toml)
|
|
646
|
+
* - AWS Lambda (via fetch-based adapter)
|
|
647
|
+
* - Vercel Serverless Functions
|
|
648
|
+
* - Any runtime supporting the Web Standards Request/Response API
|
|
649
|
+
*
|
|
650
|
+
* No app.listen() — routes through Fastify's .inject() internally.
|
|
651
|
+
${dbNote}
|
|
652
|
+
*/
|
|
653
|
+
|
|
654
|
+
import { toFetchHandler } from '@classytic/arc/factory';
|
|
655
|
+
import { createAppInstance } from './app.js';
|
|
656
|
+
|
|
657
|
+
const app = await createAppInstance();
|
|
658
|
+
const handler = toFetchHandler(app);
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Cloudflare Workers / generic fetch handler
|
|
662
|
+
*/
|
|
663
|
+
export default {
|
|
664
|
+
async fetch(request${ts ? ": Request" : ""})${ts ? ": Promise<Response>" : ""} {
|
|
665
|
+
return handler(request);
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Named export for platforms that expect it (Vercel, AWS Lambda adapters)
|
|
671
|
+
*/
|
|
672
|
+
export { handler };
|
|
673
|
+
`;
|
|
674
|
+
}
|
|
569
675
|
function appTemplate(config) {
|
|
570
676
|
const ts = config.typescript;
|
|
571
677
|
const typeImport = ts ? "import type { FastifyInstance } from 'fastify';\n" : "";
|
|
@@ -582,9 +688,9 @@ import { getAuth } from './auth.js';
|
|
|
582
688
|
*
|
|
583
689
|
* Creates and configures the Fastify app instance.
|
|
584
690
|
* Can be imported by:
|
|
585
|
-
* - index.ts (HTTP server)
|
|
691
|
+
* - index.ts (HTTP server via app.listen, or edge handler via toFetchHandler)
|
|
586
692
|
* - worker.ts (background workers)
|
|
587
|
-
* - tests (integration tests)
|
|
693
|
+
* - tests (integration tests via app.inject)
|
|
588
694
|
*/
|
|
589
695
|
|
|
590
696
|
${typeImport}import config from '#config/index.js';
|
|
@@ -613,6 +719,9 @@ export async function createAppInstance()${ts ? ": Promise<FastifyInstance>" : "
|
|
|
613
719
|
credentials: config.cors.credentials,
|
|
614
720
|
},
|
|
615
721
|
trustProxy: true,
|
|
722
|
+
arcPlugins: {
|
|
723
|
+
metrics: config.env === 'production', // Prometheus /_metrics endpoint
|
|
724
|
+
},
|
|
616
725
|
});
|
|
617
726
|
|
|
618
727
|
// Register app-specific plugins (explicit dependency injection)
|
|
@@ -633,44 +742,64 @@ function envLoaderTemplate(config) {
|
|
|
633
742
|
* Environment Loader
|
|
634
743
|
*
|
|
635
744
|
* MUST be imported FIRST before any other imports.
|
|
636
|
-
* Loads .env files based on NODE_ENV.
|
|
745
|
+
* Loads .env files based on NODE_ENV with Next.js-style priority:
|
|
746
|
+
*
|
|
747
|
+
* .env.local (always loaded first — gitignored, machine-specific overrides)
|
|
748
|
+
* .env.{environment} (e.g., .env.production, .env.dev, .env.test)
|
|
749
|
+
* .env (fallback defaults)
|
|
750
|
+
*
|
|
751
|
+
* Supports both long-form (production, development, test) and
|
|
752
|
+
* short-form (prod, dev, test) env file names.
|
|
637
753
|
*
|
|
638
754
|
* Usage:
|
|
639
|
-
* import '
|
|
755
|
+
* import '#config/env.js'; // First line of entry point
|
|
640
756
|
*/
|
|
641
757
|
|
|
642
758
|
import dotenv from 'dotenv';
|
|
643
759
|
import { existsSync } from 'node:fs';
|
|
644
760
|
import { resolve } from 'node:path';
|
|
645
761
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
762
|
+
${ts ? "type EnvName = 'prod' | 'dev' | 'test';\n" : ""}const ENV_ALIASES${ts ? ": Record<EnvName, string>" : ""} = {
|
|
763
|
+
prod: 'production',
|
|
764
|
+
dev: 'development',
|
|
765
|
+
test: 'test',
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
function normalizeEnv(env${ts ? ": string | undefined" : ""})${ts ? ": EnvName" : ""} {
|
|
769
|
+
const raw = (env || '').toLowerCase();
|
|
770
|
+
if (raw === 'production' || raw === 'prod') return 'prod';
|
|
771
|
+
if (raw === 'test' || raw === 'qa') return 'test';
|
|
653
772
|
return 'dev';
|
|
654
773
|
}
|
|
655
774
|
|
|
656
|
-
// Determine environment
|
|
657
775
|
const env = normalizeEnv(process.env.NODE_ENV);
|
|
776
|
+
const longForm = ENV_ALIASES[env];
|
|
777
|
+
|
|
778
|
+
// Priority: .env.local → .env.{long} → .env.{short} → .env
|
|
779
|
+
// Same convention as Next.js — .env.local always wins, never committed to git
|
|
780
|
+
const candidates = [
|
|
781
|
+
'.env.local',
|
|
782
|
+
\`.env.\${longForm}\`,
|
|
783
|
+
\`.env.\${env}\`,
|
|
784
|
+
'.env',
|
|
785
|
+
].map((f) => resolve(process.cwd(), f));
|
|
786
|
+
|
|
787
|
+
const loaded${ts ? ": string[]" : ""} = [];
|
|
788
|
+
for (const file of candidates) {
|
|
789
|
+
if (existsSync(file)) {
|
|
790
|
+
// override: false means earlier files take priority (first loaded wins)
|
|
791
|
+
dotenv.config({ path: file, override: false });
|
|
792
|
+
loaded.push(file.split(/[\\\\/]/).pop()${ts ? "!" : ""});
|
|
793
|
+
}
|
|
794
|
+
}
|
|
658
795
|
|
|
659
|
-
//
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
dotenv.config({ path: envFile });
|
|
665
|
-
console.log(\`Loaded: .env.\${env}\`);
|
|
666
|
-
} else if (existsSync(defaultEnvFile)) {
|
|
667
|
-
dotenv.config({ path: defaultEnvFile });
|
|
668
|
-
console.log('Loaded: .env');
|
|
669
|
-
} else {
|
|
670
|
-
console.warn('Warning: No .env file found');
|
|
796
|
+
// Only log in development (silent in production/test)
|
|
797
|
+
if (env === 'dev' && loaded.length > 0) {
|
|
798
|
+
console.log(\`env: \${loaded.join(' + ')}\`);
|
|
799
|
+
} else if (loaded.length === 0) {
|
|
800
|
+
console.warn('No .env file found — using process environment only');
|
|
671
801
|
}
|
|
672
802
|
|
|
673
|
-
// Export for reference
|
|
674
803
|
export const ENV = env;
|
|
675
804
|
`;
|
|
676
805
|
}
|
|
@@ -1461,14 +1590,16 @@ import {
|
|
|
1461
1590
|
Repository,
|
|
1462
1591
|
softDeletePlugin,
|
|
1463
1592
|
methodRegistryPlugin,
|
|
1593
|
+
mongoOperationsPlugin,
|
|
1464
1594
|
} from '@classytic/mongokit';
|
|
1465
1595
|
${ts ? "import type { ExampleDocument } from './example.model.js';\n" : ""}import Example from './example.model.js';
|
|
1466
1596
|
|
|
1467
1597
|
class ExampleRepository extends Repository${ts ? "<ExampleDocument>" : ""} {
|
|
1468
1598
|
constructor() {
|
|
1469
1599
|
super(Example, [
|
|
1470
|
-
methodRegistryPlugin(),
|
|
1471
|
-
softDeletePlugin(),
|
|
1600
|
+
methodRegistryPlugin(),
|
|
1601
|
+
softDeletePlugin(),
|
|
1602
|
+
mongoOperationsPlugin(),
|
|
1472
1603
|
]);
|
|
1473
1604
|
}
|
|
1474
1605
|
|
|
@@ -1513,12 +1644,17 @@ function exampleResourceTemplate(config) {
|
|
|
1513
1644
|
*/
|
|
1514
1645
|
|
|
1515
1646
|
import { defineResource } from '@classytic/arc';
|
|
1647
|
+
import { QueryParser } from '@classytic/mongokit';
|
|
1516
1648
|
import { createAdapter } from '#shared/adapter.js';
|
|
1517
1649
|
import { ${config.tenant === "multi" ? "orgStaffPermissions" : "publicReadPermissions"} } from '#shared/permissions.js';
|
|
1518
1650
|
${config.tenant === "multi" ? "import { flexibleMultiTenantPreset } from '#shared/presets/flexible-multi-tenant.js';\n" : ""}import Example${ts ? ", { type ExampleDocument }" : ""} from './example.model.js';
|
|
1519
1651
|
import exampleRepository from './example.repository.js';
|
|
1520
1652
|
import exampleController from './example.controller.js';
|
|
1521
1653
|
|
|
1654
|
+
const queryParser = new QueryParser({
|
|
1655
|
+
allowedFilterFields: ['isActive'],
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1522
1658
|
const exampleResource = defineResource${ts ? "<ExampleDocument>" : ""}({
|
|
1523
1659
|
name: 'example',
|
|
1524
1660
|
displayName: 'Examples',
|
|
@@ -1526,9 +1662,11 @@ const exampleResource = defineResource${ts ? "<ExampleDocument>" : ""}({
|
|
|
1526
1662
|
|
|
1527
1663
|
adapter: createAdapter(Example, exampleRepository),
|
|
1528
1664
|
controller: exampleController,
|
|
1665
|
+
queryParser,
|
|
1529
1666
|
|
|
1530
1667
|
presets: [
|
|
1531
|
-
'softDelete'
|
|
1668
|
+
'softDelete',
|
|
1669
|
+
'bulk',${config.tenant === "multi" ? `
|
|
1532
1670
|
flexibleMultiTenantPreset({ tenantField: 'organizationId' }),` : ""}
|
|
1533
1671
|
],
|
|
1534
1672
|
|
|
@@ -1549,6 +1687,7 @@ export default exampleResource;
|
|
|
1549
1687
|
`;
|
|
1550
1688
|
}
|
|
1551
1689
|
function exampleControllerTemplate(config) {
|
|
1690
|
+
config.typescript;
|
|
1552
1691
|
return `/**
|
|
1553
1692
|
* Example Controller
|
|
1554
1693
|
* Generated by Arc CLI
|
|
@@ -1565,7 +1704,7 @@ import { exampleSchemaOptions } from './example.schemas.js';
|
|
|
1565
1704
|
|
|
1566
1705
|
class ExampleController extends BaseController {
|
|
1567
1706
|
constructor() {
|
|
1568
|
-
super(exampleRepository
|
|
1707
|
+
super(exampleRepository, {
|
|
1569
1708
|
schemaOptions: exampleSchemaOptions,${config.tenant === "multi" ? `
|
|
1570
1709
|
tenantField: 'organizationId', // Configurable tenant field for multi-tenant` : `
|
|
1571
1710
|
// tenantField: 'organizationId', // For multi-tenant apps`}
|
|
@@ -2617,6 +2756,137 @@ Documentation:
|
|
|
2617
2756
|
https://github.com/classytic/arc
|
|
2618
2757
|
`);
|
|
2619
2758
|
}
|
|
2759
|
+
function dockerignoreTemplate() {
|
|
2760
|
+
return `node_modules
|
|
2761
|
+
dist
|
|
2762
|
+
.env
|
|
2763
|
+
.env.*
|
|
2764
|
+
.git
|
|
2765
|
+
.vscode
|
|
2766
|
+
.idea
|
|
2767
|
+
Dockerfile
|
|
2768
|
+
docker-compose.yml
|
|
2769
|
+
coverage
|
|
2770
|
+
npm-debug.log*
|
|
2771
|
+
.DS_Store
|
|
2772
|
+
`;
|
|
2773
|
+
}
|
|
2774
|
+
function dockerfileTemplate(config) {
|
|
2775
|
+
return `# Multi-stage Dockerfile for Arc + Fastify
|
|
2776
|
+
# Optimized for production and caching
|
|
2777
|
+
|
|
2778
|
+
# 1. Build Stage
|
|
2779
|
+
FROM node:22-alpine AS builder
|
|
2780
|
+
WORKDIR /app
|
|
2781
|
+
COPY package*.json ./
|
|
2782
|
+
${config.typescript ? "COPY tsconfig*.json ./" : ""}
|
|
2783
|
+
# If using pnpm, bun, or yarn, adjust the lockfile here
|
|
2784
|
+
RUN npm ci
|
|
2785
|
+
|
|
2786
|
+
COPY . .
|
|
2787
|
+
${config.typescript ? "RUN npm run build" : ""}
|
|
2788
|
+
|
|
2789
|
+
# 2. Production Stage
|
|
2790
|
+
FROM node:22-alpine AS runner
|
|
2791
|
+
WORKDIR /app
|
|
2792
|
+
ENV NODE_ENV=production
|
|
2793
|
+
|
|
2794
|
+
COPY package*.json ./
|
|
2795
|
+
RUN npm ci --only=production
|
|
2796
|
+
|
|
2797
|
+
${config.typescript ? "COPY --from=builder /app/dist ./dist" : "COPY src ./src"}
|
|
2620
2798
|
|
|
2799
|
+
EXPOSE 8040
|
|
2800
|
+
CMD ["npm", "start"]
|
|
2801
|
+
`;
|
|
2802
|
+
}
|
|
2803
|
+
function dockerComposeTemplate(config) {
|
|
2804
|
+
let content = `version: '3.8'
|
|
2805
|
+
|
|
2806
|
+
services:
|
|
2807
|
+
api:
|
|
2808
|
+
build:
|
|
2809
|
+
context: .
|
|
2810
|
+
dockerfile: Dockerfile
|
|
2811
|
+
ports:
|
|
2812
|
+
- "8040:8040"
|
|
2813
|
+
environment:
|
|
2814
|
+
- NODE_ENV=development
|
|
2815
|
+
- PORT=8040
|
|
2816
|
+
- HOST=0.0.0.0`;
|
|
2817
|
+
if (config.adapter === "mongokit") content += `
|
|
2818
|
+
- MONGODB_URI=mongodb://mongo:27017/${config.name}
|
|
2819
|
+
depends_on:
|
|
2820
|
+
- mongo
|
|
2821
|
+
|
|
2822
|
+
mongo:
|
|
2823
|
+
image: mongo:7
|
|
2824
|
+
ports:
|
|
2825
|
+
- "27017:27017"
|
|
2826
|
+
volumes:
|
|
2827
|
+
- mongo-data:/data/db`;
|
|
2828
|
+
content += `
|
|
2829
|
+
|
|
2830
|
+
volumes:`;
|
|
2831
|
+
if (config.adapter === "mongokit") content += `
|
|
2832
|
+
mongo-data:
|
|
2833
|
+
`;
|
|
2834
|
+
return content;
|
|
2835
|
+
}
|
|
2836
|
+
function wranglerTemplate(config) {
|
|
2837
|
+
const entry = config.typescript ? "dist/index.js" : "src/index.js";
|
|
2838
|
+
const compatFlag = config.adapter === "mongokit" ? "nodejs_compat_v2" : "nodejs_compat";
|
|
2839
|
+
let dbConfig = "";
|
|
2840
|
+
if (config.adapter === "mongokit") dbConfig = `
|
|
2841
|
+
# MongoDB Atlas — store URI as a secret:
|
|
2842
|
+
# npx wrangler secret put MONGODB_URI
|
|
2843
|
+
#
|
|
2844
|
+
# IMPORTANT: Mongoose does NOT work on Workers. Use the raw mongodb driver (6.15+).
|
|
2845
|
+
# For Lambda/Vercel (Node.js), Mongoose works normally.
|
|
2846
|
+
`;
|
|
2847
|
+
else dbConfig = `
|
|
2848
|
+
# Database options for Cloudflare Workers:
|
|
2849
|
+
#
|
|
2850
|
+
# PostgreSQL via Hyperdrive (recommended — connection pooling + caching):
|
|
2851
|
+
# npx wrangler hyperdrive create my-db --connection-string="postgres://user:pass@host:5432/db"
|
|
2852
|
+
# Then uncomment:
|
|
2853
|
+
# [[hyperdrive]]
|
|
2854
|
+
# binding = "HYPERDRIVE"
|
|
2855
|
+
# id = "<your-hyperdrive-id>"
|
|
2856
|
+
#
|
|
2857
|
+
# Turso (edge SQLite):
|
|
2858
|
+
# npx wrangler secret put TURSO_URL
|
|
2859
|
+
# npx wrangler secret put TURSO_AUTH_TOKEN
|
|
2860
|
+
#
|
|
2861
|
+
# Neon (serverless PostgreSQL via HTTP):
|
|
2862
|
+
# npx wrangler secret put DATABASE_URL
|
|
2863
|
+
#
|
|
2864
|
+
# D1 (Cloudflare's native SQLite):
|
|
2865
|
+
# [[d1_databases]]
|
|
2866
|
+
# binding = "DB"
|
|
2867
|
+
# database_name = "${config.name}-db"
|
|
2868
|
+
# database_id = "<run: npx wrangler d1 create ${config.name}-db>"
|
|
2869
|
+
`;
|
|
2870
|
+
return `# Cloudflare Workers configuration
|
|
2871
|
+
# Generated by Arc CLI — see https://developers.cloudflare.com/workers/
|
|
2872
|
+
|
|
2873
|
+
name = "${config.name}"
|
|
2874
|
+
main = "${entry}"
|
|
2875
|
+
compatibility_date = "2025-03-20"
|
|
2876
|
+
|
|
2877
|
+
# Required for Arc — enables node:crypto and AsyncLocalStorage
|
|
2878
|
+
compatibility_flags = ["${compatFlag}"]
|
|
2879
|
+
|
|
2880
|
+
[vars]
|
|
2881
|
+
NODE_ENV = "production"
|
|
2882
|
+
|
|
2883
|
+
# Secrets (never commit these — use wrangler secret put):
|
|
2884
|
+
# npx wrangler secret put JWT_SECRET
|
|
2885
|
+
${dbConfig}
|
|
2886
|
+
# Custom domain:
|
|
2887
|
+
# [routes]
|
|
2888
|
+
# { pattern = "api.example.com/*", zone_name = "example.com" }
|
|
2889
|
+
`;
|
|
2890
|
+
}
|
|
2621
2891
|
//#endregion
|
|
2622
|
-
export { init as default, init };
|
|
2892
|
+
export { init as default, init };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { t as ResourceRegistry } from "../../ResourceRegistry-
|
|
1
|
+
import { t as ResourceRegistry } from "../../ResourceRegistry-DeCIFlix.mjs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
-
|
|
5
4
|
//#region src/cli/commands/introspect.ts
|
|
6
5
|
/**
|
|
7
6
|
* Arc CLI - Introspect Command
|
|
@@ -70,6 +69,5 @@ async function introspect(args) {
|
|
|
70
69
|
throw new Error(String(error));
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
|
-
|
|
74
72
|
//#endregion
|
|
75
|
-
export { introspect as default, introspect };
|
|
73
|
+
export { introspect as default, introspect };
|
package/dist/cli/index.d.mts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import describe from "./commands/describe.mjs";
|
|
2
2
|
import { exportDocs } from "./commands/docs.mjs";
|
|
3
|
+
import { doctor } from "./commands/doctor.mjs";
|
|
3
4
|
import generate from "./commands/generate.mjs";
|
|
4
5
|
import init from "./commands/init.mjs";
|
|
5
6
|
import introspect from "./commands/introspect.mjs";
|
|
6
|
-
|
|
7
|
-
//#region src/cli/commands/doctor.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Arc CLI - Doctor Command
|
|
10
|
-
*
|
|
11
|
-
* Health check utility that validates the development environment.
|
|
12
|
-
* Checks Node.js version, dependencies, configuration, and env variables.
|
|
13
|
-
*/
|
|
14
|
-
declare function doctor(_args?: string[]): Promise<void>;
|
|
15
|
-
//#endregion
|
|
16
7
|
export { describe, doctor, exportDocs, generate, init, introspect };
|