@classytic/arc 2.28.0 → 2.30.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 (83) hide show
  1. package/dist/{BaseController-B65zbz9m.mjs → BaseController-B5DwufbB.mjs} +12 -10
  2. package/dist/EventTransport-C6YA8xY9.mjs +103 -0
  3. package/dist/{HookSystem-C1vB_Gc9.mjs → HookSystem-WkIougo6.mjs} +15 -35
  4. package/dist/applyPermissionResult-Cf6zUCvb.mjs +257 -0
  5. package/dist/audit/index.d.mts +2 -2
  6. package/dist/audit/index.mjs +1 -1
  7. package/dist/auth/index.d.mts +2 -2
  8. package/dist/auth/index.mjs +1 -1
  9. package/dist/{buildHandler-Dtwd8fdz.mjs → buildHandler-BYS3Vkfz.mjs} +32 -6
  10. package/dist/cleanup/index.d.mts +2 -2
  11. package/dist/cleanup/index.mjs +1 -1
  12. package/dist/cli/commands/describe.d.mts +1 -1
  13. package/dist/cli/commands/docs.mjs +1 -1
  14. package/dist/core/index.d.mts +2 -2
  15. package/dist/core/index.mjs +5 -4
  16. package/dist/{core-BGa8TXJR.mjs → core-poalJLvp.mjs} +2 -48
  17. package/dist/{createActionRouter-C-N7z2EL.mjs → createActionRouter-BxuywVRB.mjs} +2 -2
  18. package/dist/{createAggregationRouter-wzwFa_dW.mjs → createAggregationRouter-BiY-lGdY.mjs} +7 -20
  19. package/dist/{createApp-Du5G54TM.mjs → createApp-CXrKFciH.mjs} +24 -3
  20. package/dist/defineAggregation-ufWjUwml.mjs +47 -0
  21. package/dist/{defineResource-2zvVVpxg.mjs → defineResource-CxWqDCAy.mjs} +35 -42
  22. package/dist/docs/index.d.mts +1 -1
  23. package/dist/docs/index.mjs +1 -1
  24. package/dist/{eventPlugin-DrQ_FesK.mjs → eventPlugin-D2NNR4py.mjs} +2 -103
  25. package/dist/events/index.mjs +4 -339
  26. package/dist/factory/index.d.mts +4 -4
  27. package/dist/factory/index.mjs +1 -1
  28. package/dist/{fastify-augmentation-OzYGIiU1.d.mts → fastify-augmentation-0WhPnUH7.d.mts} +3 -3
  29. package/dist/{applyPermissionResult-BnyVE9eD.mjs → fields-CQ_7KQWw.mjs} +1 -107
  30. package/dist/hooks/index.d.mts +1 -1
  31. package/dist/hooks/index.mjs +1 -1
  32. package/dist/idempotency/index.mjs +1 -1
  33. package/dist/{index-CZY4EGga.d.mts → index-CCzVR-N3.d.mts} +1 -1
  34. package/dist/{index-BMnbzvtO.d.mts → index-Ch16aAah.d.mts} +201 -21
  35. package/dist/{index-CjVg-Epg.d.mts → index-Cx7bro4R.d.mts} +4 -4
  36. package/dist/{index-DPRH0Cme.d.mts → index-Q5gzEIWY.d.mts} +97 -66
  37. package/dist/index.d.mts +6 -6
  38. package/dist/index.mjs +8 -7
  39. package/dist/integrations/index.d.mts +1 -1
  40. package/dist/integrations/mcp/index.d.mts +6 -6
  41. package/dist/integrations/mcp/index.mjs +1 -1
  42. package/dist/integrations/mcp/testing.d.mts +1 -1
  43. package/dist/integrations/mcp/testing.mjs +1 -1
  44. package/dist/{mcpPlugin-Cs6nRGCt.mjs → mcpPlugin-nRzm5g2s.mjs} +36 -43
  45. package/dist/middleware/index.d.mts +1 -1
  46. package/dist/migrations/index.d.mts +0 -3
  47. package/dist/migrations/index.mjs +0 -3
  48. package/dist/{openapi-BLV4OMXb.mjs → openapi-B8Vkbxbn.mjs} +1 -1
  49. package/dist/permissions/index.d.mts +3 -3
  50. package/dist/permissions/index.mjs +4 -3
  51. package/dist/{permissions-BrtiBKGs.mjs → permissions-Bfu_8y1U.mjs} +318 -289
  52. package/dist/pipeline/index.d.mts +1 -1
  53. package/dist/{pipeline-BGRnV-To.mjs → pipeline-BJnpkY9i.mjs} +4 -18
  54. package/dist/plugins/index.d.mts +4 -4
  55. package/dist/plugins/index.mjs +2 -2
  56. package/dist/plugins/tracing-entry.mjs +1 -1
  57. package/dist/{preloadResources-CM4uUaFv.d.mts → preloadResources-DCfa2egz.d.mts} +3 -3
  58. package/dist/presets/filesUpload.d.mts +2 -2
  59. package/dist/presets/filesUpload.mjs +1 -1
  60. package/dist/presets/index.d.mts +14 -2
  61. package/dist/presets/index.mjs +1 -1
  62. package/dist/presets/multiTenant.d.mts +1 -1
  63. package/dist/presets/search.d.mts +2 -2
  64. package/dist/presets/search.mjs +1 -1
  65. package/dist/{presets-DwFDVKP0.mjs → presets-CJOEXLuk.mjs} +25 -9
  66. package/dist/projection-UBDgfMwu.mjs +17 -0
  67. package/dist/{realtime-DEuDu9io.mjs → realtime-B9nmIY5V.mjs} +2 -1
  68. package/dist/{registerRoute-DTp18Ah_.mjs → registerRoute-JkxIEBJq.mjs} +3 -1
  69. package/dist/registry/index.d.mts +2 -2
  70. package/dist/registry/index.mjs +1 -1
  71. package/dist/{registry-9km_w1aP.mjs → registry-BYXUUg21.mjs} +1 -1
  72. package/dist/repositoryFilter-Dz8k3S4P.mjs +44 -0
  73. package/dist/subscribe-helpers-DfzsivM6.mjs +339 -0
  74. package/dist/testing/index.d.mts +34 -6
  75. package/dist/testing/index.mjs +314 -211
  76. package/dist/types/index.d.mts +3 -3
  77. package/dist/{types-QFGc3Cjv.d.mts → types-Bmmn-fO0.d.mts} +1 -1
  78. package/dist/{types-Dmw-6Qeo.d.mts → types-ChvvVNP8.d.mts} +93 -88
  79. package/dist/{types-Cu6vVJmh.d.mts → types-D_n8L6VY.d.mts} +32 -1
  80. package/dist/usage/index.d.mts +1 -1
  81. package/dist/usage/index.mjs +0 -0
  82. package/dist/utils/index.d.mts +2 -5
  83. package/package.json +539 -539
@@ -1,192 +1,10 @@
1
- import { a as MemoryEventTransport, i as withRetry, o as createEvent, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-DrQ_FesK.mjs";
1
+ import { t as MemoryEventTransport } from "../EventTransport-C6YA8xY9.mjs";
2
+ import { a as createEventRegistry, i as wrapWithSchema, n as subscribeWithSchema, o as defineEvent, r as wrapWithBoundary, t as subscribeWithBoundary } from "../subscribe-helpers-DfzsivM6.mjs";
3
+ import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-D2NNR4py.mjs";
2
4
  import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DY6xq4sM.mjs";
3
- import { InvalidOutboxEventError, OutboxOwnershipError } from "@classytic/primitives/outbox";
4
5
  import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
6
+ import { InvalidOutboxEventError, OutboxOwnershipError } from "@classytic/primitives/outbox";
5
7
  import { update } from "@classytic/repo-core/update";
6
- //#region src/events/defineEvent.ts
7
- /**
8
- * defineEvent — Typed Event Definitions with Optional Schema Validation
9
- *
10
- * Provides:
11
- * 1. defineEvent() — declare an event with name, schema, version, description
12
- * 2. EventRegistry — catalog of all known events + payload validation
13
- * 3. .create() helper — build DomainEvent with auto-generated metadata
14
- *
15
- * The built-in validator checks: object type, required fields, and top-level
16
- * property types. It does NOT recurse into nested objects, validate arrays,
17
- * enums, patterns, formats, or $ref. This is intentional — it's a lightweight
18
- * guard, not a full JSON Schema engine.
19
- *
20
- * For full validation, pass a custom `validate` function to `createEventRegistry()`:
21
- *
22
- * @example
23
- * ```typescript
24
- * import Ajv from 'ajv';
25
- * const ajv = new Ajv();
26
- *
27
- * const registry = createEventRegistry({
28
- * validate: (schema, payload) => {
29
- * const valid = ajv.validate(schema, payload);
30
- * return valid
31
- * ? { valid: true }
32
- * : { valid: false, errors: ajv.errorsText().split(', ') };
33
- * },
34
- * });
35
- * ```
36
- *
37
- * @example
38
- * ```typescript
39
- * import { defineEvent, createEventRegistry } from '@classytic/arc/events';
40
- *
41
- * const OrderCreated = defineEvent({
42
- * name: 'order.created',
43
- * version: 1,
44
- * schema: {
45
- * type: 'object',
46
- * properties: {
47
- * orderId: { type: 'string' },
48
- * total: { type: 'number' },
49
- * },
50
- * required: ['orderId', 'total'],
51
- * },
52
- * });
53
- *
54
- * // Type-safe event creation
55
- * const event = OrderCreated.create({ orderId: 'o-1', total: 100 });
56
- * await fastify.events.publish(event.type, event.payload, event.meta);
57
- *
58
- * // Registry for introspection + validation
59
- * const registry = createEventRegistry();
60
- * registry.register(OrderCreated);
61
- * const result = registry.validate('order.created', payload);
62
- * ```
63
- */
64
- /**
65
- * Define a typed event with optional schema validation.
66
- *
67
- * @example
68
- * const OrderCreated = defineEvent({
69
- * name: 'order.created',
70
- * schema: { type: 'object', properties: { orderId: { type: 'string' } }, required: ['orderId'] },
71
- * });
72
- *
73
- * const event = OrderCreated.create({ orderId: '123' });
74
- */
75
- function defineEvent(input) {
76
- const { name, schema, version = 1, description } = input;
77
- return {
78
- name,
79
- schema,
80
- version,
81
- description,
82
- create(payload, meta) {
83
- return createEvent(name, payload, {
84
- schemaVersion: version,
85
- ...meta
86
- });
87
- }
88
- };
89
- }
90
- /**
91
- * Create an event registry for cataloging and validating events.
92
- *
93
- * The registry is opt-in — unregistered events pass validation.
94
- * This allows gradual adoption without breaking existing code.
95
- *
96
- * @param options.validate - Custom validator replacing the built-in minimal validator.
97
- * The built-in validator only checks top-level object structure (type, required, property types).
98
- * For nested objects, arrays, enums, patterns, or $ref, provide AJV or similar.
99
- */
100
- function createEventRegistry(options) {
101
- const customValidator = options?.validate;
102
- const definitions = /* @__PURE__ */ new Map();
103
- function registryKey(name, version) {
104
- return `${name}:v${version}`;
105
- }
106
- return {
107
- register(definition) {
108
- const key = registryKey(definition.name, definition.version);
109
- if (definitions.has(key)) throw new Error(`Event '${definition.name}' v${definition.version} is already registered. Use a different version number for schema evolution.`);
110
- definitions.set(key, definition);
111
- },
112
- get(name, version) {
113
- if (version !== void 0) return definitions.get(registryKey(name, version));
114
- let latest;
115
- let latestVersion = -1;
116
- for (const def of definitions.values()) if (def.name === name && def.version > latestVersion) {
117
- latest = def;
118
- latestVersion = def.version;
119
- }
120
- return latest;
121
- },
122
- catalog() {
123
- return Array.from(definitions.values()).map((def) => ({
124
- name: def.name,
125
- version: def.version,
126
- description: def.description,
127
- schema: def.schema
128
- }));
129
- },
130
- validate(name, payload, version) {
131
- let target = version !== void 0 ? definitions.get(registryKey(name, version)) : void 0;
132
- if (!target && version === void 0) {
133
- let latestVersion = -1;
134
- for (const candidate of definitions.values()) if (candidate.name === name && candidate.version > latestVersion) {
135
- target = candidate;
136
- latestVersion = candidate.version;
137
- }
138
- }
139
- if (!target) return { valid: true };
140
- if (!target.schema) return { valid: true };
141
- return customValidator ? customValidator(target.schema, payload) : validatePayload(payload, target.schema);
142
- }
143
- };
144
- }
145
- /**
146
- * Built-in minimal validator — lightweight guard, NOT a full JSON Schema engine.
147
- *
148
- * Checks:
149
- * - payload is an object (not null, not array)
150
- * - required fields are present
151
- * - top-level property types match (string, number, boolean, array, object)
152
- *
153
- * Does NOT check:
154
- * - nested object properties
155
- * - array item types
156
- * - enum, pattern, format, minLength, minimum, $ref
157
- *
158
- * For full validation, pass a custom `validate` function to `createEventRegistry()`.
159
- */
160
- function validatePayload(payload, schema) {
161
- const errors = [];
162
- if (schema.type === "object") {
163
- if (payload === null || payload === void 0 || typeof payload !== "object" || Array.isArray(payload)) return {
164
- valid: false,
165
- errors: ["Payload must be an object"]
166
- };
167
- const record = payload;
168
- if (schema.required) {
169
- for (const field of schema.required) if (!(field in record) || record[field] === void 0) errors.push(`Missing required field: '${field}'`);
170
- }
171
- if (schema.properties) {
172
- for (const [key, propSchema] of Object.entries(schema.properties)) if (key in record && record[key] !== void 0 && record[key] !== null) {
173
- const expectedType = propSchema.type;
174
- if (expectedType) {
175
- const value = record[key];
176
- const actualType = Array.isArray(value) ? "array" : typeof value;
177
- if (expectedType === "integer") {
178
- if (actualType !== "number" || !Number.isInteger(value)) errors.push(`Field '${key}': expected integer, got ${actualType}`);
179
- } else if (expectedType !== actualType) errors.push(`Field '${key}': expected ${expectedType}, got ${actualType}`);
180
- }
181
- }
182
- }
183
- }
184
- return errors.length === 0 ? { valid: true } : {
185
- valid: false,
186
- errors
187
- };
188
- }
189
- //#endregion
190
8
  //#region src/events/eventTypes.ts
191
9
  /**
192
10
  * Event Type Constants and Helpers
@@ -976,157 +794,4 @@ function normalizeError(err) {
976
794
  return { message: String(err) };
977
795
  }
978
796
  //#endregion
979
- //#region src/events/subscribe-helpers.ts
980
- /**
981
- * Subscribe-side helpers — close the symmetry that arc's publish-side
982
- * already has via [`eventPlugin({ registry, validateMode: 'reject' })`](./eventPlugin.ts).
983
- *
984
- * Two wrapper pairs, both transport-agnostic:
985
- *
986
- * - `wrapWithSchema` / `subscribeWithSchema` — read the schema from a
987
- * typed `EventDefinitionOutput<T>` and validate `event.payload` before
988
- * the handler runs. Single source of truth for event shape: declare
989
- * once at `defineEvent`, enforced on BOTH sides without parallel Zod
990
- * schemas in subscriber files.
991
- *
992
- * - `wrapWithBoundary` / `subscribeWithBoundary` — catch + log + swallow
993
- * handler errors, for projection / cache-invalidation / fire-and-forget
994
- * handlers where retry would just delay the next-event resync. Lighter
995
- * than `withRetry` (no DLQ, no exponential backoff) and avoids the
996
- * "single bad event poisons the bus" hazard.
997
- *
998
- * Both pairs compose with `withRetry` and with each other:
999
- *
1000
- * ```ts
1001
- * await subscribeWithSchema(
1002
- * fastify,
1003
- * OrderPaid,
1004
- * withRetry(handler, { maxRetries: 3 }),
1005
- * );
1006
- * ```
1007
- *
1008
- * @see [retry.ts](./retry.ts) for the retry + DLQ wrapper.
1009
- * @see [defineEvent.ts](./defineEvent.ts) for `defineEvent` / `EventDefinitionOutput`.
1010
- */
1011
- /**
1012
- * Pure handler wrapper — returns a new `EventHandler` that validates
1013
- * `event.payload` against the definition's schema before invoking the handler.
1014
- *
1015
- * The returned handler's input is `DomainEvent<unknown>` (since the transport
1016
- * delivers untyped events) but the inner `handler` receives `DomainEvent<T>`.
1017
- * No cast at the call site.
1018
- *
1019
- * @example
1020
- * ```ts
1021
- * await fastify.events.subscribe(
1022
- * OrderPaid.name,
1023
- * wrapWithSchema(OrderPaid, async (event) => {
1024
- * // event.payload is typed via the registered schema — no cast.
1025
- * await postSalesEntry(event.payload.orderId, event.payload.total);
1026
- * }),
1027
- * );
1028
- * ```
1029
- */
1030
- function wrapWithSchema(definition, handler, options = {}) {
1031
- const { validate, registry, onInvalid, logger = console, name } = options;
1032
- const label = name ?? definition.name;
1033
- return async (event) => {
1034
- const eventVersion = typeof event.meta?.schemaVersion === "number" ? event.meta.schemaVersion : definition.version;
1035
- let result;
1036
- if (validate && definition.schema) result = validate(definition.schema, event.payload);
1037
- else if (registry) result = registry.validate(definition.name, event.payload, eventVersion);
1038
- else if (definition.schema) {
1039
- const adhoc = createEventRegistry();
1040
- adhoc.register(definition);
1041
- result = adhoc.validate(definition.name, event.payload);
1042
- } else result = { valid: true };
1043
- if (!result.valid) {
1044
- const errors = result.errors ?? ["payload failed validation"];
1045
- if (onInvalid) try {
1046
- await onInvalid(event, errors);
1047
- } catch (cbErr) {
1048
- logger.error(`[Arc Events] '${label}' onInvalid callback threw:`, cbErr);
1049
- }
1050
- else logger.warn(`[Arc Events] '${label}' skipped event ${event.meta?.id ?? "<no-id>"} — payload failed validation: ${errors.join("; ")}`);
1051
- return;
1052
- }
1053
- await handler(event);
1054
- };
1055
- }
1056
- /**
1057
- * Convenience: validate + subscribe in one call. Equivalent to
1058
- * `fastify.events.subscribe(definition.name, wrapWithSchema(definition, handler, options))`.
1059
- *
1060
- * Returns the unsubscribe function from the underlying transport.
1061
- *
1062
- * @example
1063
- * ```ts
1064
- * await subscribeWithSchema(fastify, OrderPaid, async (event) => {
1065
- * await postSalesEntry(event.payload.orderId, event.payload.total);
1066
- * });
1067
- *
1068
- * // Compose with withRetry — schema validation runs FIRST, then retry on
1069
- * // handler failure. Invalid payloads skip without burning retry attempts.
1070
- * await subscribeWithSchema(
1071
- * fastify,
1072
- * OrderPaid,
1073
- * withRetry(handler, { maxRetries: 3 }),
1074
- * );
1075
- * ```
1076
- */
1077
- async function subscribeWithSchema(fastify, definition, handler, options) {
1078
- return fastify.events.subscribe(definition.name, wrapWithSchema(definition, handler, options));
1079
- }
1080
- /**
1081
- * Pure handler wrapper — returns a new `EventHandler` that catches handler
1082
- * exceptions and routes them to `onError` (or logs and swallows). For
1083
- * projection / cache-invalidation / fire-and-forget handlers where retry
1084
- * would just delay the next-event resync, and where one bad event must NOT
1085
- * stop processing of subsequent events.
1086
- *
1087
- * Lighter than `withRetry`: no exponential backoff, no DLQ. Composes with
1088
- * `withRetry` if you want both ("retry, then log if exhausted, never throw").
1089
- *
1090
- * @example
1091
- * ```ts
1092
- * await fastify.events.subscribe(
1093
- * 'product:variants.changed',
1094
- * wrapWithBoundary(async (event) => {
1095
- * cache.invalidate(event.payload.productId);
1096
- * }),
1097
- * );
1098
- * ```
1099
- */
1100
- function wrapWithBoundary(handler, options = {}) {
1101
- const { onError, logger = console, name } = options;
1102
- const label = name ?? handler.name ?? "anonymous";
1103
- return async (event) => {
1104
- try {
1105
- await handler(event);
1106
- } catch (err) {
1107
- const error = err instanceof Error ? err : new Error(String(err));
1108
- if (onError) try {
1109
- await onError(error, event);
1110
- } catch (cbErr) {
1111
- logger.error(`[Arc Events] '${label}' onError callback threw:`, cbErr);
1112
- }
1113
- else logger.error(`[Arc Events] '${label}' threw on ${event.type} — swallowed (boundary): ${error.message}`, {
1114
- err: error,
1115
- event: event.type,
1116
- eventId: event.meta?.id,
1117
- handler: label
1118
- });
1119
- }
1120
- };
1121
- }
1122
- /**
1123
- * Convenience: subscribe + error-boundary in one call. Equivalent to
1124
- * `fastify.events.subscribe(pattern, wrapWithBoundary(handler, options))`.
1125
- *
1126
- * Returns the unsubscribe function from the underlying transport.
1127
- */
1128
- async function subscribeWithBoundary(fastify, pattern, handler, options) {
1129
- return fastify.events.subscribe(pattern, wrapWithBoundary(handler, options));
1130
- }
1131
- //#endregion
1132
797
  export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, MemoryEventTransport, MemoryOutboxStore, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
@@ -1,7 +1,7 @@
1
- import { a as ModuleContribution, c as ResourceModule, h as HealthCheck, i as EventHandlerDefinition, l as loadResources, n as ArcModuleInput, o as LoadResourcesOptions, r as ArcModuleRegistry, s as ResourceLike, t as ArcModule } from "../types-Cu6vVJmh.mjs";
2
- import { a as orderModules, c as collectModuleScheduledJobs, i as resolveModule, l as collectModuleWorkflows, n as getModuleExports, o as subscribeModuleEventHandlers, r as resolveContribution, s as collectModuleHealthChecks, t as defineModule } from "../index-CZY4EGga.mjs";
3
- import "../fastify-augmentation-OzYGIiU1.mjs";
4
- import { a as RawBodyOptions, c as BetterAuthOption, d as JwtAuthOption, i as MultipartOptions, l as CustomAuthenticatorOption, n as preloadResourcesAsync, o as UnderPressureOptions, r as CreateAppOptions, s as AuthOption, t as preloadResources, u as CustomPluginAuthOption } from "../preloadResources-CM4uUaFv.mjs";
1
+ import { a as ModuleContribution, c as ResourceModule, h as HealthCheck, i as EventHandlerDefinition, l as loadResources, n as ArcModuleInput, o as LoadResourcesOptions, r as ArcModuleRegistry, s as ResourceLike, t as ArcModule } from "../types-D_n8L6VY.mjs";
2
+ import { a as orderModules, c as collectModuleScheduledJobs, i as resolveModule, l as collectModuleWorkflows, n as getModuleExports, o as subscribeModuleEventHandlers, r as resolveContribution, s as collectModuleHealthChecks, t as defineModule } from "../index-CCzVR-N3.mjs";
3
+ import "../fastify-augmentation-0WhPnUH7.mjs";
4
+ import { a as RawBodyOptions, c as BetterAuthOption, d as JwtAuthOption, i as MultipartOptions, l as CustomAuthenticatorOption, n as preloadResourcesAsync, o as UnderPressureOptions, r as CreateAppOptions, s as AuthOption, t as preloadResources, u as CustomPluginAuthOption } from "../preloadResources-DCfa2egz.mjs";
5
5
  import { FastifyInstance, FastifyServerOptions } from "fastify";
6
6
  //#region src/factory/createApp.d.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
1
  import { r as mergeHealthChecks, t as healthPlugin } from "../health-CB_m4S32.mjs";
2
- import { a as edgePreset, c as testingPreset, d as subscribeModuleEventHandlers, f as collectModuleHealthChecks, g as resolveModule, h as resolveContribution, i as developmentPreset, l as workerPreset, m as collectModuleWorkflows, n as createApp, o as getPreset, p as collectModuleScheduledJobs, s as productionPreset, t as ArcFactory, u as orderModules } from "../createApp-Du5G54TM.mjs";
2
+ import { a as edgePreset, c as testingPreset, d as subscribeModuleEventHandlers, f as collectModuleHealthChecks, g as resolveModule, h as resolveContribution, i as developmentPreset, l as workerPreset, m as collectModuleWorkflows, n as createApp, o as getPreset, p as collectModuleScheduledJobs, s as productionPreset, t as ArcFactory, u as orderModules } from "../createApp-CXrKFciH.mjs";
3
3
  import { n as getModuleExports, t as defineModule } from "../module-_-M7C5ES.mjs";
4
4
  import { t as loadResources } from "../loadResources-DpU8pfjV.mjs";
5
5
  import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-DSY5HGpS.mjs";
@@ -1,7 +1,7 @@
1
- import { mn as HookSystem, z as ResourceRegistry } from "./index-DPRH0Cme.mjs";
1
+ import { mn as HookSystem, z as ResourceRegistry } from "./index-Q5gzEIWY.mjs";
2
2
  import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
3
- import { h as HealthCheck, r as ArcModuleRegistry, t as ArcModule, u as ScheduleDefinition } from "./types-Cu6vVJmh.mjs";
4
- import "./index-CZY4EGga.mjs";
3
+ import { h as HealthCheck, r as ArcModuleRegistry, t as ArcModule, u as ScheduleDefinition } from "./types-D_n8L6VY.mjs";
4
+ import "./index-CCzVR-N3.mjs";
5
5
  import { FastifyPluginAsync } from "fastify";
6
6
  //#region src/core/arcCorePlugin.d.ts
7
7
  interface ArcCorePluginOptions {
@@ -1,4 +1,3 @@
1
- import { t as ArcError } from "./errors-DkO8Nk6-.mjs";
2
1
  //#region src/permissions/fields.ts
3
2
  /**
4
3
  * Field-Level Permissions
@@ -166,109 +165,4 @@ function resolveEffectiveRoles(userRoles, orgRoles) {
166
165
  return [.../* @__PURE__ */ new Set([...userRoles, ...orgRoles])];
167
166
  }
168
167
  //#endregion
169
- //#region src/permissions/applyPermissionResult.ts
170
- /**
171
- * Normalize a permission check return value (`boolean | PermissionResult`)
172
- * into a concrete `PermissionResult`. This is the only place in Arc that
173
- * promotes booleans to results — keeps the type narrowing honest everywhere.
174
- */
175
- function normalizePermissionResult(result) {
176
- if (typeof result === "boolean") return { granted: result };
177
- return result;
178
- }
179
- /**
180
- * Apply a granted `PermissionResult` to a Fastify request — merges row-level
181
- * filters into `_policyFilters` and conditionally installs the scope.
182
- *
183
- * **Scope install rule:** only writes `scope` when the current request scope
184
- * is absent or `public`. This prevents downgrading an already-authenticated
185
- * request (e.g. Better Auth set `member`, then a permission check returns a
186
- * narrower `service` scope — the original `member` wins because it came from
187
- * a more authoritative source).
188
- *
189
- * Safe to call with a non-granted result — it simply no-ops. Callers should
190
- * still check `result.granted` and send an error response before reaching here,
191
- * but this function tolerates the misuse defensively.
192
- */
193
- function applyPermissionResult(result, request) {
194
- if (!result.granted) return;
195
- if (result.filters) request._policyFilters = {
196
- ...request._policyFilters ?? {},
197
- ...result.filters
198
- };
199
- if (result.scope) {
200
- const current = request.scope;
201
- if (!current || current.kind === "public") request.scope = result.scope;
202
- }
203
- }
204
- /**
205
- * Max length of a `PermissionResult.reason` string before we fall back to the
206
- * generic default message. Upstream checks can return arbitrary strings; we
207
- * clamp to prevent accidental leakage of internal diagnostics or oversized
208
- * payloads via the 4xx response body.
209
- */
210
- const MAX_DENIAL_REASON_LENGTH = 100;
211
- /**
212
- * End-to-end evaluator: runs the permission check, catches throws, normalizes
213
- * the result, sends a 401/403 response on denial, and applies side-effects on
214
- * grant. Returns `true` if the caller should continue, `false` if a response
215
- * has been sent and the caller should return.
216
- *
217
- * This is the single source of truth for the 5-step sequence shared by the
218
- * CRUD router, action router, and MCP tool handlers:
219
- *
220
- * 1. `try { await check(ctx) } catch { reply 403 }`
221
- * 2. `normalizePermissionResult(result)`
222
- * 3. If denied → 401 (no user) or 403 (user) with clamped reason
223
- * 4. If granted → `applyPermissionResult` (filters + scope)
224
- * 5. Return true/false so the caller knows whether to keep going
225
- *
226
- * Context construction, pre-check auth gating, and success-path handler
227
- * invocation stay at the callsite — those are genuinely different per router
228
- * and don't belong here.
229
- *
230
- * @returns `true` if authorized (caller continues), `false` if a response was sent
231
- */
232
- async function evaluateAndApplyPermission(check, context, request, reply, opts) {
233
- let result;
234
- try {
235
- result = await check(context);
236
- } catch (err) {
237
- request.log?.warn?.({
238
- err,
239
- resource: context.resource,
240
- action: context.action
241
- }, "Permission check threw");
242
- if (err instanceof ArcError) {
243
- reply.code(err.status).send({
244
- code: err.code,
245
- message: err.message,
246
- status: err.status,
247
- ...err.meta ? { meta: err.meta } : {}
248
- });
249
- return false;
250
- }
251
- reply.code(403).send({
252
- code: "arc.forbidden",
253
- message: "Permission denied",
254
- status: 403
255
- });
256
- return false;
257
- }
258
- const permResult = normalizePermissionResult(result);
259
- if (!permResult.granted) {
260
- const defaultMsg = opts?.defaultDenialMessage?.(context.user) ?? (context.user ? "Permission denied" : "Authentication required");
261
- const reason = permResult.reason && permResult.reason.length <= MAX_DENIAL_REASON_LENGTH ? permResult.reason : defaultMsg;
262
- const status = context.user ? 403 : 401;
263
- reply.code(status).send({
264
- code: context.user ? "arc.forbidden" : "arc.unauthorized",
265
- message: reason,
266
- status
267
- });
268
- return false;
269
- }
270
- applyPermissionResult(permResult, request);
271
- return true;
272
- }
273
- //#endregion
274
- export { applyFieldWritePermissions as a, applyFieldReadPermissions as i, evaluateAndApplyPermission as n, fields as o, normalizePermissionResult as r, resolveEffectiveRoles as s, applyPermissionResult as t };
168
+ export { resolveEffectiveRoles as i, applyFieldWritePermissions as n, fields as r, applyFieldReadPermissions as t };
@@ -1,2 +1,2 @@
1
- import { Cn as defineHook, Sn as createHookSystem, _n as afterDelete, bn as beforeDelete, cn as DefineHookOptions, dn as HookOperation, fn as HookPhase, gn as afterCreate, hn as HookSystemOptions, ln as HookContext, mn as HookSystem, pn as HookRegistration, un as HookHandler, vn as afterUpdate, xn as beforeUpdate, yn as beforeCreate } from "../index-DPRH0Cme.mjs";
1
+ import { Cn as defineHook, Sn as createHookSystem, _n as afterDelete, bn as beforeDelete, cn as DefineHookOptions, dn as HookOperation, fn as HookPhase, gn as afterCreate, hn as HookSystemOptions, ln as HookContext, mn as HookSystem, pn as HookRegistration, un as HookHandler, vn as afterUpdate, xn as beforeUpdate, yn as beforeCreate } from "../index-Q5gzEIWY.mjs";
2
2
  export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,2 +1,2 @@
1
- import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-C1vB_Gc9.mjs";
1
+ import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-WkIougo6.mjs";
2
2
  export { HookSystem, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,9 +1,9 @@
1
1
  import { n as requireSingleHeaderValue } from "../headers-BvlQSDMB.mjs";
2
2
  import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DY6xq4sM.mjs";
3
3
  import { t as createIdempotencyResult } from "../interface-n5hpKg1d.mjs";
4
+ import { and, eq, exists, gt, lt, or, startsWith } from "@classytic/repo-core/filter";
4
5
  import { createHash } from "node:crypto";
5
6
  import fp from "fastify-plugin";
6
- import { and, eq, exists, gt, lt, or, startsWith } from "@classytic/repo-core/filter";
7
7
  import { update } from "@classytic/repo-core/update";
8
8
  //#region src/idempotency/repository-idempotency-adapter.ts
9
9
  function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
@@ -1,4 +1,4 @@
1
- import { a as ModuleContribution, h as HealthCheck, n as ArcModuleInput, r as ArcModuleRegistry, t as ArcModule, u as ScheduleDefinition } from "./types-Cu6vVJmh.mjs";
1
+ import { a as ModuleContribution, h as HealthCheck, n as ArcModuleInput, r as ArcModuleRegistry, t as ArcModule, u as ScheduleDefinition } from "./types-D_n8L6VY.mjs";
2
2
  import { FastifyInstance } from "fastify";
3
3
  //#region src/factory/module/contributions.d.ts
4
4
  /**