@classytic/arc 2.21.0 → 2.23.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.
Files changed (115) hide show
  1. package/dist/{BaseController-BnLYgE9A.mjs → BaseController-BQ8KNvWC.mjs} +3 -2
  2. package/dist/{ResourceRegistry-7RnrEWNP.mjs → ResourceRegistry-y9Hnypus.mjs} +1 -0
  3. package/dist/{actionPermissions-ze70xXJi.mjs → actionPermissions-lszoJle1.mjs} +1 -1
  4. package/dist/applyPermissionResult-Tw4iXtlf.mjs +264 -0
  5. package/dist/audit/index.d.mts +2 -2
  6. package/dist/auth/index.d.mts +30 -2
  7. package/dist/auth/index.mjs +6 -3
  8. package/dist/cli/commands/describe.d.mts +1 -1
  9. package/dist/cli/commands/describe.mjs +1 -1
  10. package/dist/cli/commands/docs.mjs +2 -2
  11. package/dist/cli/commands/init.mjs +1 -1
  12. package/dist/cli/commands/introspect.mjs +1 -1
  13. package/dist/cli/index.mjs +1 -1
  14. package/dist/core/index.d.mts +2 -2
  15. package/dist/core/index.mjs +3 -3
  16. package/dist/{core-DZ6Xe1jR.mjs → core-DQ6rDTyH.mjs} +126 -68
  17. package/dist/{createActionRouter-BWMDW-dL.mjs → createActionRouter-BuOv-m4Y.mjs} +10 -6
  18. package/dist/{createAggregationRouter-l5mNdYrZ.mjs → createAggregationRouter-CgJv_BU_.mjs} +3 -3
  19. package/dist/{createApp-Bi61XxqU.mjs → createApp-CjOeWJ9_.mjs} +252 -57
  20. package/dist/docs/index.d.mts +1 -1
  21. package/dist/docs/index.mjs +1 -1
  22. package/dist/encryption/index.mjs +9 -5
  23. package/dist/{errorHandler-Dxf6P_kV.mjs → errorHandler-Bq2OeEQL.mjs} +34 -10
  24. package/dist/{errorHandler-B0e-bq-O.d.mts → errorHandler-C6yv4ZTW.d.mts} +13 -0
  25. package/dist/events/index.mjs +5 -2
  26. package/dist/factory/index.d.mts +46 -9
  27. package/dist/factory/index.mjs +64 -13
  28. package/dist/{fastify-augmentation-DafigGQp.d.mts → fastify-augmentation-g_f9omSc.d.mts} +2 -2
  29. package/dist/fields-D3lhgzhD.d.mts +121 -0
  30. package/dist/health-ScyjIVo5.mjs +202 -0
  31. package/dist/hooks/index.d.mts +1 -1
  32. package/dist/idempotency/index.d.mts +7 -0
  33. package/dist/idempotency/index.mjs +7 -17
  34. package/dist/{index-Dtrj6XZk.d.mts → index-BBsvQt7U.d.mts} +1310 -1226
  35. package/dist/{types-DpxoNX5v.d.mts → index-Ba10fdKp.d.mts} +196 -66
  36. package/dist/{index-B5N-NaHs.d.mts → index-CC5ruDGh.d.mts} +3 -3
  37. package/dist/{index-B5PoNbfu.d.mts → index-wMoWBLkn.d.mts} +68 -2
  38. package/dist/index.d.mts +6 -5
  39. package/dist/index.mjs +6 -5
  40. package/dist/{init-0YhHkXGk.mjs → init-CjO4p_q1.mjs} +123 -4
  41. package/dist/integrations/event-gateway.mjs +1 -1
  42. package/dist/integrations/index.d.mts +1 -1
  43. package/dist/integrations/mcp/index.d.mts +67 -4
  44. package/dist/integrations/mcp/index.mjs +1 -1
  45. package/dist/integrations/mcp/testing.d.mts +1 -1
  46. package/dist/integrations/mcp/testing.mjs +1 -1
  47. package/dist/integrations/webhooks.d.mts +131 -7
  48. package/dist/integrations/webhooks.mjs +169 -10
  49. package/dist/interface-n5hpKg1d.mjs +16 -0
  50. package/dist/jsonBody-BlzAoGCe.mjs +43 -0
  51. package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
  52. package/dist/logger/index.d.mts +26 -1
  53. package/dist/logger/index.mjs +39 -1
  54. package/dist/{mcpPlugin-B9XjjNVG.mjs → mcpPlugin-BtzIL_kM.mjs} +312 -39
  55. package/dist/middleware/index.d.mts +1 -1
  56. package/dist/{module-WeuU5jfd.d.mts → module-B4u10iOJ.d.mts} +26 -1
  57. package/dist/{openapi-Kyod_di6.mjs → openapi-jSDx-VDv.mjs} +3 -3
  58. package/dist/permissions/index.d.mts +4 -3
  59. package/dist/permissions/index.mjs +3 -2
  60. package/dist/{permissions-BJMuKMl7.mjs → permissions-tbxFHxjZ.mjs} +77 -264
  61. package/dist/pipeline/index.d.mts +1 -1
  62. package/dist/plugins/index.d.mts +24 -7
  63. package/dist/plugins/index.mjs +14 -282
  64. package/dist/plugins/response-cache.mjs +1 -0
  65. package/dist/plugins/tracing-entry.mjs +7 -4
  66. package/dist/presets/filesUpload.d.mts +2 -2
  67. package/dist/presets/filesUpload.mjs +1 -1
  68. package/dist/presets/index.d.mts +1 -1
  69. package/dist/presets/index.mjs +1 -1
  70. package/dist/presets/multiTenant.d.mts +1 -1
  71. package/dist/presets/search.d.mts +2 -2
  72. package/dist/presets/search.mjs +1 -1
  73. package/dist/{presets-gw0ciIUD.mjs → presets-BOe4Ugkl.mjs} +1 -1
  74. package/dist/rateLimitKey-BoWRH3e7.mjs +14 -0
  75. package/dist/realtime-I8OrfnMR.mjs +375 -0
  76. package/dist/{registerRoute-oGAl0oA8.mjs → registerRoute-CWO6mdYe.mjs} +38 -18
  77. package/dist/registry/index.d.mts +2 -2
  78. package/dist/registry/index.mjs +1 -1
  79. package/dist/requestId-5BhTVzBv.mjs +113 -0
  80. package/dist/schemaConverter-DJvodiHj.mjs +171 -0
  81. package/dist/{schemaIR-ajetw3Fv.mjs → schemaIR-Ca7ySNPx.mjs} +2 -2
  82. package/dist/scim/index.mjs +6 -7
  83. package/dist/scope/index.mjs +2 -76
  84. package/dist/scope-LONQ3mfZ.mjs +37 -0
  85. package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
  86. package/dist/{sse-CfUjwjDk.mjs → sse-0u4P4-OI.mjs} +7 -30
  87. package/dist/sseStream-BQ7Qaynp.mjs +57 -0
  88. package/dist/testing/index.d.mts +83 -3
  89. package/dist/testing/index.mjs +70 -3
  90. package/dist/types/index.d.mts +2 -2
  91. package/dist/{types-DRj-Ut4n.d.mts → types-5OuqeTDX.d.mts} +37 -2
  92. package/dist/{fields-BGqrAa_u.d.mts → types-Csxga7d3.d.mts} +1 -121
  93. package/dist/usage/index.d.mts +142 -0
  94. package/dist/usage/index.mjs +0 -0
  95. package/dist/utils/index.d.mts +101 -19
  96. package/dist/utils/index.mjs +5 -3
  97. package/dist/{utils-CO8a3B_1.mjs → utils-CZyeRXTh.mjs} +80 -82
  98. package/dist/{versioning-DQ4mCvg0.d.mts → versioning-D6PvCwpq.d.mts} +43 -9
  99. package/llms.txt +39 -0
  100. package/package.json +513 -447
  101. package/skills/arc/SKILL.md +14 -8
  102. package/skills/arc/references/api-reference.md +34 -0
  103. package/skills/arc/references/auth.md +21 -4
  104. package/skills/arc/references/mcp.md +1 -1
  105. package/skills/arc/references/production.md +734 -654
  106. package/skills/arc-code-review/SKILL.md +1 -1
  107. package/skills/arc-code-review/references/anti-patterns.md +71 -18
  108. package/skills/arc-code-review/references/arc-cheatsheet.md +15 -2
  109. package/skills/arc-code-review/references/migration-recipes.md +7 -7
  110. package/skills/arc-code-review/references/mongokit-migration.md +4 -3
  111. package/skills/arc-module-publishing/SKILL.md +2 -1
  112. package/skills/arc-module-publishing/references/authoring-modules.md +55 -0
  113. package/skills/arc-module-publishing/references/package-manifest.md +4 -2
  114. package/dist/preloadResources-CHw3_oA_.mjs +0 -45
  115. package/dist/schemaConverter-U0MnO9vd.mjs +0 -110
@@ -1,75 +1,8 @@
1
1
  import { m as RESERVED_QUERY_PARAMS, t as CRUD_OPERATIONS } from "./constants-DNre0wRg.mjs";
2
2
  import { arcLog } from "./logger/index.mjs";
3
3
  import { t as ArcError } from "./errors-DkO8Nk6-.mjs";
4
- import { r as getAvailablePresets } from "./presets-gw0ciIUD.mjs";
4
+ import { r as getAvailablePresets } from "./presets-BOe4Ugkl.mjs";
5
5
  import { errorContractSchema as errorContractSchema$1, errorDetailSchema as errorDetailSchema$1 } from "@classytic/repo-core/errors";
6
- //#region src/utils/simpleEqualityMatcher.ts
7
- /**
8
- * `simpleEqualityMatcher` — a minimal, dialect-agnostic flat-key equality
9
- * matcher for `DataAdapter.matchesFilter` / `BaseController({ matchesFilter })`.
10
- *
11
- * **What it does:** for each `[key, expected]` in the filter, compares
12
- * `item[key]` to `expected` via string coercion (so Mongo `ObjectId` values
13
- * match their string representation) and returns `true` only if every
14
- * filter entry matches. Array item values are matched implicitly (contains).
15
- *
16
- * **What it does NOT do:**
17
- * - No `$eq` / `$ne` / `$in` / `$nin` / `$gt` / `$lt` / `$regex` / `$exists`
18
- * - No `$and` / `$or`
19
- * - No dot-path traversal (`"owner.id"`)
20
- * - No schema-specific coercion
21
- *
22
- * **Why it exists:** 95%+ of arc's `_policyFilters` are produced by built-in
23
- * permission helpers and are shaped like `{ ownerId: "u1" }` or
24
- * `{ organizationId: "org_x" }` — flat equality. For that common shape,
25
- * this helper is a safe, tested, 15-line defense-in-depth matcher that
26
- * hosts using minimal repos (no `getOne(compoundFilter)` DB path) can opt
27
- * into without arc shipping a full Mongo-syntax engine.
28
- *
29
- * **When to use:**
30
- * - Your adapter/repo doesn't natively filter on `getOne(compoundFilter)`
31
- * - Your `_policyFilters` are flat equality (from arc's built-in permission helpers)
32
- * - You want defense-in-depth on `validateItemAccess` / `fetchDetailed`'s `getById` fallback
33
- *
34
- * **When NOT to use:**
35
- * - Your `_policyFilters` use operators (`$in`, `$ne`, etc.) — supply a
36
- * native matcher (mongokit's repo does the filter at the DB layer; for
37
- * custom repos, wrap the kit's own predicate engine).
38
- * - You're a mongokit / sqlitekit / Prisma user — the DB-level filter
39
- * applied by `getOne(compoundFilter)` already covers this.
40
- *
41
- * @example
42
- * ```ts
43
- * import { simpleEqualityMatcher } from '@classytic/arc/utils';
44
- *
45
- * // On a custom adapter
46
- * const adapter: DataAdapter = {
47
- * repository,
48
- * type: 'custom',
49
- * name: 'in-memory',
50
- * matchesFilter: simpleEqualityMatcher,
51
- * };
52
- *
53
- * // Or directly on BaseController for ad-hoc controllers
54
- * new BaseController(repo, { matchesFilter: simpleEqualityMatcher });
55
- * ```
56
- */
57
- function simpleEqualityMatcher(item, filters) {
58
- if (!item || typeof item !== "object") return false;
59
- const obj = item;
60
- for (const [key, expected] of Object.entries(filters)) {
61
- if (expected && typeof expected === "object" && !Array.isArray(expected) && Object.getPrototypeOf(expected) === Object.prototype && Object.keys(expected).some((k) => k.startsWith("$"))) return false;
62
- const actual = obj[key];
63
- if (Array.isArray(actual)) {
64
- const expectedStr = String(expected);
65
- if (!actual.some((v) => String(v) === expectedStr)) return false;
66
- continue;
67
- }
68
- if (String(actual) !== String(expected)) return false;
69
- }
70
- return true;
71
- }
72
- //#endregion
73
6
  //#region src/core/validateResourceConfig.ts
74
7
  /**
75
8
  * Resource Configuration Validator
@@ -837,6 +770,25 @@ function handleRaw(handler, statusCode = 200) {
837
770
  };
838
771
  }
839
772
  //#endregion
773
+ //#region src/utils/headers.ts
774
+ /**
775
+ * Read a single header value. Lowercases the name (Node normalizes incoming
776
+ * header names to lowercase) and returns the FIRST value when the header
777
+ * was repeated.
778
+ *
779
+ * Duplicate policy: first-value is deterministic, but for security-sensitive
780
+ * headers (authorization material, forwarded-for chains, content-length) a
781
+ * duplicate usually signals smuggling or a broken proxy — consumers that
782
+ * care should check `Array.isArray(headers[name])` and REJECT instead of
783
+ * picking a winner.
784
+ */
785
+ function getHeader(headers, name) {
786
+ if (!headers) return void 0;
787
+ const value = headers[name.toLowerCase()];
788
+ if (Array.isArray(value)) return value[0];
789
+ return value;
790
+ }
791
+ //#endregion
840
792
  //#region src/utils/queryParser.ts
841
793
  /**
842
794
  * Arc Query Parser - Default URL-to-Query Parser
@@ -1295,26 +1247,72 @@ const paginationSchema = {
1295
1247
  ]
1296
1248
  };
1297
1249
  /**
1298
- * List response schema — full union of every wire shape `toCanonicalList`
1299
- * can emit. Hosts who know their endpoint only ever produces one variant
1300
- * can pin to a narrower helper:
1250
+ * List response schema — accepts every wire shape `toCanonicalList` can
1251
+ * emit. Hosts who know their endpoint only ever produces one variant can
1252
+ * pin to a narrower helper (sharper OpenAPI docs, same serializer speed):
1301
1253
  * - `offsetListResponse(item)` — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`
1302
1254
  * - `keysetListResponse(item)` — `{ method: 'keyset', data, limit, hasMore, next: string|null }`
1303
1255
  * - `aggregateListResponse(item)` — `{ method: 'aggregate', ...offset fields }`
1304
1256
  * - `bareListResponse(item)` — `{ data }`
1305
1257
  *
1306
- * The default `listResponse(item)` is the union (`oneOf`) of all four so
1307
- * Fastify validation accepts any canonical kit shape — pre-2.13 this only
1308
- * modelled offset and rejected keyset/aggregate/bare lists at the
1309
- * response-schema gate.
1258
+ * SHAPE: one merged permissive object, NOT a `oneOf` union (2.22). The
1259
+ * pre-2.22 union was semantically identical on the wire — every variant is
1260
+ * `additionalProperties: true` and the property definitions never conflict
1261
+ * (`next` only exists on keyset, the offset counters only on offset/
1262
+ * aggregate; fast-json-stringify skips undefined optionals) — but
1263
+ * fast-json-stringify implements top-level `oneOf` by running full AJV
1264
+ * validation PER RESPONSE to pick the branch, on the hottest route of
1265
+ * every arc app. The merge keeps the exact bytes (pinned by
1266
+ * `tests/utils/list-response-serialization.test.ts`) and drops the
1267
+ * per-response branch validation. Only `data` is required — the one field
1268
+ * all four variants share.
1310
1269
  */
1311
1270
  function listResponse(itemSchema) {
1312
- return { oneOf: [
1313
- offsetListResponse(itemSchema),
1314
- keysetListResponse(itemSchema),
1315
- aggregateListResponse(itemSchema),
1316
- bareListResponse(itemSchema)
1317
- ] };
1271
+ return {
1272
+ type: "object",
1273
+ properties: {
1274
+ method: {
1275
+ type: "string",
1276
+ description: "Pagination method discriminant — 'offset' | 'keyset' | 'aggregate'; absent on bare lists.",
1277
+ example: "offset"
1278
+ },
1279
+ data: {
1280
+ type: "array",
1281
+ items: itemSchema
1282
+ },
1283
+ page: {
1284
+ type: "integer",
1285
+ example: 1
1286
+ },
1287
+ limit: {
1288
+ type: "integer",
1289
+ example: 20
1290
+ },
1291
+ total: {
1292
+ type: "integer",
1293
+ example: 100
1294
+ },
1295
+ pages: {
1296
+ type: "integer",
1297
+ example: 5
1298
+ },
1299
+ hasNext: {
1300
+ type: "boolean",
1301
+ example: false
1302
+ },
1303
+ hasPrev: {
1304
+ type: "boolean",
1305
+ example: false
1306
+ },
1307
+ hasMore: { type: "boolean" },
1308
+ next: {
1309
+ type: ["string", "null"],
1310
+ description: "Cursor token for the next page, or null."
1311
+ }
1312
+ },
1313
+ required: ["data"],
1314
+ additionalProperties: true
1315
+ };
1318
1316
  }
1319
1317
  /** Offset variant — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`. */
1320
1318
  function offsetListResponse(itemSchema) {
@@ -1732,4 +1730,4 @@ function getUserId(user) {
1732
1730
  return id ? String(id) : void 0;
1733
1731
  }
1734
1732
  //#endregion
1735
- export { createCircuitBreaker as A, defineErrorMapper as C, CircuitBreakerError as D, CircuitBreaker as E, simpleEqualityMatcher as F, assertValidConfig as M, formatValidationErrors as N, CircuitBreakerRegistry as O, validateResourceConfig as P, defineGuard as S, withCompensation as T, ArcQueryParser as _, bareListResponse as a, MONGO_DIALECT as b, errorDetailSchema$1 as c, keysetListResponse as d, listResponse as f, responses as g, queryParams as h, aggregateListResponse as i, createCircuitBreakerRegistry as j, CircuitState as k, getDefaultCrudSchemas as l, paginationSchema as m, createStateMachine as n, deleteResponse as o, offsetListResponse as p, scheduleBackground as r, errorContractSchema$1 as s, getUserId as t, getListQueryParams as u, createQueryParser as v, defineCompensation as w, NEUTRAL_DIALECT as x, handleRaw as y };
1733
+ export { CircuitState as A, defineGuard as C, CircuitBreaker as D, withCompensation as E, validateResourceConfig as F, createCircuitBreakerRegistry as M, assertValidConfig as N, CircuitBreakerError as O, formatValidationErrors as P, NEUTRAL_DIALECT as S, defineCompensation as T, ArcQueryParser as _, bareListResponse as a, handleRaw as b, errorDetailSchema$1 as c, keysetListResponse as d, listResponse as f, responses as g, queryParams as h, aggregateListResponse as i, createCircuitBreaker as j, CircuitBreakerRegistry as k, getDefaultCrudSchemas as l, paginationSchema as m, createStateMachine as n, deleteResponse as o, offsetListResponse as p, scheduleBackground as r, errorContractSchema$1 as s, getUserId as t, getListQueryParams as u, createQueryParser as v, defineErrorMapper as w, MONGO_DIALECT as x, getHeader as y };
@@ -1,11 +1,6 @@
1
1
  import { n as DomainEvent } from "./EventTransport-QmY7A258.mjs";
2
2
  import { FastifyPluginAsync, FastifyRequest } from "fastify";
3
3
  //#region src/plugins/health.d.ts
4
- declare module "fastify" {
5
- interface FastifyRequest {
6
- _startTime?: number;
7
- }
8
- }
9
4
  interface HealthCheck {
10
5
  /** Name of the dependency */
11
6
  name: string;
@@ -31,7 +26,7 @@ interface HealthOptions {
31
26
  collectHttpMetrics?: boolean;
32
27
  }
33
28
  declare const healthPlugin: FastifyPluginAsync<HealthOptions>;
34
- declare const _default$4: FastifyPluginAsync<HealthOptions>;
29
+ declare const _default$5: FastifyPluginAsync<HealthOptions>;
35
30
  //#endregion
36
31
  //#region src/plugins/caching.d.ts
37
32
  interface CachingRule {
@@ -59,7 +54,7 @@ interface CachingOptions {
59
54
  rules?: CachingRule[];
60
55
  }
61
56
  declare const cachingPlugin: FastifyPluginAsync<CachingOptions>;
62
- declare const _default$3: FastifyPluginAsync<CachingOptions>;
57
+ declare const _default$4: FastifyPluginAsync<CachingOptions>;
63
58
  //#endregion
64
59
  //#region src/plugins/sse.d.ts
65
60
  interface SSEOptions {
@@ -84,7 +79,46 @@ interface SSEOptions {
84
79
  tokenQueryParam?: string | null;
85
80
  }
86
81
  declare const ssePlugin: FastifyPluginAsync<SSEOptions>;
87
- declare const _default$2: FastifyPluginAsync<SSEOptions>;
82
+ declare const _default$3: FastifyPluginAsync<SSEOptions>;
83
+ //#endregion
84
+ //#region src/plugins/realtime.d.ts
85
+ declare const CHANGE_OPERATIONS: readonly ["created", "updated", "deleted"];
86
+ type ChangeOperation = (typeof CHANGE_OPERATIONS)[number];
87
+ interface RealtimeOptions {
88
+ /**
89
+ * Route path — must contain `:resource` (default: `/realtime/:resource`).
90
+ */
91
+ path?: string;
92
+ /** Heartbeat interval in ms (default: 30000). */
93
+ heartbeat?: number;
94
+ /**
95
+ * Subscribable resources. Default: every registered resource — safe
96
+ * because each subscription runs that resource's own `list` permission;
97
+ * narrow it when only some resources should have a change feed at all.
98
+ */
99
+ resources?: readonly string[];
100
+ /** Operations streamed (default: created + updated + deleted). */
101
+ operations?: readonly ChangeOperation[];
102
+ /**
103
+ * Query parameter carrying the bearer token for browser `EventSource`
104
+ * clients (default `'token'`, matching the sse plugin / arc-next).
105
+ * Set `null` to disable promotion.
106
+ */
107
+ tokenQueryParam?: string | null;
108
+ /**
109
+ * Hard ceiling (ms) on any single connection's lifetime. Bounds
110
+ * permission STALENESS: the row filters are snapshotted at connect, so a
111
+ * caller whose permissions are revoked mid-connection keeps receiving
112
+ * their old view until they reconnect. Independently, arc ALWAYS closes
113
+ * the feed when the auth token's `exp` passes (forcing reconnect +
114
+ * re-authorization with fresh filters) — this option adds a cap for
115
+ * session/cookie auth that carries no `exp`, or to tighten below the
116
+ * token TTL. Omit for token-exp-only bounding.
117
+ */
118
+ maxConnectionMs?: number;
119
+ }
120
+ declare const realtimePlugin: FastifyPluginAsync<RealtimeOptions>;
121
+ declare const _default$2: FastifyPluginAsync<RealtimeOptions>;
88
122
  //#endregion
89
123
  //#region src/plugins/metrics.d.ts
90
124
  interface MetricsOptions {
@@ -153,4 +187,4 @@ declare module "fastify" {
153
187
  declare const versioningPlugin: FastifyPluginAsync<VersioningOptions>;
154
188
  declare const _default: FastifyPluginAsync<VersioningOptions>;
155
189
  //#endregion
156
- export { HealthOptions as _, MetricsCollector as a, metricsPlugin as c, ssePlugin as d, CachingOptions as f, HealthCheck as g, cachingPlugin as h, MetricEntry as i, SSEOptions as l, _default$3 as m, _default as n, MetricsOptions as o, CachingRule as p, versioningPlugin as r, _default$1 as s, VersioningOptions as t, _default$2 as u, _default$4 as v, healthPlugin as y };
190
+ export { healthPlugin as C, _default$5 as S, CachingRule as _, MetricsCollector as a, HealthCheck as b, metricsPlugin as c, _default$2 as d, realtimePlugin as f, CachingOptions as g, ssePlugin as h, MetricEntry as i, ChangeOperation as l, _default$3 as m, _default as n, MetricsOptions as o, SSEOptions as p, versioningPlugin as r, _default$1 as s, VersioningOptions as t, RealtimeOptions as u, _default$4 as v, HealthOptions as x, cachingPlugin as y };
package/llms.txt ADDED
@@ -0,0 +1,39 @@
1
+ # @classytic/arc
2
+
3
+ > Resource-oriented backend framework for Fastify. One `defineResource()` call =
4
+ > REST + validation + auth + permissions + events + caching + OpenAPI + MCP tools.
5
+ > DB-agnostic via @classytic/repo-core adapter kits (mongokit, sqlitekit, prismakit).
6
+
7
+ IMPORTANT for coding agents: hand-rolling is a bug in arc apps. Before writing any
8
+ route, validation, pagination, auth check, cron, queue worker, counter, or audit
9
+ code, check whether arc provides it — it almost certainly does:
10
+
11
+ - CRUD routes → defineResource({ name, adapter, permissions })
12
+ - Role/ownership checks → @classytic/arc/permissions (requireRoles, requireOwnership, permissionMatrix, requireQuota)
13
+ - Identity/tenant access → @classytic/arc/scope (getUserId, requireOrgId — never read scope fields directly)
14
+ - Body/response schemas → schemaOptions.fieldRules (customSchemas when wire ≠ model)
15
+ - Query filter/sort/page → the resource queryParser (allow-listed) + keyset cursors
16
+ - Soft delete/slug/tree/bulk/files/search → presets: [...]
17
+ - Recurring jobs → schedulesPlugin (@classytic/arc/plugins; LockAdapter leader safety)
18
+ - Durable queues → @classytic/arc/integrations/jobs (BullMQ/Redis) or @classytic/streamline (Mongo)
19
+ - Headless worker process→ createWorker(sameOptions) (@classytic/arc/factory)
20
+ - Usage counters/quotas → @classytic/arc/usage (usagePlugin, requireQuota) + rateLimit.plan
21
+ - Audit + record history → audit: true, history: true on the resource
22
+ - Idempotency → @classytic/arc/idempotency
23
+ - Events/outbox/webhooks → @classytic/arc/events, integrations/webhooks
24
+ - AI/MCP tools → generated from every resource (incl. _count/_exists/_distinct list verbs)
25
+ - App boot → createApp({ beforeBoot: connectDatabase, ... }) — DB connect goes IN beforeBoot
26
+
27
+ ## Docs (inside this package)
28
+
29
+ - skills/arc/SKILL.md — full framework guide (boot, defineResource, presets, auth, testing)
30
+ - skills/arc/references/api-reference.md — every subpath import
31
+ - skills/arc-code-review/references/arc-cheatsheet.md — hand-rolled-code → arc-feature map
32
+ - skills/arc-code-review/SKILL.md — audit workflow for existing codebases
33
+
34
+ ## Conventions that bite
35
+
36
+ - request.user is undefined on public routes — guard it.
37
+ - Custom endpoints go in the resource's routes:/actions: (inherit auth/OpenAPI/MCP);
38
+ bare fastify.get() bypasses everything.
39
+ - mongokit >=3.21 defaults schemaGenerator; schemaGenerator: false kills OpenAPI+MCP bodies.