@classytic/arc 2.10.3 → 2.11.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 (153) hide show
  1. package/README.md +1 -1
  2. package/dist/{BaseController-CbKKIflT.mjs → BaseController-JNV08qOT.mjs} +595 -537
  3. package/dist/{queryCachePlugin-BKbWjgDG.d.mts → QueryCache-DOBNHBE0.d.mts} +2 -32
  4. package/dist/actionPermissions-C8YYU92K.mjs +22 -0
  5. package/dist/adapters/index.d.mts +2 -2
  6. package/dist/adapters/index.mjs +1 -1
  7. package/dist/{adapters-BXY4i-hw.mjs → adapters-D0tT2Tyo.mjs} +54 -0
  8. package/dist/audit/index.d.mts +2 -2
  9. package/dist/audit/index.mjs +15 -17
  10. package/dist/auth/index.d.mts +4 -4
  11. package/dist/auth/index.mjs +3 -3
  12. package/dist/auth/redis-session.d.mts +1 -1
  13. package/dist/{betterAuthOpenApi-BBRVhjQN.mjs → betterAuthOpenApi-DwxtK3uG.mjs} +1 -1
  14. package/dist/cache/index.d.mts +3 -2
  15. package/dist/cache/index.mjs +3 -3
  16. package/dist/cli/commands/docs.mjs +2 -2
  17. package/dist/cli/commands/generate.mjs +37 -27
  18. package/dist/cli/commands/init.mjs +47 -34
  19. package/dist/cli/commands/introspect.mjs +1 -1
  20. package/dist/context/index.d.mts +58 -0
  21. package/dist/context/index.mjs +2 -0
  22. package/dist/core/index.d.mts +3 -3
  23. package/dist/core/index.mjs +4 -3
  24. package/dist/core-DXdSSFW-.mjs +1037 -0
  25. package/dist/createActionRouter-BwaSM0No.mjs +166 -0
  26. package/dist/{createApp-BuvPma24.mjs → createApp-DvNYEhpb.mjs} +118 -36
  27. package/dist/docs/index.d.mts +2 -2
  28. package/dist/docs/index.mjs +1 -1
  29. package/dist/{elevation-C7hgL_aI.mjs → elevation-DOFoxoDs.mjs} +1 -1
  30. package/dist/errorHandler-Co3lnVmJ.d.mts +114 -0
  31. package/dist/{eventPlugin-DCUjuiQT.mjs → eventPlugin--5HIkdPU.mjs} +1 -1
  32. package/dist/{eventPlugin-CxWgpd6K.d.mts → eventPlugin-CUNjYYRY.d.mts} +1 -1
  33. package/dist/events/index.d.mts +4 -4
  34. package/dist/events/index.mjs +69 -51
  35. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  36. package/dist/events/transports/redis.d.mts +1 -1
  37. package/dist/factory/index.d.mts +1 -1
  38. package/dist/factory/index.mjs +2 -2
  39. package/dist/{fields-Lo1VUDpt.d.mts → fields-C8Y0XLAu.d.mts} +1 -1
  40. package/dist/hooks/index.d.mts +1 -1
  41. package/dist/hooks/index.mjs +1 -1
  42. package/dist/idempotency/index.d.mts +3 -3
  43. package/dist/idempotency/index.mjs +38 -27
  44. package/dist/idempotency/redis.d.mts +1 -1
  45. package/dist/{index-ChIw3776.d.mts → index-BYCqHCVu.d.mts} +4 -4
  46. package/dist/{index-Cl0uoKd5.d.mts → index-Cm0vUrr_.d.mts} +2100 -1688
  47. package/dist/{index-DStwgFUK.d.mts → index-DAushRTt.d.mts} +29 -10
  48. package/dist/index-DsJ1MNfC.d.mts +1179 -0
  49. package/dist/{index-8qw4y6ff.d.mts → index-t8pLpPFW.d.mts} +13 -10
  50. package/dist/index.d.mts +7 -251
  51. package/dist/index.mjs +8 -128
  52. package/dist/integrations/event-gateway.d.mts +2 -2
  53. package/dist/integrations/event-gateway.mjs +1 -1
  54. package/dist/integrations/index.d.mts +2 -2
  55. package/dist/integrations/mcp/index.d.mts +2 -2
  56. package/dist/integrations/mcp/index.mjs +1 -1
  57. package/dist/integrations/mcp/testing.d.mts +1 -1
  58. package/dist/integrations/mcp/testing.mjs +1 -1
  59. package/dist/integrations/streamline.d.mts +46 -5
  60. package/dist/integrations/streamline.mjs +50 -21
  61. package/dist/integrations/websocket-redis.d.mts +1 -1
  62. package/dist/integrations/websocket.d.mts +2 -154
  63. package/dist/integrations/websocket.mjs +292 -224
  64. package/dist/{keys-qcD-TVJl.mjs → keys-CARyUjiR.mjs} +2 -0
  65. package/dist/{loadResources-BAzJItAJ.mjs → loadResources-YNwKHvRA.mjs} +3 -1
  66. package/dist/logger/index.d.mts +81 -0
  67. package/dist/{logger-DLg8-Ueg.mjs → logger/index.mjs} +1 -6
  68. package/dist/middleware/index.d.mts +109 -0
  69. package/dist/middleware/index.mjs +70 -0
  70. package/dist/multipartBody-CvTR1Un6.mjs +123 -0
  71. package/dist/{openapi-B5F8AddX.mjs → openapi-C0L9ar7m.mjs} +9 -7
  72. package/dist/org/index.d.mts +2 -2
  73. package/dist/permissions/index.d.mts +2 -2
  74. package/dist/permissions/index.mjs +1 -3
  75. package/dist/{permissions-Dk6mshja.mjs → permissions-B4vU9L0Q.mjs} +220 -2
  76. package/dist/pipe-DVoIheVC.mjs +62 -0
  77. package/dist/pipeline/index.d.mts +62 -0
  78. package/dist/pipeline/index.mjs +53 -0
  79. package/dist/plugins/index.d.mts +25 -5
  80. package/dist/plugins/index.mjs +10 -10
  81. package/dist/plugins/response-cache.mjs +1 -1
  82. package/dist/plugins/tracing-entry.d.mts +1 -1
  83. package/dist/plugins/tracing-entry.mjs +42 -24
  84. package/dist/presets/filesUpload.d.mts +4 -4
  85. package/dist/presets/filesUpload.mjs +255 -1
  86. package/dist/presets/index.d.mts +1 -1
  87. package/dist/presets/index.mjs +2 -2
  88. package/dist/presets/multiTenant.d.mts +1 -1
  89. package/dist/presets/multiTenant.mjs +48 -8
  90. package/dist/presets/search.d.mts +2 -2
  91. package/dist/presets/search.mjs +1 -1
  92. package/dist/{presets-fLJVXdVn.mjs → presets-k604Lj99.mjs} +1 -1
  93. package/dist/queryCachePlugin-BUXBSm4F.d.mts +34 -0
  94. package/dist/{queryCachePlugin-DQCEfJis.mjs → queryCachePlugin-Bq6bO6vc.mjs} +3 -3
  95. package/dist/{redis-DqyeggCa.d.mts → redis-Cm1gnRDf.d.mts} +1 -1
  96. package/dist/{redis-stream-CakIQmwR.d.mts → redis-stream-CM8TXTix.d.mts} +1 -1
  97. package/dist/registry/index.d.mts +1 -1
  98. package/dist/registry/index.mjs +2 -2
  99. package/dist/{requestContext-xHIKedG6.mjs → requestContext-CfRkaxwf.mjs} +1 -1
  100. package/dist/{resourceToTools-BElv3xPT.mjs → resourceToTools--okX6QBr.mjs} +534 -415
  101. package/dist/routerShared-DeESFp4a.mjs +515 -0
  102. package/dist/schemaIR-BlG9bY7v.mjs +137 -0
  103. package/dist/scope/index.d.mts +2 -2
  104. package/dist/scope/index.mjs +1 -1
  105. package/dist/{sse-yBCgOLGu.mjs → sse-V7aXc3bW.mjs} +1 -1
  106. package/dist/{store-helpers-ZCSMJJAX.mjs → store-helpers-BhrzxvyQ.mjs} +4 -0
  107. package/dist/testing/index.d.mts +367 -711
  108. package/dist/testing/index.mjs +646 -1434
  109. package/dist/testing/storageContract.d.mts +1 -1
  110. package/dist/{tracing-65B51Dw3.d.mts → tracing-DokiEsuz.d.mts} +9 -4
  111. package/dist/types/index.d.mts +5 -5
  112. package/dist/types/index.mjs +1 -3
  113. package/dist/types/storage.d.mts +1 -1
  114. package/dist/{types-Co8k3NyS.d.mts → types-CgikqKAj.d.mts} +133 -21
  115. package/dist/{types-Btdda02s.d.mts → types-D9NqiYIw.d.mts} +1 -1
  116. package/dist/utils/index.d.mts +2 -898
  117. package/dist/utils/index.mjs +4 -5
  118. package/dist/utils-D3Yxnrwr.mjs +1639 -0
  119. package/dist/versioning-M9lNLhO8.d.mts +117 -0
  120. package/dist/websocket-CyJ1VIFI.d.mts +186 -0
  121. package/package.json +26 -8
  122. package/skills/arc/SKILL.md +124 -39
  123. package/skills/arc/references/testing.md +212 -183
  124. package/dist/applyPermissionResult-QhV1Pa-g.mjs +0 -37
  125. package/dist/core-CcR01lup.mjs +0 -1411
  126. package/dist/createActionRouter-Bp_5c_2b.mjs +0 -249
  127. package/dist/errorHandler-DRQ3EqfL.d.mts +0 -218
  128. package/dist/errors-CCSsMpXE.d.mts +0 -140
  129. package/dist/fields-bxkeltzz.mjs +0 -126
  130. package/dist/filesUpload-t21LS-py.mjs +0 -377
  131. package/dist/queryParser-DBqBB6AC.mjs +0 -352
  132. package/dist/types-Csi3FLfq.mjs +0 -27
  133. package/dist/utils-B2fNOD_i.mjs +0 -929
  134. /package/dist/{EventTransport-CUw5NNWe.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
  135. /package/dist/{HookSystem-BNYKnrXF.mjs → HookSystem-CGsMd6oK.mjs} +0 -0
  136. /package/dist/{ResourceRegistry-BPd6NQDm.mjs → ResourceRegistry-DkAeAuTX.mjs} +0 -0
  137. /package/dist/{caching-CBpK_SCM.mjs → caching-CheW3m-S.mjs} +0 -0
  138. /package/dist/{elevation-C5SwtkAn.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
  139. /package/dist/{errorHandler-Bb49BvPD.mjs → errorHandler-BQm8ZxTK.mjs} +0 -0
  140. /package/dist/{externalPaths-BQ8QijNH.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
  141. /package/dist/{interface-CSbZdv_3.d.mts → interface-CkkWm5uR.d.mts} +0 -0
  142. /package/dist/{interface-D218ikEo.d.mts → interface-Da0r7Lna.d.mts} +0 -0
  143. /package/dist/{memory-B5Amv9A1.mjs → memory-DikHSvWa.mjs} +0 -0
  144. /package/dist/{metrics-DuhiSEZI.mjs → metrics-Csh4nsvv.mjs} +0 -0
  145. /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-BneOJkpi.mjs} +0 -0
  146. /package/dist/{registry-B3lRFBWo.mjs → registry-D63ee7fl.mjs} +0 -0
  147. /package/dist/{replyHelpers-CXtJDAZ0.mjs → replyHelpers-ByllIXXV.mjs} +0 -0
  148. /package/dist/{schemaConverter-BxFDdtXu.mjs → schemaConverter-B0oKLuqI.mjs} +0 -0
  149. /package/dist/{sessionManager-BkzVU8h2.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
  150. /package/dist/{storage-CVk_SEn2.d.mts → storage-BwGQXUpd.d.mts} +0 -0
  151. /package/dist/{typeGuards-Cj5Rgvlg.mjs → typeGuards-CcFZXgU7.mjs} +0 -0
  152. /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
  153. /package/dist/{versioning-C2U_bLY0.mjs → versioning-CGPjkqAg.mjs} +0 -0
@@ -1,5 +1,7 @@
1
- import { a as MemoryEventTransport, i as withRetry, o as createChildEvent, r as createDeadLetterPublisher, s as createEvent, t as eventPlugin } from "../eventPlugin-DCUjuiQT.mjs";
2
- import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-ZCSMJJAX.mjs";
1
+ import { a as MemoryEventTransport, i as withRetry, o as createChildEvent, r as createDeadLetterPublisher, s as createEvent, t as eventPlugin } from "../eventPlugin--5HIkdPU.mjs";
2
+ import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-BhrzxvyQ.mjs";
3
+ import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
4
+ import { update } from "@classytic/repo-core/update";
3
5
  //#region src/events/defineEvent.ts
4
6
  /**
5
7
  * defineEvent — Typed Event Definitions with Optional Schema Validation
@@ -339,6 +341,30 @@ var MemoryOutboxStore = class {
339
341
  };
340
342
  //#endregion
341
343
  //#region src/events/repository-outbox-adapter.ts
344
+ /**
345
+ * RepositoryLike → OutboxStore adapter.
346
+ *
347
+ * Maps the `OutboxStore` vocabulary (save / claimPending / acknowledge /
348
+ * fail / getDeadLettered / purge) onto arc's own `RepositoryLike` primitives
349
+ * (create / getOne / findAll / deleteMany / findOneAndUpdate). `EventOutbox`
350
+ * wraps a passed repository with this helper when you use the
351
+ * `{ repository }` option; the function is also re-exported from
352
+ * `@classytic/arc/events` so consumers can build and decorate the store
353
+ * manually (metrics, tracing, multi-transport fan-out).
354
+ *
355
+ * Portability: filters compose via `@classytic/repo-core/filter` and
356
+ * updates via `@classytic/repo-core/update`. The primary-key column name
357
+ * is read from `repository.idField` — mongokit defaults to `_id`,
358
+ * sqlitekit / pgkit / prismakit to the schema's declared PK. The adapter
359
+ * therefore runs on any kit that implements `StandardRepo.findOneAndUpdate`
360
+ * + `getOne` + `getAll` + `deleteMany` + `create`.
361
+ *
362
+ * `fail()` uses a lease-gated read-then-write pair to preserve
363
+ * `firstFailedAt` across retries without relying on Mongo's aggregation-
364
+ * pipeline `$ifNull`. Leases guarantee single-writer during the failure
365
+ * window (`claimPending` filters out non-owned rows), so the two calls are
366
+ * safe under concurrent relayers.
367
+ */
342
368
  const DEFAULT_LEASE_MS$1 = 3e4;
343
369
  const DEFAULT_CLAIM_LIMIT = 100;
344
370
  const DEFAULT_PURGE_BATCH = 500;
@@ -351,6 +377,7 @@ function repositoryAsOutboxStore(repository) {
351
377
  if (typeof repository.findOneAndUpdate !== "function") missing.push("findOneAndUpdate");
352
378
  if (missing.length > 0) throw new Error(`EventOutbox: repository is missing required methods: ${missing.join(", ")}. mongokit ≥3.10.2 satisfies all five; other kits must implement them to back the outbox.`);
353
379
  const r = repository;
380
+ const idField = repository.idField ?? "_id";
354
381
  /**
355
382
  * Unwrap mongokit's pagination envelope ({ docs, total, ... }) — some
356
383
  * kits may return a bare array when pagination is disabled. Handle both.
@@ -362,13 +389,20 @@ function repositoryAsOutboxStore(repository) {
362
389
  const isDuplicateKeyError = createIsDuplicateKeyError(repository);
363
390
  const safeGetOne = createSafeGetOne(repository);
364
391
  const isWellFormed = (event) => !!event && typeof event.type === "string" && !!event.meta?.id;
392
+ /**
393
+ * Filter matching every row that's eligible to be claimed by a relayer:
394
+ * status=pending, visible now, and either unleased or under an expired
395
+ * lease. Used by `getPending` and `claimPending` — defined once so the
396
+ * two code paths stay in lockstep.
397
+ */
398
+ const claimableFilter = (now) => and(eq("status", "pending"), lte("visibleAt", now), or(eq("leaseOwner", null), lte("leaseExpiresAt", now)));
365
399
  return {
366
400
  async save(event, options) {
367
401
  if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
368
402
  if (!event.meta?.id || typeof event.meta.id !== "string") throw new InvalidOutboxEventError("event.meta.id is required");
369
403
  const now = /* @__PURE__ */ new Date();
370
404
  const doc = {
371
- _id: event.meta.id,
405
+ [idField]: event.meta.id,
372
406
  event,
373
407
  type: event.type,
374
408
  status: "pending",
@@ -395,11 +429,7 @@ function repositoryAsOutboxStore(repository) {
395
429
  async getPending(limit) {
396
430
  const now = /* @__PURE__ */ new Date();
397
431
  return unwrapDocs(await r.getAll({
398
- filters: {
399
- status: "pending",
400
- visibleAt: { $lte: now },
401
- $or: [{ leaseOwner: null }, { leaseExpiresAt: { $lte: now } }]
402
- },
432
+ filters: claimableFilter(now),
403
433
  sort: { createdAt: 1 },
404
434
  page: 1,
405
435
  limit
@@ -409,23 +439,19 @@ function repositoryAsOutboxStore(repository) {
409
439
  const limit = options?.limit ?? DEFAULT_CLAIM_LIMIT;
410
440
  const leaseMs = options?.leaseMs ?? DEFAULT_LEASE_MS$1;
411
441
  const consumerId = options?.consumerId ?? "anonymous";
412
- const typeFilter = options?.types?.length ? { type: { $in: options.types } } : {};
442
+ const typeFilter = options?.types?.length ? anyOf("type", options.types) : null;
413
443
  const claimed = [];
414
444
  for (let i = 0; i < limit; i++) {
415
445
  const now = /* @__PURE__ */ new Date();
416
446
  const leaseExpiresAt = new Date(now.getTime() + leaseMs);
417
- const doc = await r.findOneAndUpdate({
418
- status: "pending",
419
- visibleAt: { $lte: now },
420
- $or: [{ leaseOwner: null }, { leaseExpiresAt: { $lte: now } }],
421
- ...typeFilter
422
- }, {
423
- $set: {
447
+ const filter = typeFilter ? and(claimableFilter(now), typeFilter) : claimableFilter(now);
448
+ const doc = await r.findOneAndUpdate(filter, update({
449
+ set: {
424
450
  leaseOwner: consumerId,
425
451
  leaseExpiresAt
426
452
  },
427
- $inc: { attempts: 1 }
428
- }, {
453
+ inc: { attempts: 1 }
454
+ }), {
429
455
  sort: { createdAt: 1 },
430
456
  returnDocument: "after"
431
457
  });
@@ -436,18 +462,15 @@ function repositoryAsOutboxStore(repository) {
436
462
  },
437
463
  async acknowledge(eventId, options) {
438
464
  const now = /* @__PURE__ */ new Date();
439
- const filter = {
440
- _id: eventId,
441
- status: { $ne: "delivered" }
442
- };
443
- if (options?.consumerId) filter.leaseOwner = options.consumerId;
444
- if (await r.findOneAndUpdate(filter, { $set: {
465
+ const baseFilter = and(eq(idField, eventId), ne("status", "delivered"));
466
+ const filter = options?.consumerId ? and(baseFilter, eq("leaseOwner", options.consumerId)) : baseFilter;
467
+ if (await r.findOneAndUpdate(filter, update({ set: {
445
468
  status: "delivered",
446
469
  deliveredAt: now,
447
470
  leaseOwner: null,
448
471
  leaseExpiresAt: null
449
- } }, { returnDocument: "after" })) return;
450
- const current = await safeGetOne({ _id: eventId });
472
+ } }), { returnDocument: "after" })) return;
473
+ const current = await safeGetOne(eq(idField, eventId));
451
474
  if (!current) return;
452
475
  if (current.status === "delivered") return;
453
476
  if (options?.consumerId && current.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, current.leaseOwner);
@@ -456,32 +479,30 @@ function repositoryAsOutboxStore(repository) {
456
479
  const now = /* @__PURE__ */ new Date();
457
480
  const targetStatus = options?.deadLetter ? "dead_letter" : "pending";
458
481
  const visibleAt = options?.retryAt ?? now;
459
- const filter = { _id: eventId };
460
- if (options?.consumerId) filter.leaseOwner = options.consumerId;
461
- const pipeline = [{ $set: {
482
+ const baseFilter = eq(idField, eventId);
483
+ const filter = options?.consumerId ? and(baseFilter, eq("leaseOwner", options.consumerId)) : baseFilter;
484
+ const current = await safeGetOne(baseFilter);
485
+ if (!current) return;
486
+ if (options?.consumerId && current.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, current.leaseOwner);
487
+ const errorInfo = error.code ? {
488
+ message: error.message,
489
+ code: error.code
490
+ } : { message: error.message };
491
+ const firstFailedAt = current.firstFailedAt ?? now;
492
+ await r.findOneAndUpdate(filter, update({ set: {
462
493
  status: targetStatus,
463
494
  visibleAt,
464
495
  leaseOwner: null,
465
496
  leaseExpiresAt: null,
466
497
  lastFailedAt: now,
467
- lastError: {
468
- message: error.message,
469
- ...error.code ? { code: error.code } : {}
470
- },
471
- firstFailedAt: { $ifNull: ["$firstFailedAt", now] }
472
- } }];
473
- if (await r.findOneAndUpdate(filter, pipeline, {
474
- returnDocument: "after",
475
- updatePipeline: true
476
- })) return;
477
- const current = await safeGetOne({ _id: eventId });
478
- if (!current) return;
479
- if (options?.consumerId && current.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, current.leaseOwner);
498
+ lastError: errorInfo,
499
+ firstFailedAt
500
+ } }), { returnDocument: "after" });
480
501
  },
481
502
  async getDeadLettered(limit) {
482
503
  return unwrapDocs(await r.getAll({
483
- filters: { status: "dead_letter" },
484
- sort: { _id: 1 },
504
+ filters: eq("status", "dead_letter"),
505
+ sort: { [idField]: 1 },
485
506
  page: 1,
486
507
  limit
487
508
  })).filter((d) => isWellFormed(d.event)).map((d) => ({
@@ -500,18 +521,15 @@ function repositoryAsOutboxStore(repository) {
500
521
  let totalDeleted = 0;
501
522
  for (;;) {
502
523
  const batch = unwrapDocs(await r.getAll({
503
- filters: {
504
- status: "delivered",
505
- deliveredAt: { $lte: cutoff }
506
- },
524
+ filters: and(eq("status", "delivered"), lte("deliveredAt", cutoff)),
507
525
  sort: { deliveredAt: 1 },
508
526
  page: 1,
509
527
  limit: DEFAULT_PURGE_BATCH,
510
- select: "_id"
528
+ select: idField
511
529
  }));
512
530
  if (batch.length === 0) break;
513
- const ids = batch.map((d) => d._id);
514
- const res = await r.deleteMany({ _id: { $in: ids } });
531
+ const ids = batch.map((d) => d[idField]);
532
+ const res = await r.deleteMany(anyOf(idField, ids));
515
533
  totalDeleted += res.deletedCount ?? 0;
516
534
  if (batch.length < DEFAULT_PURGE_BATCH) break;
517
535
  }
@@ -1,2 +1,2 @@
1
- import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-CakIQmwR.mjs";
1
+ import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-CM8TXTix.mjs";
2
2
  export { type RedisStreamLike, RedisStreamTransport, type RedisStreamTransportOptions };
@@ -1,4 +1,4 @@
1
- import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "../../EventTransport-CUw5NNWe.mjs";
1
+ import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "../../EventTransport-CfVEGaEl.mjs";
2
2
 
3
3
  //#region src/events/transports/redis.d.ts
4
4
  interface RedisLike {
@@ -1,4 +1,4 @@
1
- import { a as CustomPluginAuthOption, c as RawBodyOptions, d as ResourceLike, f as loadResources, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as LoadResourcesOptions } from "../types-Co8k3NyS.mjs";
1
+ import { a as CustomPluginAuthOption, c as RawBodyOptions, d as ResourceLike, f as loadResources, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as LoadResourcesOptions } from "../types-CgikqKAj.mjs";
2
2
  import { FastifyInstance } from "fastify";
3
3
 
4
4
  //#region src/factory/createApp.d.ts
@@ -1,5 +1,5 @@
1
- import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-BuvPma24.mjs";
2
- import { t as loadResources } from "../loadResources-BAzJItAJ.mjs";
1
+ import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-DvNYEhpb.mjs";
2
+ import { t as loadResources } from "../loadResources-YNwKHvRA.mjs";
3
3
  //#region src/factory/edge.ts
4
4
  /**
5
5
  * Convert a Fastify app into a Web Standards fetch handler.
@@ -1,4 +1,4 @@
1
- import { r as RequestScope } from "./types-BD85MlEK.mjs";
1
+ import { r as RequestScope } from "./types-tgR4Pt8F.mjs";
2
2
  import { FastifyRequest } from "fastify";
3
3
 
4
4
  //#region src/permissions/types.d.ts
@@ -1,2 +1,2 @@
1
- import { $t as HookPhase, Qt as HookOperation, Xt as HookContext, Yt as DefineHookOptions, Zt as HookHandler, an as afterUpdate, cn as beforeUpdate, en as HookRegistration, in as afterDelete, ln as createHookSystem, nn as HookSystemOptions, on as beforeCreate, rn as afterCreate, sn as beforeDelete, tn as HookSystem, un as defineHook } from "../index-Cl0uoKd5.mjs";
1
+ import { Cn as beforeUpdate, Sn as beforeDelete, Tn as defineHook, _n as HookSystemOptions, bn as afterUpdate, dn as HookContext, fn as HookHandler, gn as HookSystem, hn as HookRegistration, mn as HookPhase, pn as HookOperation, un as DefineHookOptions, vn as afterCreate, wn as createHookSystem, xn as beforeCreate, yn as afterDelete } from "../index-Cm0vUrr_.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-BNYKnrXF.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-CGsMd6oK.mjs";
2
2
  export { HookSystem, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,6 +1,6 @@
1
- import { kt as RepositoryLike } from "../index-Cl0uoKd5.mjs";
2
- import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-CSbZdv_3.mjs";
3
- import { i as RedisIdempotencyStoreOptions, n as RedisClient } from "../redis-DqyeggCa.mjs";
1
+ import { jn as RepositoryLike } from "../index-Cm0vUrr_.mjs";
2
+ import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-CkkWm5uR.mjs";
3
+ import { i as RedisIdempotencyStoreOptions, n as RedisClient } from "../redis-Cm1gnRDf.mjs";
4
4
  import { FastifyPluginAsync } from "fastify";
5
5
 
6
6
  //#region src/idempotency/idempotencyPlugin.d.ts
@@ -1,7 +1,28 @@
1
- import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-ZCSMJJAX.mjs";
1
+ import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-BhrzxvyQ.mjs";
2
2
  import { createHash } from "node:crypto";
3
3
  import fp from "fastify-plugin";
4
+ import { and, eq, exists, gt, lt, or, startsWith } from "@classytic/repo-core/filter";
5
+ import { update } from "@classytic/repo-core/update";
4
6
  //#region src/idempotency/repository-idempotency-adapter.ts
7
+ /**
8
+ * RepositoryLike → IdempotencyStore adapter.
9
+ *
10
+ * Maps the idempotency store's verbs (get / set / tryLock / unlock / delete /
11
+ * deleteByPrefix / findByPrefix) onto arc's canonical repository primitives
12
+ * (`getOne` / `deleteMany` / `findOneAndUpdate`). `idempotencyPlugin` wraps
13
+ * a passed repository with this helper when you use the `{ repository }`
14
+ * option; the function is also re-exported from `@classytic/arc/idempotency`
15
+ * so consumers can build and decorate the store (metrics, tracing, key
16
+ * namespacing) before passing it via `store:`.
17
+ *
18
+ * Portability: filters compose via `@classytic/repo-core/filter` builders
19
+ * (`and` / `or` / `eq` / `gt` / `lt` / `exists` / `startsWith`) and updates
20
+ * via `@classytic/repo-core/update` (`update({ set, unset, setOnInsert })`).
21
+ * Both IRs compile to Mongo operators on mongokit, SQL predicates on
22
+ * sqlitekit / pgkit, and `WhereInput` / `update` on prismakit. The store
23
+ * therefore runs identically on every backend that implements the
24
+ * `StandardRepo.findOneAndUpdate` + `getOne` + `deleteMany` surface.
25
+ */
5
26
  function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
6
27
  const missing = [];
7
28
  if (typeof repository.getOne !== "function") missing.push("getOne");
@@ -9,13 +30,13 @@ function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
9
30
  if (typeof repository.findOneAndUpdate !== "function") missing.push("findOneAndUpdate");
10
31
  if (missing.length > 0) throw new Error(`idempotencyPlugin: repository is missing required methods: ${missing.join(", ")}. mongokit ≥3.8 satisfies these; other kits must implement them to back idempotency via a repository.`);
11
32
  const r = repository;
33
+ const idField = repository.idField ?? "_id";
12
34
  const isDuplicateKeyError = createIsDuplicateKeyError(repository);
13
35
  const safeGetOne = createSafeGetOne(repository);
14
- const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15
36
  return {
16
37
  name: "repository",
17
38
  async get(key) {
18
- const doc = await safeGetOne({ _id: key });
39
+ const doc = await safeGetOne(eq(idField, key));
19
40
  if (!doc?.result) return void 0;
20
41
  if (new Date(doc.expiresAt) < /* @__PURE__ */ new Date()) return void 0;
21
42
  return {
@@ -28,8 +49,8 @@ function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
28
49
  };
29
50
  },
30
51
  async set(key, result) {
31
- await r.findOneAndUpdate({ _id: key }, {
32
- $set: {
52
+ await r.findOneAndUpdate(eq(idField, key), update({
53
+ set: {
33
54
  result: {
34
55
  statusCode: result.statusCode,
35
56
  headers: result.headers,
@@ -38,8 +59,8 @@ function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
38
59
  createdAt: result.createdAt,
39
60
  expiresAt: result.expiresAt
40
61
  },
41
- $unset: { lock: "" }
42
- }, {
62
+ unset: ["lock"]
63
+ }), {
43
64
  upsert: true,
44
65
  returnDocument: "after"
45
66
  });
@@ -49,19 +70,16 @@ function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
49
70
  const lockExpiresAt = new Date(now.getTime() + ttlMs);
50
71
  const docExpiresAt = new Date(now.getTime() + defaultTtlMs);
51
72
  try {
52
- const doc = await r.findOneAndUpdate({
53
- _id: key,
54
- $or: [{ lock: { $exists: false } }, { "lock.expiresAt": { $lt: now } }]
55
- }, {
56
- $set: { lock: {
73
+ const doc = await r.findOneAndUpdate(and(eq(idField, key), or(exists("lock", false), lt("lock.expiresAt", now))), update({
74
+ set: { lock: {
57
75
  requestId,
58
76
  expiresAt: lockExpiresAt
59
77
  } },
60
- $setOnInsert: {
78
+ setOnInsert: {
61
79
  createdAt: now,
62
80
  expiresAt: docExpiresAt
63
81
  }
64
- }, {
82
+ }), {
65
83
  upsert: true,
66
84
  returnDocument: "after"
67
85
  });
@@ -72,31 +90,24 @@ function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
72
90
  }
73
91
  },
74
92
  async unlock(key, requestId) {
75
- await r.findOneAndUpdate({
76
- _id: key,
77
- "lock.requestId": requestId
78
- }, { $unset: { lock: "" } });
93
+ await r.findOneAndUpdate(and(eq(idField, key), eq("lock.requestId", requestId)), update({ unset: ["lock"] }));
79
94
  },
80
95
  async isLocked(key) {
81
- const doc = await safeGetOne({ _id: key });
96
+ const doc = await safeGetOne(eq(idField, key));
82
97
  if (!doc?.lock) return false;
83
98
  return new Date(doc.lock.expiresAt) > /* @__PURE__ */ new Date();
84
99
  },
85
100
  async delete(key) {
86
- await r.deleteMany({ _id: key });
101
+ await r.deleteMany(eq(idField, key));
87
102
  },
88
103
  async deleteByPrefix(prefix) {
89
- return (await r.deleteMany({ _id: { $regex: `^${escapeRegex(prefix)}` } })).deletedCount ?? 0;
104
+ return (await r.deleteMany(startsWith(idField, prefix, "sensitive"))).deletedCount ?? 0;
90
105
  },
91
106
  async findByPrefix(prefix) {
92
- const doc = await safeGetOne({
93
- _id: { $regex: `^${escapeRegex(prefix)}` },
94
- result: { $exists: true },
95
- expiresAt: { $gt: /* @__PURE__ */ new Date() }
96
- });
107
+ const doc = await safeGetOne(and(startsWith(idField, prefix, "sensitive"), exists("result", true), gt("expiresAt", /* @__PURE__ */ new Date())));
97
108
  if (!doc?.result) return void 0;
98
109
  return {
99
- key: doc._id,
110
+ key: String(doc[idField] ?? prefix),
100
111
  statusCode: doc.result.statusCode,
101
112
  headers: doc.result.headers,
102
113
  body: doc.result.body,
@@ -1,2 +1,2 @@
1
- import { a as UpstashRedisLike, i as RedisIdempotencyStoreOptions, n as RedisClient, o as ioredisAsIdempotencyClient, r as RedisIdempotencyStore, s as upstashAsIdempotencyClient, t as IoredisLike } from "../redis-DqyeggCa.mjs";
1
+ import { a as UpstashRedisLike, i as RedisIdempotencyStoreOptions, n as RedisClient, o as ioredisAsIdempotencyClient, r as RedisIdempotencyStore, s as upstashAsIdempotencyClient, t as IoredisLike } from "../redis-Cm1gnRDf.mjs";
2
2
  export { type IoredisLike, type RedisClient, RedisIdempotencyStore, type RedisIdempotencyStoreOptions, type UpstashRedisLike, ioredisAsIdempotencyClient, upstashAsIdempotencyClient };
@@ -1,7 +1,7 @@
1
- import { r as RequestScope } from "./types-BD85MlEK.mjs";
2
- import { c as PermissionCheck, l as PermissionContext, u as PermissionResult } from "./fields-Lo1VUDpt.mjs";
3
- import { r as CacheStore, t as CacheLogger } from "./interface-D218ikEo.mjs";
4
- import { FastifyRequest } from "fastify";
1
+ import { r as CacheStore, t as CacheLogger } from "./interface-Da0r7Lna.mjs";
2
+ import { r as RequestScope } from "./types-tgR4Pt8F.mjs";
3
+ import { c as PermissionCheck, l as PermissionContext, u as PermissionResult } from "./fields-C8Y0XLAu.mjs";
4
+ import { FastifyReply, FastifyRequest } from "fastify";
5
5
 
6
6
  //#region src/permissions/applyPermissionResult.d.ts
7
7
  /**