@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.
Files changed (175) hide show
  1. package/README.md +187 -18
  2. package/bin/arc.js +11 -3
  3. package/dist/BaseController-CkM5dUh_.mjs +1031 -0
  4. package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
  5. package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
  6. package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
  7. package/dist/adapters/index.d.mts +3 -5
  8. package/dist/adapters/index.mjs +2 -3
  9. package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
  10. package/dist/audit/index.d.mts +4 -7
  11. package/dist/audit/index.mjs +2 -29
  12. package/dist/audit/mongodb.d.mts +1 -4
  13. package/dist/audit/mongodb.mjs +2 -3
  14. package/dist/auth/index.d.mts +7 -9
  15. package/dist/auth/index.mjs +65 -63
  16. package/dist/auth/redis-session.d.mts +1 -1
  17. package/dist/auth/redis-session.mjs +1 -2
  18. package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
  19. package/dist/cache/index.d.mts +23 -23
  20. package/dist/cache/index.mjs +4 -6
  21. package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
  22. package/dist/chunk-BpYLSNr0.mjs +14 -0
  23. package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
  24. package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
  25. package/dist/cli/commands/describe.mjs +24 -7
  26. package/dist/cli/commands/docs.mjs +6 -7
  27. package/dist/cli/commands/doctor.d.mts +10 -0
  28. package/dist/cli/commands/doctor.mjs +156 -0
  29. package/dist/cli/commands/generate.mjs +66 -17
  30. package/dist/cli/commands/init.mjs +315 -45
  31. package/dist/cli/commands/introspect.mjs +2 -4
  32. package/dist/cli/index.d.mts +1 -10
  33. package/dist/cli/index.mjs +4 -153
  34. package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
  35. package/dist/core/index.d.mts +3 -5
  36. package/dist/core/index.mjs +5 -4
  37. package/dist/core-C1XCMtqM.mjs +185 -0
  38. package/dist/{createApp-CgKOPhA4.mjs → createApp-ByWNRsZj.mjs} +64 -35
  39. package/dist/{defineResource-DWbpJYtm.mjs → defineResource-D9aY5Cy6.mjs} +108 -1157
  40. package/dist/discovery/index.mjs +37 -5
  41. package/dist/docs/index.d.mts +6 -9
  42. package/dist/docs/index.mjs +3 -21
  43. package/dist/dynamic/index.d.mts +93 -0
  44. package/dist/dynamic/index.mjs +122 -0
  45. package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
  46. package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
  47. package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
  48. package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
  49. package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
  50. package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
  51. package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
  52. package/dist/events/index.d.mts +72 -7
  53. package/dist/events/index.mjs +216 -4
  54. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  55. package/dist/events/transports/redis-stream-entry.mjs +19 -7
  56. package/dist/events/transports/redis.d.mts +1 -1
  57. package/dist/events/transports/redis.mjs +3 -4
  58. package/dist/factory/index.d.mts +23 -9
  59. package/dist/factory/index.mjs +48 -3
  60. package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
  61. package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
  62. package/dist/hooks/index.d.mts +1 -3
  63. package/dist/hooks/index.mjs +2 -3
  64. package/dist/idempotency/index.d.mts +5 -5
  65. package/dist/idempotency/index.mjs +3 -7
  66. package/dist/idempotency/mongodb.d.mts +1 -1
  67. package/dist/idempotency/mongodb.mjs +4 -5
  68. package/dist/idempotency/redis.d.mts +1 -1
  69. package/dist/idempotency/redis.mjs +2 -5
  70. package/dist/{fastifyAdapter-6b_eRDBw.d.mts → index-BL8CaQih.d.mts} +56 -57
  71. package/dist/index-Diqcm14c.d.mts +369 -0
  72. package/dist/{prisma-Dy5S5F5i.d.mts → index-yhxyjqNb.d.mts} +4 -5
  73. package/dist/index.d.mts +100 -105
  74. package/dist/index.mjs +85 -58
  75. package/dist/integrations/event-gateway.d.mts +1 -1
  76. package/dist/integrations/event-gateway.mjs +8 -4
  77. package/dist/integrations/index.d.mts +4 -2
  78. package/dist/integrations/index.mjs +1 -1
  79. package/dist/integrations/jobs.d.mts +2 -2
  80. package/dist/integrations/jobs.mjs +63 -14
  81. package/dist/integrations/mcp/index.d.mts +219 -0
  82. package/dist/integrations/mcp/index.mjs +572 -0
  83. package/dist/integrations/mcp/testing.d.mts +53 -0
  84. package/dist/integrations/mcp/testing.mjs +104 -0
  85. package/dist/integrations/streamline.mjs +39 -19
  86. package/dist/integrations/webhooks.d.mts +56 -0
  87. package/dist/integrations/webhooks.mjs +139 -0
  88. package/dist/integrations/websocket-redis.d.mts +46 -0
  89. package/dist/integrations/websocket-redis.mjs +50 -0
  90. package/dist/integrations/websocket.d.mts +68 -2
  91. package/dist/integrations/websocket.mjs +96 -13
  92. package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
  93. package/dist/interface-DGmPxakH.d.mts +2213 -0
  94. package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
  95. package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
  96. package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
  97. package/dist/metrics-Csh4nsvv.mjs +224 -0
  98. package/dist/migrations/index.d.mts +113 -44
  99. package/dist/migrations/index.mjs +84 -102
  100. package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
  101. package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
  102. package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
  103. package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
  104. package/dist/org/index.d.mts +12 -14
  105. package/dist/org/index.mjs +92 -119
  106. package/dist/org/types.d.mts +2 -2
  107. package/dist/org/types.mjs +1 -1
  108. package/dist/permissions/index.d.mts +4 -278
  109. package/dist/permissions/index.mjs +4 -579
  110. package/dist/permissions-CA5zg0yK.mjs +751 -0
  111. package/dist/plugins/index.d.mts +104 -107
  112. package/dist/plugins/index.mjs +203 -313
  113. package/dist/plugins/response-cache.mjs +4 -69
  114. package/dist/plugins/tracing-entry.d.mts +1 -1
  115. package/dist/plugins/tracing-entry.mjs +24 -11
  116. package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
  117. package/dist/policies/index.d.mts +2 -2
  118. package/dist/policies/index.mjs +80 -83
  119. package/dist/presets/index.d.mts +26 -19
  120. package/dist/presets/index.mjs +2 -142
  121. package/dist/presets/multiTenant.d.mts +1 -4
  122. package/dist/presets/multiTenant.mjs +4 -6
  123. package/dist/presets-C9QXJV1u.mjs +422 -0
  124. package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
  125. package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
  126. package/dist/queryParser-CgCtsjti.mjs +352 -0
  127. package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
  128. package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
  129. package/dist/registry/index.d.mts +1 -4
  130. package/dist/registry/index.mjs +3 -4
  131. package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
  132. package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
  133. package/dist/resourceToTools-PMFE8HIv.mjs +533 -0
  134. package/dist/rpc/index.d.mts +90 -0
  135. package/dist/rpc/index.mjs +248 -0
  136. package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
  137. package/dist/schemas/index.d.mts +30 -30
  138. package/dist/schemas/index.mjs +2 -4
  139. package/dist/scope/index.d.mts +13 -2
  140. package/dist/scope/index.mjs +18 -5
  141. package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
  142. package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
  143. package/dist/testing/index.d.mts +551 -567
  144. package/dist/testing/index.mjs +1744 -1799
  145. package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
  146. package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
  147. package/dist/types/index.d.mts +4 -946
  148. package/dist/types/index.mjs +2 -4
  149. package/dist/types-BJmgxNbF.d.mts +275 -0
  150. package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
  151. package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
  152. package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
  153. package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
  154. package/dist/utils/index.d.mts +254 -351
  155. package/dist/utils/index.mjs +7 -6
  156. package/dist/utils-Dc0WhlIl.mjs +594 -0
  157. package/dist/versioning-BzfeHmhj.mjs +37 -0
  158. package/package.json +44 -10
  159. package/skills/arc/SKILL.md +518 -0
  160. package/skills/arc/references/auth.md +250 -0
  161. package/skills/arc/references/events.md +272 -0
  162. package/skills/arc/references/integrations.md +385 -0
  163. package/skills/arc/references/mcp.md +431 -0
  164. package/skills/arc/references/production.md +610 -0
  165. package/skills/arc/references/testing.md +183 -0
  166. package/dist/audited-CGdLiSlE.mjs +0 -140
  167. package/dist/chunk-C7Uep-_p.mjs +0 -20
  168. package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
  169. package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
  170. package/dist/interface-BtdYtQUA.d.mts +0 -1114
  171. package/dist/presets-BTeYbw7h.d.mts +0 -57
  172. package/dist/presets-CeFtfDR8.mjs +0 -119
  173. /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
  174. /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
  175. /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
@@ -1,9 +1,8 @@
1
- import { t as ResourceRegistry } from "../../ResourceRegistry-7Ic20ZMw.mjs";
2
- import { t as buildOpenApiSpec } from "../../openapi-9nB_kiuR.mjs";
3
- import { writeFileSync } from "node:fs";
4
- import { resolve } from "node:path";
1
+ import { t as ResourceRegistry } from "../../ResourceRegistry-DeCIFlix.mjs";
2
+ import { t as buildOpenApiSpec } from "../../openapi-CBmZ6EQN.mjs";
3
+ import { dirname, resolve } from "node:path";
5
4
  import { pathToFileURL } from "node:url";
6
-
5
+ import { mkdirSync, writeFileSync } from "node:fs";
7
6
  //#region src/cli/commands/docs.ts
8
7
  /**
9
8
  * Arc CLI - Docs Command
@@ -41,12 +40,12 @@ async function exportDocs(args) {
41
40
  description: "Auto-generated from Arc resources"
42
41
  });
43
42
  const fullPath = resolve(process.cwd(), outputPath);
43
+ mkdirSync(dirname(fullPath), { recursive: true });
44
44
  writeFileSync(fullPath, JSON.stringify(spec, null, 2));
45
45
  console.log(`OpenAPI spec exported to: ${fullPath}`);
46
46
  console.log(`\nResources included: ${resources.length}`);
47
47
  console.log(`Total endpoints: ${Object.keys(spec.paths).length}`);
48
48
  }
49
49
  var docs_default = { exportDocs };
50
-
51
50
  //#endregion
52
- export { docs_default as default, exportDocs };
51
+ export { docs_default as default, exportDocs };
@@ -0,0 +1,10 @@
1
+ //#region src/cli/commands/doctor.d.ts
2
+ /**
3
+ * Arc CLI - Doctor Command
4
+ *
5
+ * Health check utility that validates the development environment.
6
+ * Checks Node.js version, dependencies, configuration, and env variables.
7
+ */
8
+ declare function doctor(_args?: string[]): Promise<void>;
9
+ //#endregion
10
+ export { doctor };
@@ -0,0 +1,156 @@
1
+ import { dirname, join, parse, resolve } from "node:path";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ //#region src/cli/commands/doctor.ts
4
+ /**
5
+ * Arc CLI - Doctor Command
6
+ *
7
+ * Health check utility that validates the development environment.
8
+ * Checks Node.js version, dependencies, configuration, and env variables.
9
+ */
10
+ async function doctor(_args = []) {
11
+ console.log("\nArc Doctor\n");
12
+ const results = [];
13
+ const cwd = process.cwd();
14
+ const nodeVersion = process.versions.node;
15
+ if (parseInt(nodeVersion.split(".")[0] ?? "0", 10) >= 22) results.push({
16
+ status: "pass",
17
+ label: `Node.js ${nodeVersion}`,
18
+ detail: "required: >=22"
19
+ });
20
+ else results.push({
21
+ status: "fail",
22
+ label: `Node.js ${nodeVersion}`,
23
+ detail: "required: >=22"
24
+ });
25
+ const pkg = findPackageJson(cwd);
26
+ const allDeps = {
27
+ ...pkg?.dependencies ?? {},
28
+ ...pkg?.devDependencies ?? {}
29
+ };
30
+ const arcVersion = allDeps["@classytic/arc"];
31
+ if (arcVersion) results.push({
32
+ status: "pass",
33
+ label: `@classytic/arc ${arcVersion}`
34
+ });
35
+ else results.push({
36
+ status: "warn",
37
+ label: "@classytic/arc not found in dependencies"
38
+ });
39
+ const fastifyVersion = allDeps.fastify;
40
+ if (fastifyVersion) {
41
+ const clean = fastifyVersion.replace(/^[\^~>=<]+/, "").split("-")[0] ?? "0.0.0";
42
+ if (parseInt(clean.split(".")[0] ?? "0", 10) >= 5) results.push({
43
+ status: "pass",
44
+ label: `fastify ${fastifyVersion}`,
45
+ detail: "required: ^5.0.0"
46
+ });
47
+ else results.push({
48
+ status: "fail",
49
+ label: `fastify ${fastifyVersion}`,
50
+ detail: "required: ^5.0.0 — Arc requires Fastify 5"
51
+ });
52
+ } else results.push({
53
+ status: "fail",
54
+ label: "fastify not found in dependencies",
55
+ detail: "required: ^5.0.0"
56
+ });
57
+ const tsconfigPath = resolve(cwd, "tsconfig.json");
58
+ if (existsSync(tsconfigPath)) try {
59
+ const stripped = readFileSync(tsconfigPath, "utf-8").replace(/\/\/.*$/gm, "");
60
+ const moduleRes = JSON.parse(stripped)?.compilerOptions?.moduleResolution;
61
+ if (moduleRes && ![
62
+ "nodenext",
63
+ "node16",
64
+ "bundler"
65
+ ].includes(moduleRes.toLowerCase())) results.push({
66
+ status: "warn",
67
+ label: "tsconfig.json found",
68
+ detail: `moduleResolution "${moduleRes}" — recommend "NodeNext" or "Bundler"`
69
+ });
70
+ else results.push({
71
+ status: "pass",
72
+ label: "tsconfig.json found"
73
+ });
74
+ } catch {
75
+ results.push({
76
+ status: "pass",
77
+ label: "tsconfig.json found"
78
+ });
79
+ }
80
+ else results.push({
81
+ status: "warn",
82
+ label: "tsconfig.json not found"
83
+ });
84
+ for (const dep of [
85
+ {
86
+ name: "@fastify/rate-limit",
87
+ purpose: "rate limiting"
88
+ },
89
+ {
90
+ name: "@fastify/helmet",
91
+ purpose: "security headers"
92
+ },
93
+ {
94
+ name: "@fastify/cors",
95
+ purpose: "CORS support"
96
+ }
97
+ ]) if (allDeps[dep.name]) results.push({
98
+ status: "pass",
99
+ label: `${dep.name} installed`
100
+ });
101
+ else results.push({
102
+ status: "warn",
103
+ label: `${dep.name} not installed`,
104
+ detail: `${dep.purpose} disabled`
105
+ });
106
+ if (allDeps["better-auth"]) results.push({
107
+ status: "pass",
108
+ label: `better-auth ${allDeps["better-auth"]}`
109
+ });
110
+ for (const env of [{
111
+ name: "MONGO_URI",
112
+ severity: "warn",
113
+ detail: "required at runtime for MongoDB"
114
+ }, {
115
+ name: "BETTER_AUTH_SECRET",
116
+ severity: "warn",
117
+ detail: "required for Better Auth session encryption"
118
+ }]) if (process.env[env.name]) results.push({
119
+ status: "pass",
120
+ label: `${env.name} set`
121
+ });
122
+ else results.push({
123
+ status: env.severity,
124
+ label: `${env.name} not set`,
125
+ detail: env.detail
126
+ });
127
+ let passCount = 0;
128
+ let warnCount = 0;
129
+ let failCount = 0;
130
+ for (const r of results) {
131
+ const icon = r.status === "pass" ? "[pass]" : r.status === "warn" ? "[warn]" : "[FAIL]";
132
+ const detail = r.detail ? ` (${r.detail})` : "";
133
+ console.log(` ${icon} ${r.label}${detail}`);
134
+ if (r.status === "pass") passCount++;
135
+ else if (r.status === "warn") warnCount++;
136
+ else failCount++;
137
+ }
138
+ console.log(`\n${passCount} passed, ${warnCount} warnings, ${failCount} failures\n`);
139
+ if (failCount > 0) process.exitCode = 1;
140
+ }
141
+ function findPackageJson(dir) {
142
+ let current = resolve(dir);
143
+ const root = parse(current).root;
144
+ while (current !== root) {
145
+ const p = join(current, "package.json");
146
+ try {
147
+ if (existsSync(p)) return JSON.parse(readFileSync(p, "utf-8"));
148
+ } catch {}
149
+ const parent = dirname(current);
150
+ if (parent === current) break;
151
+ current = parent;
152
+ }
153
+ return null;
154
+ }
155
+ //#endregion
156
+ export { doctor };
@@ -1,7 +1,6 @@
1
- import { t as pluralize } from "../../pluralize-CM-jZg7p.mjs";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { t as pluralize } from "../../pluralize-CcT6qF0a.mjs";
3
2
  import { join } from "node:path";
4
-
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
4
  //#region src/cli/commands/generate.ts
6
5
  /**
7
6
  * Arc CLI - Generate Command
@@ -45,7 +44,7 @@ function toCamelCase(pascalName) {
45
44
  function getTemplates(ts, config = {}) {
46
45
  const isMultiTenant = config.tenant === "multi";
47
46
  return {
48
- model: (name, fileName) => {
47
+ model: (name, _fileName) => {
49
48
  const camel = toCamelCase(name);
50
49
  return `/**
51
50
  * ${name} Model
@@ -83,6 +82,7 @@ export default ${name};
83
82
  },
84
83
  repository: (name, fileName) => {
85
84
  const camel = toCamelCase(name);
85
+ const generic = ts ? `<I${name}>` : "";
86
86
  return `/**
87
87
  * ${name} Repository
88
88
  * Generated by Arc CLI
@@ -94,9 +94,9 @@ import {
94
94
  softDeletePlugin,
95
95
  mongoOperationsPlugin,
96
96
  } from '@classytic/mongokit';
97
- import ${name} from './${fileName}.model.js';
97
+ import ${name} from './${fileName}.model.js';${ts ? `\nimport type { I${name} } from './${fileName}.model.js';` : ""}
98
98
 
99
- class ${name}Repository extends Repository {
99
+ class ${name}Repository extends Repository${generic} {
100
100
  constructor() {
101
101
  super(${name}, [
102
102
  methodRegistryPlugin(),
@@ -159,8 +159,14 @@ import { buildCrudSchemasFromModel } from '@classytic/mongokit/utils';
159
159
  const crudSchemas = buildCrudSchemasFromModel(${name}, {
160
160
  strictAdditionalProperties: true,
161
161
  fieldRules: {
162
- // Mark fields as system-managed (excluded from create/update)
162
+ // systemManaged: excluded from create/update schemas
163
163
  // deletedAt: { systemManaged: true },
164
+ // immutable: cannot be updated after creation
165
+ // slug: { immutable: true },
166
+ // immutableAfterCreate: same as immutable (alias)
167
+ // organizationId: { immutableAfterCreate: true },
168
+ // optional: removed from required array
169
+ // description: { optional: true },
164
170
  },
165
171
  query: {
166
172
  filterableFields: {
@@ -175,7 +181,7 @@ export default crudSchemas;
175
181
  resource: (name, fileName) => {
176
182
  const camel = toCamelCase(name);
177
183
  const useMongoKit = config.adapter === "mongokit" || !config.adapter;
178
- const queryParserImport = useMongoKit ? `\nimport { QueryParser } from '@classytic/mongokit';\n\nconst queryParser = new QueryParser();\n` : "";
184
+ const queryParserImport = useMongoKit ? `\nimport { QueryParser } from '@classytic/mongokit';\n\nconst queryParser = new QueryParser({\n allowedFilterFields: ['isActive'],\n});\n` : "";
179
185
  const queryParserConfig = useMongoKit ? `\n queryParser,` : "";
180
186
  return isMultiTenant ? `/**
181
187
  * ${name} Resource
@@ -189,7 +195,7 @@ import ${camel}Repository from './${fileName}.repository.js';${queryParserImport
189
195
 
190
196
  const ${camel}Resource = defineResource${ts ? `<I${name}>` : ""}({
191
197
  name: '${fileName}',
192
- adapter: createMongooseAdapter(${name}, ${camel}Repository),${queryParserConfig}
198
+ adapter: createMongooseAdapter({ model: ${name}, repository: ${camel}Repository }),${queryParserConfig}
193
199
  presets: ['softDelete'],
194
200
  permissions: {
195
201
  list: requireAuth(),
@@ -213,7 +219,7 @@ import ${camel}Repository from './${fileName}.repository.js';${queryParserImport
213
219
 
214
220
  const ${camel}Resource = defineResource${ts ? `<I${name}>` : ""}({
215
221
  name: '${fileName}',
216
- adapter: createMongooseAdapter(${name}, ${camel}Repository),${queryParserConfig}
222
+ adapter: createMongooseAdapter({ model: ${name}, repository: ${camel}Repository }),${queryParserConfig}
217
223
  presets: ['softDelete'],
218
224
  permissions: {
219
225
  list: requireAuth(),
@@ -225,6 +231,42 @@ const ${camel}Resource = defineResource${ts ? `<I${name}>` : ""}({
225
231
  });
226
232
 
227
233
  export default ${camel}Resource;
234
+ `;
235
+ },
236
+ mcp: (name, fileName) => {
237
+ const camel = toCamelCase(name);
238
+ return `/**
239
+ * ${name} MCP Tools
240
+ * Generated by Arc CLI
241
+ *
242
+ * Custom MCP tools for the ${name} domain.
243
+ * CRUD tools (list/get/create/update/delete) are auto-generated by mcpPlugin.
244
+ * Add domain-specific tools here (actions, analytics, workflows).
245
+ */
246
+
247
+ import { defineTool } from '@classytic/arc/mcp';
248
+ ${ts ? "import { z } from 'zod';\n" : "const { z } = require('zod');\n"}
249
+ // Example: domain-specific action tool
250
+ // export const activate${name}Tool = defineTool('activate_${fileName}', {
251
+ // description: 'Activate a ${name.toLowerCase()} by ID',
252
+ // input: { id: z.string().describe('${name} ID') },
253
+ // annotations: { destructiveHint: true, idempotentHint: true },
254
+ // handler: async ({ id }, ctx) => {
255
+ // // Your logic here
256
+ // return { content: [{ type: 'text', text: \`Activated ${name.toLowerCase()} \${id}\` }] };
257
+ // },
258
+ // });
259
+
260
+ // Example: read-only analytics tool
261
+ // export const ${camel}StatsTool = defineTool('${fileName}_stats', {
262
+ // description: 'Get ${name.toLowerCase()} statistics',
263
+ // input: { period: z.enum(['7d', '30d', '90d']).optional() },
264
+ // annotations: { readOnlyHint: true },
265
+ // handler: async ({ period }) => {
266
+ // // Your logic here
267
+ // return { content: [{ type: 'text', text: JSON.stringify({ total: 0, period }) }] };
268
+ // },
269
+ // });
228
270
  `;
229
271
  },
230
272
  test: (name, fileName) => {
@@ -278,7 +320,7 @@ describe('${name} Resource', () => {
278
320
  */
279
321
  async function generate(type, args) {
280
322
  if (!type) throw new Error("Missing type argument\nUsage: arc generate <resource|controller|model|repository|schemas> <name>");
281
- const [name] = args;
323
+ const [name, ...restArgs] = args;
282
324
  if (!name) throw new Error("Missing name argument\nUsage: arc generate <type> <name>\nExample: arc generate resource product");
283
325
  const capitalizedName = toPascalCase(name);
284
326
  const lowerName = name.toLowerCase();
@@ -290,7 +332,7 @@ async function generate(type, args) {
290
332
  switch (type) {
291
333
  case "resource":
292
334
  case "r":
293
- await generateResource(capitalizedName, lowerName, resourcePath, templates, ext);
335
+ await generateResource(capitalizedName, lowerName, resourcePath, templates, ext, projectConfig.mcp === true || restArgs.includes("--mcp"));
294
336
  break;
295
337
  case "controller":
296
338
  case "c":
@@ -308,13 +350,16 @@ async function generate(type, args) {
308
350
  case "s":
309
351
  await generateFile(capitalizedName, lowerName, resourcePath, "schemas", templates.schemas, ext);
310
352
  break;
311
- default: throw new Error(`Unknown type: ${type}\nAvailable types: resource, controller, model, repository, schemas`);
353
+ case "mcp":
354
+ await generateFile(capitalizedName, lowerName, resourcePath, "mcp", templates.mcp, ext);
355
+ break;
356
+ default: throw new Error(`Unknown type: ${type}\nAvailable types: resource, controller, model, repository, schemas, mcp`);
312
357
  }
313
358
  }
314
359
  /**
315
360
  * Generate a full resource
316
361
  */
317
- async function generateResource(name, lowerName, resourcePath, templates, ext) {
362
+ async function generateResource(name, lowerName, resourcePath, templates, ext, includeMcp = false) {
318
363
  console.log(`\nGenerating resource: ${name}...\n`);
319
364
  if (!existsSync(resourcePath)) {
320
365
  mkdirSync(resourcePath, { recursive: true });
@@ -325,6 +370,7 @@ async function generateResource(name, lowerName, resourcePath, templates, ext) {
325
370
  [`${lowerName}.repository.${ext}`]: templates.repository(name, lowerName),
326
371
  [`${lowerName}.resource.${ext}`]: templates.resource(name, lowerName)
327
372
  };
373
+ if (includeMcp) files[`${lowerName}.mcp.${ext}`] = templates.mcp(name, lowerName);
328
374
  for (const [filename, content] of Object.entries(files)) {
329
375
  const filepath = join(resourcePath, filename);
330
376
  if (existsSync(filepath)) console.warn(` ! Skipped: ${filename} (already exists)`);
@@ -367,7 +413,11 @@ ${isMultiTenant ? ` - requireAuth() → any authenticated user
367
413
 
368
414
  4. Run tests:
369
415
  npm test
370
- `);
416
+ ${includeMcp ? `
417
+ 5. MCP tools file created: ${lowerName}.mcp.${ext}
418
+ Uncomment and customize the example tools.
419
+ Import and add to extraTools in your mcpPlugin config.
420
+ ` : ""}`);
371
421
  }
372
422
  /**
373
423
  * Generate a single file
@@ -384,6 +434,5 @@ async function generateFile(name, lowerName, resourcePath, fileType, template, e
384
434
  writeFileSync(filepath, template(name, lowerName));
385
435
  console.log(` + Created: ${filename}`);
386
436
  }
387
-
388
437
  //#endregion
389
- export { generate as default, generate };
438
+ export { generate as default, generate };