@classytic/arc 2.8.5 → 2.10.3

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 (155) hide show
  1. package/README.md +50 -38
  2. package/dist/{BaseController-DAGGc5Xn.mjs → BaseController-CbKKIflT.mjs} +193 -143
  3. package/dist/EventTransport-CUw5NNWe.d.mts +293 -0
  4. package/dist/{ResourceRegistry-C6uXlWe3.mjs → ResourceRegistry-BPd6NQDm.mjs} +1 -1
  5. package/dist/adapters/index.d.mts +3 -3
  6. package/dist/adapters/index.mjs +2 -2
  7. package/dist/{adapters-BBqAVvPK.mjs → adapters-BXY4i-hw.mjs} +210 -41
  8. package/dist/audit/index.d.mts +135 -11
  9. package/dist/audit/index.mjs +107 -20
  10. package/dist/auth/index.d.mts +17 -9
  11. package/dist/auth/index.mjs +14 -7
  12. package/dist/auth/redis-session.d.mts +1 -1
  13. package/dist/{betterAuthOpenApi-BuUcUEJq.mjs → betterAuthOpenApi-BBRVhjQN.mjs} +1 -1
  14. package/dist/cache/index.d.mts +17 -15
  15. package/dist/cache/index.mjs +15 -14
  16. package/dist/{caching-IMuYVjTL.mjs → caching-CBpK_SCM.mjs} +8 -3
  17. package/dist/cli/commands/describe.mjs +1 -1
  18. package/dist/cli/commands/docs.mjs +2 -2
  19. package/dist/cli/commands/generate.mjs +1 -1
  20. package/dist/cli/commands/init.mjs +1 -1
  21. package/dist/cli/commands/introspect.mjs +1 -1
  22. package/dist/core/index.d.mts +3 -3
  23. package/dist/core/index.mjs +4 -6
  24. package/dist/{defineResource-tcgySDo1.mjs → core-CcR01lup.mjs} +58 -61
  25. package/dist/{createActionRouter-BORM8f17.mjs → createActionRouter-Bp_5c_2b.mjs} +3 -3
  26. package/dist/{createApp-B1EY8zxa.mjs → createApp-BuvPma24.mjs} +15 -14
  27. package/dist/docs/index.d.mts +2 -2
  28. package/dist/docs/index.mjs +2 -2
  29. package/dist/{elevation-DtFxrG0s.mjs → elevation-C7hgL_aI.mjs} +22 -8
  30. package/dist/{errorHandler-f869_8PQ.mjs → errorHandler-Bb49BvPD.mjs} +59 -7
  31. package/dist/{errorHandler-Bah5JhBd.d.mts → errorHandler-DRQ3EqfL.d.mts} +37 -2
  32. package/dist/{eventPlugin-D9DKB2zM.d.mts → eventPlugin-CxWgpd6K.d.mts} +14 -2
  33. package/dist/{eventPlugin-CDjVTM82.mjs → eventPlugin-DCUjuiQT.mjs} +83 -5
  34. package/dist/events/index.d.mts +150 -36
  35. package/dist/events/index.mjs +355 -101
  36. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  37. package/dist/events/transports/redis.d.mts +1 -1
  38. package/dist/factory/index.d.mts +1 -1
  39. package/dist/factory/index.mjs +2 -2
  40. package/dist/{types-DZi1aYhm.d.mts → fields-Lo1VUDpt.d.mts} +121 -1
  41. package/dist/{fields-ipsbIRPK.mjs → fields-bxkeltzz.mjs} +18 -5
  42. package/dist/{filesUpload-C7r7HIeA.mjs → filesUpload-t21LS-py.mjs} +65 -7
  43. package/dist/hooks/index.d.mts +1 -1
  44. package/dist/hooks/index.mjs +1 -1
  45. package/dist/idempotency/index.d.mts +32 -5
  46. package/dist/idempotency/index.mjs +119 -12
  47. package/dist/idempotency/redis.d.mts +1 -1
  48. package/dist/{index-DtDzOBn8.d.mts → index-8qw4y6ff.d.mts} +4 -135
  49. package/dist/{index-BLXBmWud.d.mts → index-ChIw3776.d.mts} +283 -408
  50. package/dist/{interface-CMRutPfe.d.mts → index-Cl0uoKd5.d.mts} +1758 -2506
  51. package/dist/{index-C1meYuDn.d.mts → index-DStwgFUK.d.mts} +81 -7
  52. package/dist/index.d.mts +7 -8
  53. package/dist/index.mjs +11 -12
  54. package/dist/integrations/event-gateway.d.mts +1 -1
  55. package/dist/integrations/event-gateway.mjs +1 -1
  56. package/dist/integrations/index.d.mts +1 -1
  57. package/dist/integrations/mcp/index.d.mts +26 -8
  58. package/dist/integrations/mcp/index.mjs +96 -17
  59. package/dist/integrations/mcp/testing.d.mts +1 -1
  60. package/dist/integrations/mcp/testing.mjs +1 -1
  61. package/dist/integrations/webhooks.d.mts +5 -0
  62. package/dist/integrations/webhooks.mjs +6 -0
  63. package/dist/interface-D218ikEo.d.mts +77 -0
  64. package/dist/{memory-Cp7_cAko.mjs → memory-B5Amv9A1.mjs} +23 -8
  65. package/dist/{openapi-CbKUJY_m.mjs → openapi-B5F8AddX.mjs} +3 -3
  66. package/dist/org/index.d.mts +2 -2
  67. package/dist/permissions/index.d.mts +3 -4
  68. package/dist/permissions/index.mjs +5 -5
  69. package/dist/{permissions-CH4cNwJi.mjs → permissions-Dk6mshja.mjs} +315 -397
  70. package/dist/plugins/index.d.mts +7 -7
  71. package/dist/plugins/index.mjs +14 -16
  72. package/dist/plugins/response-cache.mjs +2 -2
  73. package/dist/plugins/tracing-entry.d.mts +1 -1
  74. package/dist/plugins/tracing-entry.mjs +1 -1
  75. package/dist/presets/filesUpload.d.mts +27 -5
  76. package/dist/presets/filesUpload.mjs +1 -1
  77. package/dist/presets/index.d.mts +3 -2
  78. package/dist/presets/index.mjs +4 -3
  79. package/dist/presets/multiTenant.d.mts +1 -1
  80. package/dist/presets/multiTenant.mjs +2 -2
  81. package/dist/presets/search.d.mts +178 -0
  82. package/dist/presets/search.mjs +150 -0
  83. package/dist/{presets-C2xgzW6x.mjs → presets-fLJVXdVn.mjs} +1 -1
  84. package/dist/{queryCachePlugin-BJJGBTlu.d.mts → queryCachePlugin-BKbWjgDG.d.mts} +1 -1
  85. package/dist/{queryCachePlugin-BH-fidlv.mjs → queryCachePlugin-DQCEfJis.mjs} +9 -9
  86. package/dist/{queryParser-CgCtsjti.mjs → queryParser-DBqBB6AC.mjs} +1 -1
  87. package/dist/{redis-BM00zaPB.d.mts → redis-DqyeggCa.d.mts} +1 -1
  88. package/dist/{redis-stream-CrsfUmPt.d.mts → redis-stream-CakIQmwR.d.mts} +1 -1
  89. package/dist/registry/index.d.mts +1 -1
  90. package/dist/registry/index.mjs +2 -2
  91. package/dist/{resourceToTools-8s-EsCCe.mjs → resourceToTools-BElv3xPT.mjs} +65 -48
  92. package/dist/{schemaConverter-Y7nCYaLJ.mjs → schemaConverter-BxFDdtXu.mjs} +1 -1
  93. package/dist/scope/index.d.mts +1 -1
  94. package/dist/scope/index.mjs +2 -2
  95. package/dist/{sse-Ad7ypl9e.mjs → sse-yBCgOLGu.mjs} +1 -1
  96. package/dist/store-helpers-ZCSMJJAX.mjs +57 -0
  97. package/dist/testing/index.d.mts +9 -17
  98. package/dist/testing/index.mjs +27 -83
  99. package/dist/testing/storageContract.d.mts +1 -1
  100. package/dist/types/index.d.mts +4 -4
  101. package/dist/types/index.mjs +1 -31
  102. package/dist/types/storage.d.mts +1 -1
  103. package/dist/{types-BsbNMEDR.d.mts → types-Btdda02s.d.mts} +1 -1
  104. package/dist/{types-Ch9pTQbf.d.mts → types-Co8k3NyS.d.mts} +11 -9
  105. package/dist/types-Csi3FLfq.mjs +27 -0
  106. package/dist/utils/index.d.mts +208 -4
  107. package/dist/utils/index.mjs +5 -6
  108. package/dist/{utils-yYT3HDXt.mjs → utils-B2fNOD_i.mjs} +285 -2
  109. package/dist/{versioning-CDugduqI.mjs → versioning-C2U_bLY0.mjs} +3 -5
  110. package/package.json +20 -26
  111. package/skills/arc/SKILL.md +97 -23
  112. package/skills/arc/references/auth.md +94 -0
  113. package/skills/arc/references/events.md +200 -12
  114. package/skills/arc/references/mcp.md +4 -17
  115. package/skills/arc/references/multi-tenancy.md +43 -0
  116. package/skills/arc/references/production.md +34 -60
  117. package/dist/EventTransport-BXja8NOc.d.mts +0 -135
  118. package/dist/audit/mongodb.d.mts +0 -2
  119. package/dist/audit/mongodb.mjs +0 -2
  120. package/dist/circuitBreaker-cmi5XDv5.mjs +0 -284
  121. package/dist/circuitBreaker-dTtG-UyS.d.mts +0 -206
  122. package/dist/core-F0QoWBt2.mjs +0 -34
  123. package/dist/dynamic/index.d.mts +0 -93
  124. package/dist/dynamic/index.mjs +0 -122
  125. package/dist/fields-DpZQa_Q3.d.mts +0 -109
  126. package/dist/idempotency/mongodb.d.mts +0 -2
  127. package/dist/idempotency/mongodb.mjs +0 -123
  128. package/dist/interface-4y979v99.d.mts +0 -54
  129. package/dist/mongodb-BsP-WbhN.d.mts +0 -127
  130. package/dist/mongodb-CTcp0hQZ.d.mts +0 -80
  131. package/dist/mongodb-Utc5k_-0.mjs +0 -90
  132. package/dist/policies/index.d.mts +0 -432
  133. package/dist/policies/index.mjs +0 -318
  134. package/dist/rpc/index.d.mts +0 -90
  135. package/dist/rpc/index.mjs +0 -248
  136. /package/dist/{HookSystem-HprTmvVY.mjs → HookSystem-BNYKnrXF.mjs} +0 -0
  137. /package/dist/{applyPermissionResult-D6GPMsvh.mjs → applyPermissionResult-QhV1Pa-g.mjs} +0 -0
  138. /package/dist/{constants-Cxde4rpC.mjs → constants-BhY1OHoH.mjs} +0 -0
  139. /package/dist/{elevation-B6S5csVA.d.mts → elevation-C5SwtkAn.d.mts} +0 -0
  140. /package/dist/{errors-Ck2h67pm.d.mts → errors-CCSsMpXE.d.mts} +0 -0
  141. /package/dist/{errors-BF2bIOIS.mjs → errors-D5c-5BJL.mjs} +0 -0
  142. /package/dist/{externalPaths-BnkYrNzp.d.mts → externalPaths-BQ8QijNH.d.mts} +0 -0
  143. /package/dist/{interface-DfLGcus7.d.mts → interface-CSbZdv_3.d.mts} +0 -0
  144. /package/dist/{loadResources-PWd0OCpV.mjs → loadResources-BAzJItAJ.mjs} +0 -0
  145. /package/dist/{logger-D1YrIImS.mjs → logger-DLg8-Ueg.mjs} +0 -0
  146. /package/dist/{metrics-B-PU4-Yu.mjs → metrics-DuhiSEZI.mjs} +0 -0
  147. /package/dist/{pluralize-CWP6MB39.mjs → pluralize-A0tWEl1K.mjs} +0 -0
  148. /package/dist/{registry-BiTKT1Dg.mjs → registry-B3lRFBWo.mjs} +0 -0
  149. /package/dist/{replyHelpers-CxkYGT81.mjs → replyHelpers-CXtJDAZ0.mjs} +0 -0
  150. /package/dist/{requestContext-DYvHl113.mjs → requestContext-xHIKedG6.mjs} +0 -0
  151. /package/dist/{sessionManager-DDCmiNIo.d.mts → sessionManager-BkzVU8h2.d.mts} +0 -0
  152. /package/dist/{storage-Dfzt4VTl.d.mts → storage-CVk_SEn2.d.mts} +0 -0
  153. /package/dist/{tracing-DdN2-wHJ.d.mts → tracing-65B51Dw3.d.mts} +0 -0
  154. /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-Cj5Rgvlg.mjs} +0 -0
  155. /package/dist/{types-ZUu_h0jp.mjs → types-DV9WDfeg.mjs} +0 -0
@@ -1,122 +0,0 @@
1
- import { t as ArcQueryParser } from "../queryParser-CgCtsjti.mjs";
2
- import { n as defineResource } from "../defineResource-tcgySDo1.mjs";
3
- import { C as publicRead, T as readOnly, b as fullPublic, v as adminOnly, w as publicReadAdminWrite, x as ownerWithAdminBypass, y as authenticated } from "../permissions-CH4cNwJi.mjs";
4
- //#region src/dynamic/ArcDynamicLoader.ts
5
- const VALID_FIELD_TYPES = new Set([
6
- "string",
7
- "number",
8
- "boolean",
9
- "date",
10
- "object",
11
- "array"
12
- ]);
13
- function validateSchema(schema) {
14
- if (!schema.app || typeof schema.app !== "string") throw new Error("AAS: 'app' name is required");
15
- if (!Array.isArray(schema.resources) || schema.resources.length === 0) throw new Error("AAS: 'resources' must be a non-empty array");
16
- for (const r of schema.resources) {
17
- if (!r.name || typeof r.name !== "string") throw new Error("AAS: each resource must have a 'name' string");
18
- if (!r.permissions) throw new Error(`AAS: resource "${r.name}" must have 'permissions'`);
19
- if (r.fields) for (const [fieldName, fieldDef] of Object.entries(r.fields)) {
20
- const type = typeof fieldDef === "string" ? fieldDef : fieldDef.type;
21
- if (!VALID_FIELD_TYPES.has(type)) throw new Error(`AAS: resource "${r.name}" field "${fieldName}" has invalid type "${type}". Valid types: ${[...VALID_FIELD_TYPES].join(", ")}`);
22
- }
23
- }
24
- }
25
- /**
26
- * Load an Arc Architecture Schema (JSON) and produce fully configured ResourceDefinitions.
27
- *
28
- * Each resource gets:
29
- * - Adapter from the resolver
30
- * - Permissions from presets or fine-grained map
31
- * - schemaOptions.fieldRules for validation and MCP tool schemas
32
- * - ArcQueryParser with allowedFilterFields/allowedSortFields for MCP auto-derive
33
- * - Presets applied
34
- */
35
- var ArcDynamicLoader = class {
36
- context;
37
- constructor(context) {
38
- this.context = context;
39
- }
40
- /**
41
- * Load an AAS definition and return fully constructed ResourceDefinitions.
42
- * Validates the schema before processing — throws on malformed input.
43
- */
44
- load(schema) {
45
- validateSchema(schema);
46
- return schema.resources.map((r) => {
47
- const adapter = this.context.adapterResolver(r.name, r.adapterPattern);
48
- const fieldRules = this.buildFieldRules(r.fields);
49
- const queryParser = this.buildQueryParser(r);
50
- return defineResource({
51
- name: r.name,
52
- displayName: r.displayName,
53
- prefix: r.prefix,
54
- adapter,
55
- queryParser,
56
- presets: r.presets,
57
- permissions: this.resolvePermissions(r.permissions),
58
- disabledRoutes: r.disabledRoutes,
59
- tenantField: r.tenantField,
60
- schemaOptions: fieldRules ? {
61
- fieldRules,
62
- filterableFields: r.filterable
63
- } : void 0
64
- });
65
- });
66
- }
67
- buildFieldRules(fields) {
68
- if (!fields) return void 0;
69
- const rules = {};
70
- for (const [name, def] of Object.entries(fields)) rules[name] = typeof def === "string" ? { type: def } : def;
71
- return rules;
72
- }
73
- buildQueryParser(r) {
74
- if (!r.filterable && !r.sortable) return void 0;
75
- return new ArcQueryParser({
76
- allowedFilterFields: r.filterable,
77
- allowedSortFields: r.sortable
78
- });
79
- }
80
- resolvePermissions(policy) {
81
- if (typeof policy === "string") return this.resolvePreset(policy);
82
- return this.resolveFinGrained(policy);
83
- }
84
- resolvePreset(preset) {
85
- switch (preset) {
86
- case "publicRead": return publicRead();
87
- case "publicReadAdminWrite": return publicReadAdminWrite();
88
- case "authenticated": return authenticated();
89
- case "adminOnly": return adminOnly();
90
- case "ownerWithAdminBypass": return ownerWithAdminBypass();
91
- case "fullPublic": return fullPublic();
92
- case "readOnly": return readOnly();
93
- default:
94
- if (this.context.permissionResolver) {
95
- const resolved = this.context.permissionResolver(preset);
96
- if (resolved) return resolved;
97
- }
98
- throw new Error(`Unknown permission preset: "${preset}"`);
99
- }
100
- }
101
- resolveFinGrained(policy) {
102
- const pick = (preset, op) => preset[op] ?? authenticated()[op];
103
- const map = {
104
- public: (op) => pick(publicRead(), op),
105
- auth: (op) => pick(authenticated(), op),
106
- admin: (op) => pick(adminOnly(), op),
107
- owner: (op) => pick(ownerWithAdminBypass(), op)
108
- };
109
- const permissions = {};
110
- const ops = {
111
- list: policy.list,
112
- get: policy.get,
113
- create: policy.create,
114
- update: policy.update,
115
- delete: policy.delete
116
- };
117
- for (const [op, level] of Object.entries(ops)) if (level && map[level]) permissions[op] = map[level](op);
118
- return permissions;
119
- }
120
- };
121
- //#endregion
122
- export { ArcDynamicLoader };
@@ -1,109 +0,0 @@
1
- //#region src/permissions/fields.d.ts
2
- /**
3
- * Field-Level Permissions
4
- *
5
- * Control field visibility and writability per role.
6
- * Integrated into the response path (read) and sanitization path (write).
7
- *
8
- * @example
9
- * ```typescript
10
- * import { fields, defineResource } from '@classytic/arc';
11
- *
12
- * const userResource = defineResource({
13
- * name: 'user',
14
- * adapter: userAdapter,
15
- * fields: {
16
- * salary: fields.visibleTo(['admin', 'hr']),
17
- * internalNotes: fields.writableBy(['admin']),
18
- * email: fields.redactFor(['viewer']),
19
- * password: fields.hidden(),
20
- * },
21
- * });
22
- * ```
23
- */
24
- type FieldPermissionType = "hidden" | "visibleTo" | "writableBy" | "redactFor";
25
- interface FieldPermission {
26
- readonly _type: FieldPermissionType;
27
- readonly roles?: readonly string[];
28
- readonly redactValue?: unknown;
29
- }
30
- type FieldPermissionMap = Record<string, FieldPermission>;
31
- declare const fields: {
32
- /**
33
- * Field is never included in responses. Not writable via API.
34
- *
35
- * @example
36
- * ```typescript
37
- * fields: { password: fields.hidden() }
38
- * ```
39
- */
40
- hidden(): FieldPermission;
41
- /**
42
- * Field is only visible to users with specified roles.
43
- * Other users don't see the field at all.
44
- *
45
- * @example
46
- * ```typescript
47
- * fields: { salary: fields.visibleTo(['admin', 'hr']) }
48
- * ```
49
- */
50
- visibleTo(roles: readonly string[]): FieldPermission;
51
- /**
52
- * Field is only writable by users with specified roles.
53
- * All users can still read the field. Users without the role
54
- * have the field silently stripped from write operations.
55
- *
56
- * @example
57
- * ```typescript
58
- * fields: { role: fields.writableBy(['admin']) }
59
- * ```
60
- */
61
- writableBy(roles: readonly string[]): FieldPermission;
62
- /**
63
- * Field is redacted (replaced with a placeholder) for specified roles.
64
- * Other users see the real value.
65
- *
66
- * @param roles - Roles that see the redacted value
67
- * @param redactValue - Replacement value (default: '***')
68
- *
69
- * @example
70
- * ```typescript
71
- * fields: {
72
- * email: fields.redactFor(['viewer']),
73
- * ssn: fields.redactFor(['basic'], '***-**-****'),
74
- * }
75
- * ```
76
- */
77
- redactFor(roles: readonly string[], redactValue?: unknown): FieldPermission;
78
- };
79
- /**
80
- * Apply field-level READ permissions to a response object.
81
- * Strips hidden fields, enforces visibility, and applies redaction.
82
- *
83
- * @param data - The response object (mutated in place for performance)
84
- * @param fieldPermissions - Field permission map from resource config
85
- * @param userRoles - Current user's roles (empty array for unauthenticated)
86
- * @returns The filtered object
87
- */
88
- declare function applyFieldReadPermissions<T extends Record<string, unknown>>(data: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): T;
89
- /**
90
- * Apply field-level WRITE permissions to request body.
91
- * Strips fields that the user doesn't have permission to write.
92
- *
93
- * @param body - The request body (returns a new filtered copy)
94
- * @param fieldPermissions - Field permission map from resource config
95
- * @param userRoles - Current user's roles
96
- * @returns Filtered body
97
- */
98
- declare function applyFieldWritePermissions<T extends Record<string, unknown>>(body: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): T;
99
- /**
100
- * Resolve effective roles by merging global user roles with org-level roles.
101
- *
102
- * Global roles come from `req.user.role` (normalized via getUserRoles()).
103
- * Org roles come from `req.context.orgRoles` (set by BA adapter's org bridge).
104
- *
105
- * When no org context exists, returns global roles only — backward compatible.
106
- */
107
- declare function resolveEffectiveRoles(userRoles: readonly string[], orgRoles: readonly string[]): string[];
108
- //#endregion
109
- export { applyFieldWritePermissions as a, applyFieldReadPermissions as i, FieldPermissionMap as n, fields as o, FieldPermissionType as r, resolveEffectiveRoles as s, FieldPermission as t };
@@ -1,2 +0,0 @@
1
- import { n as MongoIdempotencyStoreOptions, t as MongoIdempotencyStore } from "../mongodb-CTcp0hQZ.mjs";
2
- export { MongoIdempotencyStore, type MongoIdempotencyStoreOptions };
@@ -1,123 +0,0 @@
1
- //#region src/idempotency/stores/mongodb.ts
2
- var MongoIdempotencyStore = class {
3
- name = "mongodb";
4
- connection;
5
- collectionName;
6
- ttlMs;
7
- indexCreated = false;
8
- shouldEnsureIndex;
9
- logger;
10
- constructor(options) {
11
- this.connection = options.connection;
12
- this.collectionName = options.collection ?? "arc_idempotency";
13
- this.ttlMs = options.ttlMs ?? 864e5;
14
- this.shouldEnsureIndex = options.createIndex !== false;
15
- this.logger = options.logger ?? console;
16
- if (this.shouldEnsureIndex) this.ensureIndex().catch(() => {});
17
- }
18
- get collection() {
19
- return this.connection.db.collection(this.collectionName);
20
- }
21
- async ensureIndex() {
22
- if (this.indexCreated) return;
23
- try {
24
- await this.collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
25
- this.indexCreated = true;
26
- } catch (err) {
27
- const code = err?.code;
28
- if (code === 85 || code === 86) {
29
- this.indexCreated = true;
30
- return;
31
- }
32
- this.logger.warn(`[MongoIdempotencyStore] TTL index creation failed (will retry on next write): ${err.message ?? err}`);
33
- }
34
- }
35
- async get(key) {
36
- const doc = await this.collection.findOne({ _id: key });
37
- if (!doc?.result) return void 0;
38
- if (new Date(doc.expiresAt) < /* @__PURE__ */ new Date()) return;
39
- return {
40
- key,
41
- statusCode: doc.result.statusCode,
42
- headers: doc.result.headers,
43
- body: doc.result.body,
44
- createdAt: new Date(doc.createdAt),
45
- expiresAt: new Date(doc.expiresAt)
46
- };
47
- }
48
- async set(key, result) {
49
- if (this.shouldEnsureIndex && !this.indexCreated) await this.ensureIndex().catch(() => {});
50
- await this.collection.updateOne({ _id: key }, {
51
- $set: {
52
- result: {
53
- statusCode: result.statusCode,
54
- headers: result.headers,
55
- body: result.body
56
- },
57
- createdAt: result.createdAt,
58
- expiresAt: result.expiresAt
59
- },
60
- $unset: { lock: "" }
61
- }, { upsert: true });
62
- }
63
- async tryLock(key, requestId, ttlMs) {
64
- if (this.shouldEnsureIndex && !this.indexCreated) await this.ensureIndex().catch(() => {});
65
- const now = /* @__PURE__ */ new Date();
66
- const expiresAt = new Date(now.getTime() + ttlMs);
67
- try {
68
- const result = await this.collection.updateOne({
69
- _id: key,
70
- $or: [{ lock: { $exists: false } }, { "lock.expiresAt": { $lt: now } }]
71
- }, {
72
- $set: { lock: {
73
- requestId,
74
- expiresAt
75
- } },
76
- $setOnInsert: {
77
- createdAt: now,
78
- expiresAt: new Date(now.getTime() + this.ttlMs)
79
- }
80
- }, { upsert: true });
81
- return result.matchedCount === 1 || (result.upsertedCount ?? 0) === 1;
82
- } catch (err) {
83
- if (err?.code === 11e3) return false;
84
- throw err;
85
- }
86
- }
87
- async unlock(key, requestId) {
88
- await this.collection.updateOne({
89
- _id: key,
90
- "lock.requestId": requestId
91
- }, { $unset: { lock: "" } });
92
- }
93
- async isLocked(key) {
94
- const doc = await this.collection.findOne({ _id: key });
95
- if (!doc?.lock) return false;
96
- return new Date(doc.lock.expiresAt) > /* @__PURE__ */ new Date();
97
- }
98
- async delete(key) {
99
- await this.collection.deleteOne({ _id: key });
100
- }
101
- async deleteByPrefix(prefix) {
102
- return (await this.collection.deleteMany({ _id: { $regex: `^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}` } })).deletedCount;
103
- }
104
- async findByPrefix(prefix) {
105
- const doc = await this.collection.findOne({
106
- _id: { $regex: `^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}` },
107
- result: { $exists: true },
108
- expiresAt: { $gt: /* @__PURE__ */ new Date() }
109
- });
110
- if (!doc?.result) return void 0;
111
- return {
112
- key: doc._id,
113
- statusCode: doc.result.statusCode,
114
- headers: doc.result.headers,
115
- body: doc.result.body,
116
- createdAt: new Date(doc.createdAt),
117
- expiresAt: new Date(doc.expiresAt)
118
- };
119
- }
120
- async close() {}
121
- };
122
- //#endregion
123
- export { MongoIdempotencyStore };
@@ -1,54 +0,0 @@
1
- //#region src/cache/interface.d.ts
2
- /**
3
- * Generic Cache Store Interface
4
- *
5
- * Shared contract for reusable cache backends (memory, Redis, etc.).
6
- * Used by runtime systems such as dynamic permission matrices and QueryCache.
7
- */
8
- interface CacheLogger {
9
- warn(message: string, ...args: unknown[]): void;
10
- error(message: string, ...args: unknown[]): void;
11
- }
12
- interface CacheSetOptions {
13
- /** Time-to-live in milliseconds */
14
- ttlMs?: number;
15
- }
16
- interface CacheStats {
17
- /** Number of entries currently stored */
18
- entries: number;
19
- /** Estimated memory usage in bytes (-1 if unavailable) */
20
- memoryBytes: number;
21
- /** Cache hit count since creation */
22
- hits: number;
23
- /** Cache miss count since creation */
24
- misses: number;
25
- /** Number of entries evicted since creation */
26
- evictions: number;
27
- }
28
- interface CacheStore<TValue = unknown> {
29
- /** Store name for logs/diagnostics */
30
- readonly name: string;
31
- /**
32
- * Get cached value by key.
33
- * Returns undefined when missing or expired.
34
- */
35
- get(key: string): Promise<TValue | undefined>;
36
- /**
37
- * Set a cache value.
38
- * Store implementation handles TTL and eviction policy.
39
- */
40
- set(key: string, value: TValue, options?: CacheSetOptions): Promise<void>;
41
- /**
42
- * Delete a single cache key.
43
- */
44
- delete(key: string): Promise<void>;
45
- /**
46
- * Clear all keys in this store namespace.
47
- * Optional because distributed stores may not support cheap global clear.
48
- */
49
- clear?(): Promise<void>;
50
- /** Cache statistics for observability. */
51
- stats?(): CacheStats;
52
- }
53
- //#endregion
54
- export { CacheStore as i, CacheSetOptions as n, CacheStats as r, CacheLogger as t };
@@ -1,127 +0,0 @@
1
- import { i as UserBase } from "./types-DZi1aYhm.mjs";
2
-
3
- //#region src/audit/stores/interface.d.ts
4
- type AuditAction = "create" | "update" | "delete" | "restore" | "custom";
5
- interface AuditEntry {
6
- /** Unique audit log ID */
7
- id: string;
8
- /** Resource name (e.g., 'product', 'user') */
9
- resource: string;
10
- /** Document/entity ID */
11
- documentId: string;
12
- /** Action performed */
13
- action: AuditAction;
14
- /** User who performed the action */
15
- userId?: string;
16
- /** Organization context */
17
- organizationId?: string;
18
- /** Previous state (for updates) */
19
- before?: Record<string, unknown>;
20
- /** New state (for creates/updates) */
21
- after?: Record<string, unknown>;
22
- /** Changed fields (for updates) */
23
- changes?: string[];
24
- /** Request ID for tracing */
25
- requestId?: string;
26
- /** IP address */
27
- ipAddress?: string;
28
- /** User agent */
29
- userAgent?: string;
30
- /** Custom metadata */
31
- metadata?: Record<string, unknown>;
32
- /** When the action occurred */
33
- timestamp: Date;
34
- }
35
- interface AuditContext {
36
- user?: UserBase;
37
- organizationId?: string;
38
- requestId?: string;
39
- ipAddress?: string;
40
- userAgent?: string;
41
- /** HTTP method + route pattern (e.g., 'PATCH /api/products/:id') */
42
- endpoint?: string;
43
- /** Request duration in milliseconds */
44
- duration?: number;
45
- }
46
- interface AuditStoreOptions {
47
- /** Store name for logging */
48
- name: string;
49
- }
50
- /**
51
- * Abstract audit store interface
52
- */
53
- interface AuditStore {
54
- /** Store name */
55
- readonly name: string;
56
- /** Log an audit entry */
57
- log(entry: AuditEntry): Promise<void>;
58
- /** Query audit logs (optional - not all stores support querying) */
59
- query?(options: AuditQueryOptions): Promise<AuditEntry[]>;
60
- /** Close/cleanup (optional) */
61
- close?(): Promise<void>;
62
- }
63
- interface AuditQueryOptions {
64
- resource?: string;
65
- documentId?: string;
66
- userId?: string;
67
- organizationId?: string;
68
- action?: AuditAction | AuditAction[];
69
- from?: Date;
70
- to?: Date;
71
- limit?: number;
72
- offset?: number;
73
- }
74
- /**
75
- * Create audit entry from context
76
- */
77
- declare function createAuditEntry(resource: string, documentId: string, action: AuditAction, context: AuditContext, data?: {
78
- before?: Record<string, unknown>;
79
- after?: Record<string, unknown>;
80
- metadata?: Record<string, unknown>;
81
- }): AuditEntry;
82
- //#endregion
83
- //#region src/audit/stores/mongodb.d.ts
84
- interface MongoAuditStoreOptions {
85
- /** MongoDB connection or mongoose instance */
86
- connection: MongoConnection;
87
- /** Collection name (default: 'audit_logs') */
88
- collection?: string;
89
- /** TTL in days (default: 90, 0 = no expiry) */
90
- ttlDays?: number;
91
- }
92
- /**
93
- * Minimal MongoDB connection interface — DB-agnostic.
94
- *
95
- * Accepts:
96
- * - Native MongoDB `Db` instance (has `.collection()`)
97
- * - Mongoose `connection.db` (has `.collection()`)
98
- * - Any object with `.collection(name)` method
99
- *
100
- * For Mongoose users: pass `mongoose.connection.db`, not `mongoose.connection`.
101
- */
102
- interface MongoConnection {
103
- collection: (name: string) => MongoCollection;
104
- }
105
- interface MongoCollection {
106
- insertOne: (doc: Record<string, unknown>) => Promise<unknown>;
107
- find: (query: Record<string, unknown>) => MongoCursor;
108
- createIndex: (spec: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
109
- }
110
- interface MongoCursor {
111
- sort: (spec: Record<string, unknown>) => MongoCursor;
112
- skip: (n: number) => MongoCursor;
113
- limit: (n: number) => MongoCursor;
114
- toArray: () => Promise<Record<string, unknown>[]>;
115
- }
116
- declare class MongoAuditStore implements AuditStore {
117
- readonly name = "mongodb";
118
- private collection;
119
- private initialized;
120
- private ttlDays;
121
- constructor(options: MongoAuditStoreOptions);
122
- private ensureIndexes;
123
- log(entry: AuditEntry): Promise<void>;
124
- query(options?: AuditQueryOptions): Promise<AuditEntry[]>;
125
- }
126
- //#endregion
127
- export { AuditContext as a, AuditStore as c, AuditAction as i, AuditStoreOptions as l, MongoAuditStoreOptions as n, AuditEntry as o, MongoConnection as r, AuditQueryOptions as s, MongoAuditStore as t, createAuditEntry as u };
@@ -1,80 +0,0 @@
1
- import { n as IdempotencyResult, r as IdempotencyStore } from "./interface-DfLGcus7.mjs";
2
-
3
- //#region src/idempotency/stores/mongodb.d.ts
4
- interface MongoConnection {
5
- db: {
6
- collection(name: string): MongoCollection;
7
- };
8
- }
9
- interface MongoCollection {
10
- findOne(filter: object): Promise<IdempotencyDocument | null>;
11
- insertOne(doc: object): Promise<{
12
- acknowledged: boolean;
13
- }>;
14
- updateOne(filter: object, update: object, options?: object): Promise<{
15
- acknowledged: boolean;
16
- matchedCount: number;
17
- modifiedCount: number;
18
- upsertedCount?: number;
19
- }>;
20
- deleteOne(filter: object): Promise<{
21
- deletedCount: number;
22
- }>;
23
- deleteMany(filter: object): Promise<{
24
- deletedCount: number;
25
- }>;
26
- createIndex(spec: object, options?: object): Promise<string>;
27
- }
28
- interface IdempotencyDocument {
29
- _id: string;
30
- result?: {
31
- statusCode: number;
32
- headers: Record<string, string>;
33
- body: unknown;
34
- };
35
- lock?: {
36
- requestId: string;
37
- expiresAt: Date;
38
- };
39
- createdAt: Date;
40
- expiresAt: Date;
41
- }
42
- /** Minimal logger interface — compatible with console, pino, fastify.log */
43
- interface IdempotencyLogger {
44
- warn(message: string, ...args: unknown[]): void;
45
- }
46
- interface MongoIdempotencyStoreOptions {
47
- /** Mongoose connection or MongoDB connection object */
48
- connection: MongoConnection;
49
- /** Collection name (default: 'arc_idempotency') */
50
- collection?: string;
51
- /** Create TTL index on startup (default: true) */
52
- createIndex?: boolean;
53
- /** Default TTL in ms (default: 86400000 = 24 hours) */
54
- ttlMs?: number;
55
- /** Logger for operational warnings (default: console) */
56
- logger?: IdempotencyLogger;
57
- }
58
- declare class MongoIdempotencyStore implements IdempotencyStore {
59
- readonly name = "mongodb";
60
- private connection;
61
- private collectionName;
62
- private ttlMs;
63
- private indexCreated;
64
- private shouldEnsureIndex;
65
- private logger;
66
- constructor(options: MongoIdempotencyStoreOptions);
67
- private get collection();
68
- private ensureIndex;
69
- get(key: string): Promise<IdempotencyResult | undefined>;
70
- set(key: string, result: Omit<IdempotencyResult, "key">): Promise<void>;
71
- tryLock(key: string, requestId: string, ttlMs: number): Promise<boolean>;
72
- unlock(key: string, requestId: string): Promise<void>;
73
- isLocked(key: string): Promise<boolean>;
74
- delete(key: string): Promise<void>;
75
- deleteByPrefix(prefix: string): Promise<number>;
76
- findByPrefix(prefix: string): Promise<IdempotencyResult | undefined>;
77
- close(): Promise<void>;
78
- }
79
- //#endregion
80
- export { MongoIdempotencyStoreOptions as n, MongoIdempotencyStore as t };