@classytic/arc 2.23.0 → 2.28.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.
- package/README.md +2 -0
- package/dist/{BaseController-BQ8KNvWC.mjs → BaseController-B65zbz9m.mjs} +94 -23
- package/dist/QueryCache-DOMT_iq7.d.mts +72 -0
- package/dist/actionPermissions-CAphcmUG.mjs +24 -0
- package/dist/{applyPermissionResult-Tw4iXtlf.mjs → applyPermissionResult-BnyVE9eD.mjs} +10 -0
- package/dist/audit/index.d.mts +32 -3
- package/dist/audit/index.mjs +7 -4
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +263 -259
- package/dist/{buildHandler-D_2ULWxZ.mjs → buildHandler-Dtwd8fdz.mjs} +1 -1
- package/dist/cache/index.d.mts +41 -3
- package/dist/cache/index.mjs +33 -4
- package/dist/cleanup/index.d.mts +313 -0
- package/dist/cleanup/index.mjs +1267 -0
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/index.mjs +2 -2
- package/dist/context/index.mjs +2 -64
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -4
- package/dist/core-BGa8TXJR.mjs +221 -0
- package/dist/{createActionRouter-BuOv-m4Y.mjs → createActionRouter-C-N7z2EL.mjs} +3 -2
- package/dist/{createAggregationRouter-CgJv_BU_.mjs → createAggregationRouter-wzwFa_dW.mjs} +3 -3
- package/dist/{createApp-CjOeWJ9_.mjs → createApp-Du5G54TM.mjs} +187 -96
- package/dist/{core-DQ6rDTyH.mjs → defineResource-2zvVVpxg.mjs} +297 -230
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-COpYKt2h.mjs → elevation-CIz1GjGm.mjs} +4 -3
- package/dist/{eventPlugin-A6EbthXR.d.mts → eventPlugin-Bg3cP2_u.d.mts} +38 -3
- package/dist/{eventPlugin-DJG2D7sy.mjs → eventPlugin-DrQ_FesK.mjs} +22 -4
- package/dist/events/index.d.mts +83 -314
- package/dist/events/index.mjs +183 -95
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +24 -2
- package/dist/factory/index.d.mts +5 -5
- package/dist/factory/index.mjs +30 -8
- package/dist/{fastify-augmentation-g_f9omSc.d.mts → fastify-augmentation-OzYGIiU1.d.mts} +26 -2
- package/dist/{generate-usRSdQmP.mjs → generate-D0wZupBg.mjs} +1 -1
- package/dist/headers-BvlQSDMB.mjs +45 -0
- package/dist/health-CB_m4S32.mjs +377 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.mjs +27 -3
- package/dist/{index-wMoWBLkn.d.mts → index-BMnbzvtO.d.mts} +3 -3
- package/dist/{integrations/jobs.d.mts → index-CG4yqMDB.d.mts} +82 -2
- package/dist/index-CZY4EGga.d.mts +107 -0
- package/dist/{index-CC5ruDGh.d.mts → index-CjVg-Epg.d.mts} +37 -5
- package/dist/{index-BBsvQt7U.d.mts → index-DPRH0Cme.d.mts} +36 -106
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +7 -6
- package/dist/{init-CjO4p_q1.mjs → init-tlRSt667.mjs} +206 -208
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/jobs/index.d.mts +2 -0
- package/dist/integrations/{jobs.mjs → jobs/index.mjs} +110 -67
- package/dist/integrations/mcp/index.d.mts +4 -4
- package/dist/integrations/mcp/index.mjs +2 -2
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +37 -30
- package/dist/integrations/webhooks.mjs +51 -62
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +41 -14
- package/dist/{interface-C1_WLTMK.d.mts → interface-BGZCzOGL.d.mts} +24 -31
- package/dist/{mcpPlugin-BtzIL_kM.mjs → mcpPlugin-Cs6nRGCt.mjs} +12 -10
- package/dist/{memory-CLXA2VeJ.mjs → memory-CmnzXE5Z.mjs} +29 -0
- package/dist/middleware/index.d.mts +17 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/migrations/index.d.mts +94 -44
- package/dist/migrations/index.mjs +196 -44
- package/dist/module-_-M7C5ES.mjs +29 -0
- package/dist/{multipartBody-CHEQSZ6G.mjs → multipartBody-D0eBvkeq.mjs} +17 -0
- package/dist/{openapi-jSDx-VDv.mjs → openapi-BLV4OMXb.mjs} +3 -3
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-tbxFHxjZ.mjs → permissions-BrtiBKGs.mjs} +229 -229
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/{actionPermissions-lszoJle1.mjs → pipeline-BGRnV-To.mjs} +4 -27
- package/dist/plugins/index.d.mts +6 -77
- package/dist/plugins/index.mjs +11 -95
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{index-Ba10fdKp.d.mts → preloadResources-CM4uUaFv.d.mts} +51 -50
- package/dist/preloadResources-DSY5HGpS.mjs +45 -0
- package/dist/presets/filesUpload.d.mts +3 -3
- package/dist/presets/filesUpload.mjs +3 -3
- package/dist/presets/index.d.mts +2 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-BOe4Ugkl.mjs → presets-DwFDVKP0.mjs} +2 -2
- package/dist/{queryCachePlugin-DzqLN904.mjs → queryCachePlugin-DSGuSVLW.mjs} +39 -8
- package/dist/{queryCachePlugin-CfYWq-Ak.d.mts → queryCachePlugin-br4otiqF.d.mts} +2 -2
- package/dist/{registerRoute-CWO6mdYe.mjs → rateLimit-DrED-C4v.mjs} +2 -109
- package/dist/{realtime-I8OrfnMR.mjs → realtime-DEuDu9io.mjs} +3 -3
- package/dist/{redis-stream-iVR2l7aS.d.mts → redis-stream-uIaauNpZ.d.mts} +22 -0
- package/dist/registerRoute-DTp18Ah_.mjs +108 -0
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/{registry-vcOCtPij.mjs → registry-9km_w1aP.mjs} +15 -0
- package/dist/renewingLease-3cuQH7h8.mjs +44 -0
- package/dist/{requestContext-BZKOr13H.mjs → requestContext-MrlJwECf.mjs} +2 -2
- package/dist/schedules-CjOd2MG9.mjs +113 -0
- package/dist/schemas/index.d.mts +67 -67
- package/dist/schemas/index.mjs +2 -3
- package/dist/scim/index.mjs +1 -2
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +4 -4
- package/dist/{scope-LONQ3mfZ.mjs → scope-DfaPntpo.mjs} +4 -5
- package/dist/{sse-0u4P4-OI.mjs → sse-BQ1w0dfl.mjs} +1 -1
- package/dist/testing/cleanupStoreContract.d.mts +10 -0
- package/dist/testing/cleanupStoreContract.mjs +375 -0
- package/dist/testing/index.d.mts +5 -3
- package/dist/testing/index.mjs +4 -2
- package/dist/testing/outboxStoreContract.d.mts +13 -0
- package/dist/testing/outboxStoreContract.mjs +247 -0
- package/dist/transactionContext-PNdoyLLm.mjs +64 -0
- package/dist/types/index.d.mts +4 -4
- package/dist/{types-bHblZ9YN.mjs → types-CSyCxl2V.mjs} +38 -7
- package/dist/types-C_ZZRcD6.d.mts +450 -0
- package/dist/{module-B4u10iOJ.d.mts → types-Cu6vVJmh.d.mts} +196 -60
- package/dist/{types-DH0GupI7.d.mts → types-DjC-KUCz.d.mts} +118 -20
- package/dist/{types-Csxga7d3.d.mts → types-Dmw-6Qeo.d.mts} +96 -4
- package/dist/{types-5OuqeTDX.d.mts → types-QFGc3Cjv.d.mts} +1 -1
- package/dist/usage/index.d.mts +3 -52
- package/dist/usage/index.mjs +0 -0
- package/dist/userHelpers-Cg_y3fBz.mjs +29 -0
- package/dist/utils/index.d.mts +58 -85
- package/dist/utils/index.mjs +4 -2
- package/dist/{utils-CZyeRXTh.mjs → utils-taUVD4p3.mjs} +47 -337
- package/dist/{validate-C2mGgSXg.mjs → validate-Bk44F2yT.mjs} +1 -1
- package/dist/{versioning-D6PvCwpq.d.mts → versioning-DXgY5BUm.d.mts} +11 -29
- package/dist/{versioning-Dk-71iIz.mjs → versioning-gKWyryT3.mjs} +8 -1
- package/dist/{websocket-uuWqM6UY.d.mts → websocket-_Pd0CgR0.d.mts} +15 -1
- package/package.json +541 -513
- package/skills/arc/references/api-reference.md +3 -1
- package/skills/arc/references/integrations.md +20 -11
- package/dist/QueryCache-Z1B38koh.d.mts +0 -40
- package/dist/betterAuthOpenApi-CV1lxpRX.mjs +0 -2
- package/dist/health-ScyjIVo5.mjs +0 -202
- package/dist/requestId-5BhTVzBv.mjs +0 -113
- package/dist/types-XTApa8b6.mjs +0 -24
|
@@ -648,7 +648,15 @@ function safeAsync(p, logger, op, ctx) {
|
|
|
648
648
|
*/
|
|
649
649
|
var SendQueue = class {
|
|
650
650
|
entries = [];
|
|
651
|
-
/**
|
|
651
|
+
/**
|
|
652
|
+
* Logical queue head — `entries[head..]` is the live queue. Draining
|
|
653
|
+
* advances the pointer instead of `shift()`ing (which moves every
|
|
654
|
+
* trailing element AND invalidated every tracked coalesce index,
|
|
655
|
+
* making a full drain O(n²)). The prefix is garbage-collected by
|
|
656
|
+
* {@link compactIfNeeded} — amortized O(1) per drained frame.
|
|
657
|
+
*/
|
|
658
|
+
head = 0;
|
|
659
|
+
/** `coalesceKey → ABSOLUTE entries[] index` for O(1) replacement. */
|
|
652
660
|
coalesceIndex = /* @__PURE__ */ new Map();
|
|
653
661
|
socket;
|
|
654
662
|
capacity;
|
|
@@ -666,7 +674,7 @@ var SendQueue = class {
|
|
|
666
674
|
/** Ordered, guaranteed-best-effort delivery. Overflow ⇒ terminate. */
|
|
667
675
|
send(payload) {
|
|
668
676
|
if (this.disposed) return;
|
|
669
|
-
if (this.
|
|
677
|
+
if (this.size() >= this.capacity) {
|
|
670
678
|
this.terminate("Queue overflow");
|
|
671
679
|
return;
|
|
672
680
|
}
|
|
@@ -685,7 +693,7 @@ var SendQueue = class {
|
|
|
685
693
|
if (this.disposed) return;
|
|
686
694
|
if (coalesceKey !== void 0) {
|
|
687
695
|
const idx = this.coalesceIndex.get(coalesceKey);
|
|
688
|
-
if (idx !== void 0 && this.entries[idx]) {
|
|
696
|
+
if (idx !== void 0 && idx >= this.head && this.entries[idx]) {
|
|
689
697
|
this.entries[idx] = {
|
|
690
698
|
payload,
|
|
691
699
|
cls: "droppable",
|
|
@@ -695,12 +703,16 @@ var SendQueue = class {
|
|
|
695
703
|
return;
|
|
696
704
|
}
|
|
697
705
|
}
|
|
698
|
-
if (this.
|
|
699
|
-
|
|
706
|
+
if (this.size() >= this.capacity) {
|
|
707
|
+
let shedIdx = -1;
|
|
708
|
+
for (let i = this.head; i < this.entries.length; i++) if (this.entries[i]?.cls === "droppable") {
|
|
709
|
+
shedIdx = i;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
700
712
|
if (shedIdx === -1) return;
|
|
701
713
|
const evicted = this.entries.splice(shedIdx, 1)[0];
|
|
702
714
|
if (evicted?.coalesceKey !== void 0) this.coalesceIndex.delete(evicted.coalesceKey);
|
|
703
|
-
this.
|
|
715
|
+
for (const [key, idx] of this.coalesceIndex) if (idx > shedIdx) this.coalesceIndex.set(key, idx - 1);
|
|
704
716
|
}
|
|
705
717
|
this.entries.push({
|
|
706
718
|
payload,
|
|
@@ -718,28 +730,44 @@ var SendQueue = class {
|
|
|
718
730
|
*/
|
|
719
731
|
flush() {
|
|
720
732
|
if (this.disposed) return;
|
|
721
|
-
while (this.
|
|
733
|
+
while (this.size() > 0 && this.socket.readyState === 1) {
|
|
722
734
|
if ((this.socket.bufferedAmount ?? 0) >= this.drainThreshold) {
|
|
723
735
|
this.scheduleRetry();
|
|
724
|
-
|
|
736
|
+
break;
|
|
725
737
|
}
|
|
726
|
-
const entry = this.entries.
|
|
738
|
+
const entry = this.entries[this.head];
|
|
727
739
|
if (!entry) return;
|
|
740
|
+
this.head++;
|
|
728
741
|
if (entry.coalesceKey !== void 0) this.coalesceIndex.delete(entry.coalesceKey);
|
|
729
|
-
if (this.coalesceIndex.size > 0) this.reindexCoalesceFrom(0);
|
|
730
742
|
try {
|
|
731
743
|
this.socket.send(entry.payload);
|
|
732
744
|
} catch {
|
|
733
745
|
return;
|
|
734
746
|
}
|
|
735
747
|
}
|
|
748
|
+
this.compactIfNeeded();
|
|
736
749
|
}
|
|
737
750
|
size() {
|
|
738
|
-
return this.entries.length;
|
|
751
|
+
return this.entries.length - this.head;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Drop the drained prefix once it dominates the array. Rebuilding the
|
|
755
|
+
* (small) coalesce index here is the amortized cost that replaces the
|
|
756
|
+
* per-frame O(n) reindex the old `shift()` drain paid.
|
|
757
|
+
*/
|
|
758
|
+
compactIfNeeded() {
|
|
759
|
+
if (this.head < 64 || this.head * 2 < this.entries.length) return;
|
|
760
|
+
this.entries = this.entries.slice(this.head);
|
|
761
|
+
this.head = 0;
|
|
762
|
+
if (this.coalesceIndex.size > 0) {
|
|
763
|
+
this.coalesceIndex.clear();
|
|
764
|
+
this.reindexCoalesceFrom(0);
|
|
765
|
+
}
|
|
739
766
|
}
|
|
740
767
|
dispose() {
|
|
741
768
|
this.disposed = true;
|
|
742
769
|
this.entries.length = 0;
|
|
770
|
+
this.head = 0;
|
|
743
771
|
this.coalesceIndex.clear();
|
|
744
772
|
if (this.retryTimer) {
|
|
745
773
|
clearTimeout(this.retryTimer);
|
|
@@ -829,7 +857,6 @@ async function handleConnection(ctx, socket, request) {
|
|
|
829
857
|
clientId: serviceClientId,
|
|
830
858
|
authMode: options.auth
|
|
831
859
|
});
|
|
832
|
-
options.messageEnvelope;
|
|
833
860
|
const urlPushRef = readPushRefFromRequest(request);
|
|
834
861
|
let pushRef;
|
|
835
862
|
let envelope;
|
|
@@ -1279,7 +1306,7 @@ var RoomManager = class {
|
|
|
1279
1306
|
for (const clientId of members) {
|
|
1280
1307
|
if (clientId === excludeClientId) continue;
|
|
1281
1308
|
const client = this.clients.get(clientId);
|
|
1282
|
-
if (
|
|
1309
|
+
if (client?.socket.readyState !== 1) continue;
|
|
1283
1310
|
if ((client.socket.bufferedAmount ?? 0) > WS_BACKPRESSURE_LIMIT) continue;
|
|
1284
1311
|
try {
|
|
1285
1312
|
client.socket.send(message);
|
|
@@ -1303,7 +1330,7 @@ var RoomManager = class {
|
|
|
1303
1330
|
if (!members) return;
|
|
1304
1331
|
for (const clientId of members) {
|
|
1305
1332
|
const client = this.clients.get(clientId);
|
|
1306
|
-
if (
|
|
1333
|
+
if (client?.socket.readyState !== 1) continue;
|
|
1307
1334
|
if (client.queue) client.queue.sendRealtime(message, coalesceKey);
|
|
1308
1335
|
else if ((client.socket.bufferedAmount ?? 0) <= WS_BACKPRESSURE_LIMIT) try {
|
|
1309
1336
|
client.socket.send(message);
|
|
@@ -1,34 +1,5 @@
|
|
|
1
|
+
import { CacheAdapter } from "@classytic/repo-core/cache";
|
|
1
2
|
//#region src/cache/interface.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Cache Store Interface — aligned with `@classytic/repo-core/cache.CacheAdapter`.
|
|
4
|
-
*
|
|
5
|
-
* Arc's cache layer speaks the same `get / set(ttlSeconds?) / del / clear(pattern?)`
|
|
6
|
-
* transport-level contract published by `@classytic/repo-core`. One Redis
|
|
7
|
-
* implementation drops into Arc's `QueryCache`, mongokit's cache plugin,
|
|
8
|
-
* sqlitekit's cache plugin, and every future kit without wrapper shims.
|
|
9
|
-
*
|
|
10
|
-
* Arc extends the bare adapter with two optional observability fields —
|
|
11
|
-
* `name` (for diagnostics) and `stats()` (for the response-cache plugin) —
|
|
12
|
-
* that are opt-in: consumers implementing only `CacheAdapter` still
|
|
13
|
-
* structurally satisfy `CacheStore`, so a raw repo-core adapter plugs
|
|
14
|
-
* directly into Arc.
|
|
15
|
-
*
|
|
16
|
-
* ## TTL unit
|
|
17
|
-
*
|
|
18
|
-
* `ttlSeconds`, not milliseconds. Matches Redis (`SET … EX seconds`) which
|
|
19
|
-
* is the dominant backend. `0` or `undefined` means no expiry; implementations
|
|
20
|
-
* may apply their own default.
|
|
21
|
-
*
|
|
22
|
-
* ## Not-found semantics
|
|
23
|
-
*
|
|
24
|
-
* `get()` returns `undefined` on miss / expired. Matches repo-core.
|
|
25
|
-
*
|
|
26
|
-
* ## Sync-or-async
|
|
27
|
-
*
|
|
28
|
-
* Method returns are `Promise<T> | T` — in-memory `Map` adapters can be
|
|
29
|
-
* synchronous; Redis adapters are async. Consumers always `await`, so
|
|
30
|
-
* sync values just short-circuit the microtask.
|
|
31
|
-
*/
|
|
32
3
|
interface CacheLogger {
|
|
33
4
|
warn(message: string, ...args: unknown[]): void;
|
|
34
5
|
error(message: string, ...args: unknown[]): void;
|
|
@@ -45,7 +16,14 @@ interface CacheStats {
|
|
|
45
16
|
/** Number of entries evicted since creation */
|
|
46
17
|
evictions: number;
|
|
47
18
|
}
|
|
48
|
-
|
|
19
|
+
/**
|
|
20
|
+
* `extends CacheAdapter` is load-bearing: repo-core owns the transport
|
|
21
|
+
* contract, and extending (instead of maintaining a structural copy) makes
|
|
22
|
+
* any drift between the two a COMPILE error in arc rather than a runtime
|
|
23
|
+
* incompatibility — e.g. an argument-order divergence on `increment` would
|
|
24
|
+
* make a conforming adapter misread a TTL as the increment amount.
|
|
25
|
+
*/
|
|
26
|
+
interface CacheStore<TValue = unknown> extends CacheAdapter {
|
|
49
27
|
/** Store name for logs/diagnostics. Optional to match repo-core's bare `CacheAdapter`. */
|
|
50
28
|
readonly name?: string;
|
|
51
29
|
/**
|
|
@@ -70,6 +48,21 @@ interface CacheStore<TValue = unknown> {
|
|
|
70
48
|
* need strict invalidation must check for its presence: `store.clear?.(pattern)`.
|
|
71
49
|
*/
|
|
72
50
|
clear?(pattern?: string): Promise<void> | void;
|
|
51
|
+
/**
|
|
52
|
+
* Atomic increment — adds `by` (default 1) to the integer at `key`,
|
|
53
|
+
* creating it with value `by` when absent; returns the NEW value.
|
|
54
|
+
* Signature is the canonical `@classytic/repo-core/cache.CacheAdapter`
|
|
55
|
+
* contract — Redis adapters map to `INCRBY` (+ `EXPIRE` when
|
|
56
|
+
* `ttlSeconds` is given).
|
|
57
|
+
*
|
|
58
|
+
* Optional — consumers feature-detect it. Arc's `QueryCache` uses it for
|
|
59
|
+
* version-based invalidation: with `increment`, concurrent bumps from
|
|
60
|
+
* multiple replicas are strictly monotonic and never collide; without it,
|
|
61
|
+
* QueryCache falls back to a read-modify-write that is monotonic within a
|
|
62
|
+
* process but can lose a concurrent replica's bump. Distributed hosts
|
|
63
|
+
* should use a store that implements this.
|
|
64
|
+
*/
|
|
65
|
+
increment?(key: string, by?: number, ttlSeconds?: number): Promise<number> | number;
|
|
73
66
|
/** Cache statistics for observability. Optional. */
|
|
74
67
|
stats?(): CacheStats;
|
|
75
68
|
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { p as isArcError, t as ArcError } from "./errors-DkO8Nk6-.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { n as requireSingleHeaderValue } from "./headers-BvlQSDMB.mjs";
|
|
3
|
+
import { b as isElevated, c as getOrgId, g as getUserId, x as isMember } from "./types-CSyCxl2V.mjs";
|
|
4
|
+
import { t as BaseController } from "./BaseController-B65zbz9m.mjs";
|
|
5
|
+
import { i as applyFieldReadPermissions, r as normalizePermissionResult, s as resolveEffectiveRoles } from "./applyPermissionResult-BnyVE9eD.mjs";
|
|
5
6
|
import { t as executePipeline } from "./pipe-BOLmGIxa.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import { n as validateAggregations } from "./validate-
|
|
8
|
-
import { n as executeAggregation } from "./buildHandler-
|
|
7
|
+
import { r as resolvePipelineSteps, u as buildRequestScopeProjection } from "./pipeline-BGRnV-To.mjs";
|
|
8
|
+
import { n as validateAggregations } from "./validate-Bk44F2yT.mjs";
|
|
9
|
+
import { n as executeAggregation } from "./buildHandler-Dtwd8fdz.mjs";
|
|
9
10
|
import { t as pluralize } from "./pluralize-BhrgwZb9.mjs";
|
|
11
|
+
import { t as resolveActionPermission } from "./actionPermissions-CAphcmUG.mjs";
|
|
10
12
|
import { t as createIdempotencyResult } from "./interface-n5hpKg1d.mjs";
|
|
11
13
|
import { r as shouldRejectAdditionalProperties, t as normalizeSchemaIR } from "./schemaIR-Ca7ySNPx.mjs";
|
|
12
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
13
14
|
import { isHttpError, toErrorContract } from "@classytic/repo-core/errors";
|
|
15
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
14
16
|
import fp from "fastify-plugin";
|
|
15
17
|
import { z } from "zod";
|
|
16
18
|
//#region src/integrations/mcp/createMcpServer.ts
|
|
@@ -2382,7 +2384,7 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
|
|
|
2382
2384
|
}
|
|
2383
2385
|
});
|
|
2384
2386
|
}
|
|
2385
|
-
const sessionId = request.headers
|
|
2387
|
+
const sessionId = requireSingleHeaderValue(request.headers, "mcp-session-id");
|
|
2386
2388
|
if (sessionId) {
|
|
2387
2389
|
const entry = cache.get(sessionId);
|
|
2388
2390
|
if (entry) {
|
|
@@ -2417,7 +2419,7 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
|
|
|
2417
2419
|
await transport.handleRequest(request.raw, reply.raw, request.body);
|
|
2418
2420
|
});
|
|
2419
2421
|
fastify.get(prefix, async (request, reply) => {
|
|
2420
|
-
const sessionId = request.headers
|
|
2422
|
+
const sessionId = requireSingleHeaderValue(request.headers, "mcp-session-id");
|
|
2421
2423
|
if (!sessionId) return reply.code(400).send({ error: "Missing Mcp-Session-Id header" });
|
|
2422
2424
|
const entry = cache.get(sessionId);
|
|
2423
2425
|
if (!entry) return reply.code(403).send({ error: "Unauthorized" });
|
|
@@ -2432,7 +2434,7 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
|
|
|
2432
2434
|
await entry.transport.handleRequest(request.raw, reply.raw);
|
|
2433
2435
|
});
|
|
2434
2436
|
fastify.delete(prefix, async (request, reply) => {
|
|
2435
|
-
const sessionId = request.headers
|
|
2437
|
+
const sessionId = requireSingleHeaderValue(request.headers, "mcp-session-id");
|
|
2436
2438
|
if (!sessionId) return reply.code(400).send({ error: "Missing Mcp-Session-Id header" });
|
|
2437
2439
|
const entry = cache.get(sessionId);
|
|
2438
2440
|
if (!entry) return reply.code(204).send();
|
|
@@ -76,6 +76,35 @@ var MemoryCacheStore = class {
|
|
|
76
76
|
const entry = this.cache.get(key);
|
|
77
77
|
if (entry) this.removeEntry(key, entry);
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Atomic increment (canonical `CacheAdapter` signature: key, by, ttl) —
|
|
81
|
+
* synchronous on purpose: no `await` between read and write means
|
|
82
|
+
* concurrent bumps on the event loop can't interleave, which is the whole
|
|
83
|
+
* point of the `CacheStore.increment` contract.
|
|
84
|
+
*
|
|
85
|
+
* TTL semantics follow the repo-core contract: `ttlSeconds` applies only
|
|
86
|
+
* when the key is CREATED — an existing counter keeps its original expiry
|
|
87
|
+
* (Redis `EXPIRE ... NX` semantics), so every conforming adapter ages a
|
|
88
|
+
* counter identically.
|
|
89
|
+
*/
|
|
90
|
+
increment(key, by = 1, ttlSeconds) {
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const entry = this.cache.get(key);
|
|
93
|
+
const live = entry && entry.expiresAt > now;
|
|
94
|
+
const next = (live && typeof entry.value === "number" ? entry.value : 0) + by;
|
|
95
|
+
const effectiveTtlSeconds = ttlSeconds ?? this.defaultTtlSeconds;
|
|
96
|
+
if (entry) this.currentBytes -= entry.size;
|
|
97
|
+
this.cache.delete(key);
|
|
98
|
+
const size = 8;
|
|
99
|
+
this.cache.set(key, {
|
|
100
|
+
value: next,
|
|
101
|
+
size,
|
|
102
|
+
expiresAt: live ? entry.expiresAt : now + Math.max(1, effectiveTtlSeconds) * 1e3
|
|
103
|
+
});
|
|
104
|
+
this.currentBytes += size;
|
|
105
|
+
this.evictToLimit();
|
|
106
|
+
return next;
|
|
107
|
+
}
|
|
79
108
|
async clear(pattern) {
|
|
80
109
|
if (pattern === void 0) {
|
|
81
110
|
this.cache.clear();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as MiddlewareHandler, L as RequestWithExtras, Q as MiddlewareConfig } from "../index-
|
|
1
|
+
import { I as MiddlewareHandler, L as RequestWithExtras, Q as MiddlewareConfig } from "../index-DPRH0Cme.mjs";
|
|
2
2
|
import { RouteHandlerMethod } from "fastify";
|
|
3
3
|
//#region src/middleware/middleware.d.ts
|
|
4
4
|
interface NamedMiddleware {
|
|
@@ -90,6 +90,14 @@ interface MultipartBodyOptions {
|
|
|
90
90
|
* @example ['avatar', 'cover'] // multi-field upload (profile editor)
|
|
91
91
|
*/
|
|
92
92
|
requiredFields?: string[];
|
|
93
|
+
/**
|
|
94
|
+
* Cap on the TOTAL buffered file bytes per request (default: 25 MiB).
|
|
95
|
+
* Per-file and per-count limits bound individual files but not their
|
|
96
|
+
* SUM — without this, `maxFiles: 5` × `maxFileSize: 10 MiB` lets one
|
|
97
|
+
* request retain ~50 MiB of process memory, and concurrency multiplies
|
|
98
|
+
* it (20 such uploads ≈ 1 GiB). Exceeding the cap returns 413.
|
|
99
|
+
*/
|
|
100
|
+
maxTotalBytes?: number;
|
|
93
101
|
}
|
|
94
102
|
/**
|
|
95
103
|
* Create a multipart body parsing middleware.
|
|
@@ -102,6 +110,14 @@ interface MultipartBodyOptions {
|
|
|
102
110
|
* For non-multipart requests (regular JSON), this is a no-op — the request
|
|
103
111
|
* passes through unchanged. This makes it safe to add to create/update
|
|
104
112
|
* middlewares without breaking JSON clients.
|
|
113
|
+
*
|
|
114
|
+
* **BUFFERED — for small uploads only.** Every accepted file is held fully
|
|
115
|
+
* in process memory for the request's lifetime; memory cost is
|
|
116
|
+
* `concurrency × maxTotalBytes`. Suitable for avatars, documents, and
|
|
117
|
+
* form attachments. For large files use PRESIGNED direct-to-object-storage
|
|
118
|
+
* uploads (see the files-upload preset / production guide) and pair upload
|
|
119
|
+
* routes with strict rate limits — buffering video through the app tier is
|
|
120
|
+
* the wrong architecture regardless of limits.
|
|
105
121
|
*/
|
|
106
122
|
declare function multipartBody(options?: MultipartBodyOptions): RouteHandlerMethod;
|
|
107
123
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-DNre0wRg.mjs";
|
|
2
|
-
import { t as multipartBody } from "../multipartBody-
|
|
2
|
+
import { t as multipartBody } from "../multipartBody-D0eBvkeq.mjs";
|
|
3
3
|
//#region src/middleware/middleware.ts
|
|
4
4
|
/**
|
|
5
5
|
* Named Middleware — Priority-based, conditional middleware execution.
|
|
@@ -1,41 +1,5 @@
|
|
|
1
|
+
import { LockAdapter } from "@classytic/repo-core/lock";
|
|
1
2
|
//#region src/migrations/index.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Schema Versioning and Migrations System
|
|
4
|
-
*
|
|
5
|
-
* Manages database schema changes over time with version tracking.
|
|
6
|
-
* Supports forward migrations, rollbacks, and schema compatibility layers.
|
|
7
|
-
*
|
|
8
|
-
* DB-agnostic: the `db` parameter is typed as `unknown` — the user passes
|
|
9
|
-
* whatever connection object their adapter uses (Mongoose db, Prisma client,
|
|
10
|
-
* Knex instance, etc.) and their `up`/`down` functions cast it internally.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* import { defineMigration, MigrationRunner } from '@classytic/arc/migrations';
|
|
14
|
-
*
|
|
15
|
-
* const productV2 = defineMigration({
|
|
16
|
-
* version: 2,
|
|
17
|
-
* resource: 'product',
|
|
18
|
-
* up: async (db) => {
|
|
19
|
-
* const mongo = db as import('mongoose').mongo.Db;
|
|
20
|
-
* await mongo.collection('products').updateMany(
|
|
21
|
-
* {},
|
|
22
|
-
* { $rename: { 'oldField': 'newField' } }
|
|
23
|
-
* );
|
|
24
|
-
* },
|
|
25
|
-
* down: async (db) => {
|
|
26
|
-
* const mongo = db as import('mongoose').mongo.Db;
|
|
27
|
-
* await mongo.collection('products').updateMany(
|
|
28
|
-
* {},
|
|
29
|
-
* { $rename: { 'newField': 'oldField' } }
|
|
30
|
-
* );
|
|
31
|
-
* },
|
|
32
|
-
* });
|
|
33
|
-
*
|
|
34
|
-
* const runner = new MigrationRunner(mongoose.connection.db, {
|
|
35
|
-
* store: new MongoMigrationStore(mongoose.connection.db),
|
|
36
|
-
* });
|
|
37
|
-
* await runner.up(migrations);
|
|
38
|
-
*/
|
|
39
3
|
interface Migration {
|
|
40
4
|
/** Migration version (sequential number) */
|
|
41
5
|
version: number;
|
|
@@ -63,6 +27,12 @@ interface MigrationRecord {
|
|
|
63
27
|
description?: string;
|
|
64
28
|
appliedAt: Date;
|
|
65
29
|
executionTime: number;
|
|
30
|
+
/**
|
|
31
|
+
* SHA-256 of the migration's `up` + `down` source at apply time.
|
|
32
|
+
* Present on records written by 2.24+; older records lack it and are
|
|
33
|
+
* skipped by drift detection.
|
|
34
|
+
*/
|
|
35
|
+
checksum?: string;
|
|
66
36
|
}
|
|
67
37
|
/**
|
|
68
38
|
* DB-agnostic migration store interface.
|
|
@@ -75,8 +45,14 @@ interface MigrationRecord {
|
|
|
75
45
|
interface MigrationStore {
|
|
76
46
|
/** Get all applied migration records, sorted by appliedAt ascending */
|
|
77
47
|
getApplied(): Promise<MigrationRecord[]>;
|
|
78
|
-
/**
|
|
79
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Record a completed migration. `meta.checksum` (2.24+) is the source
|
|
50
|
+
* checksum for drift detection — stores should persist it when given;
|
|
51
|
+
* existing two-argument implementations remain valid and simply skip it.
|
|
52
|
+
*/
|
|
53
|
+
record(migration: Migration, executionTime: number, meta?: {
|
|
54
|
+
checksum?: string;
|
|
55
|
+
}): Promise<void>;
|
|
80
56
|
/** Remove a migration record (for rollback) */
|
|
81
57
|
remove(migration: Migration): Promise<void>;
|
|
82
58
|
}
|
|
@@ -123,7 +99,9 @@ declare class MongoMigrationStore implements MigrationStore {
|
|
|
123
99
|
collectionName?: string;
|
|
124
100
|
});
|
|
125
101
|
getApplied(): Promise<MigrationRecord[]>;
|
|
126
|
-
record(migration: Migration, executionTime: number
|
|
102
|
+
record(migration: Migration, executionTime: number, meta?: {
|
|
103
|
+
checksum?: string;
|
|
104
|
+
}): Promise<void>;
|
|
127
105
|
remove(migration: Migration): Promise<void>;
|
|
128
106
|
}
|
|
129
107
|
interface MigrationRunnerOptions {
|
|
@@ -131,7 +109,41 @@ interface MigrationRunnerOptions {
|
|
|
131
109
|
store: MigrationStore;
|
|
132
110
|
/** Logger (defaults to process.stdout/stderr) */
|
|
133
111
|
logger?: MigrationLogger;
|
|
112
|
+
/**
|
|
113
|
+
* Ecosystem lock adapter (`LockAdapter` from `@classytic/repo-core/lock`)
|
|
114
|
+
* for multi-replica / concurrent-deploy safety.
|
|
115
|
+
* When set, `up()` / `down()` / `downTo()` acquire the `arc:migrations`
|
|
116
|
+
* lease first and FAIL FAST if another runner holds it — two replicas
|
|
117
|
+
* starting together can no longer both execute the same migration.
|
|
118
|
+
* Omit only when a single owner (deploy job, CI step) runs migrations.
|
|
119
|
+
*/
|
|
120
|
+
lock?: LockAdapter;
|
|
121
|
+
/**
|
|
122
|
+
* Lease duration for the migration lock (default: 10 minutes). Must
|
|
123
|
+
* comfortably exceed the slowest migration so the lease cannot lapse
|
|
124
|
+
* mid-run and admit a second runner.
|
|
125
|
+
*/
|
|
126
|
+
lockLeaseMs?: number;
|
|
127
|
+
/** Lease holder identity (default: random per runner instance). */
|
|
128
|
+
holderId?: string;
|
|
129
|
+
/**
|
|
130
|
+
* What to do when an APPLIED migration's current source no longer matches
|
|
131
|
+
* the checksum recorded at apply time (the file was edited after being
|
|
132
|
+
* applied — drift that silently invalidates rollback correctness).
|
|
133
|
+
* - `'warn'` (default) — log and continue. Checksums come from
|
|
134
|
+
* `Function.prototype.toString()`, so a transpile-target change can
|
|
135
|
+
* shift them without semantic drift; warn keeps that honest.
|
|
136
|
+
* - `'error'` — throw before running anything.
|
|
137
|
+
* Records without a checksum (written by older stores) are skipped.
|
|
138
|
+
*/
|
|
139
|
+
checksumMismatch?: "warn" | "error";
|
|
134
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Source checksum for drift detection — SHA-256 over the migration's
|
|
143
|
+
* `up`/`down` source. Stable for a given build; a transpile-target change
|
|
144
|
+
* can legitimately shift it (hence `checksumMismatch: 'warn'` default).
|
|
145
|
+
*/
|
|
146
|
+
declare function computeMigrationChecksum(migration: Migration): string;
|
|
135
147
|
/**
|
|
136
148
|
* Define a migration
|
|
137
149
|
*/
|
|
@@ -162,9 +174,40 @@ declare class MigrationRunner {
|
|
|
162
174
|
private readonly db;
|
|
163
175
|
private readonly store;
|
|
164
176
|
private readonly log;
|
|
177
|
+
private readonly lock?;
|
|
178
|
+
private readonly lockLeaseMs;
|
|
179
|
+
private readonly holderId;
|
|
180
|
+
private readonly checksumMismatch;
|
|
165
181
|
constructor(db: unknown, opts: MigrationRunnerOptions);
|
|
166
182
|
/**
|
|
167
|
-
*
|
|
183
|
+
* Acquire the migration lease (when a lock is configured) and run `fn`.
|
|
184
|
+
* Fail-fast on contention: a concurrent deploy should error loudly, not
|
|
185
|
+
* queue — the OTHER runner is applying the same set.
|
|
186
|
+
*
|
|
187
|
+
* The lease renews while `fn` runs (`startRenewingLease` — serialized,
|
|
188
|
+
* awaited teardown), so a migration slower than `lockLeaseMs` can't let
|
|
189
|
+
* a second deployment acquire the lock mid-run. Ownership loss is
|
|
190
|
+
* ENFORCED, not just logged: `fn` receives a `leaseLost` probe to refuse
|
|
191
|
+
* starting further migrations, and even a successful `fn` fails the run
|
|
192
|
+
* when the lease was lost — a concurrent runner may have interleaved, so
|
|
193
|
+
* the applied state must be verified, not trusted. (A stale holder's
|
|
194
|
+
* in-flight write can only be truly fenced by a token from the lock
|
|
195
|
+
* contract — planned repo-core evolution.)
|
|
196
|
+
*/
|
|
197
|
+
private withLock;
|
|
198
|
+
/**
|
|
199
|
+
* Drift detection: compare each APPLIED record's stored checksum against
|
|
200
|
+
* the current source of the same migration. A mismatch means the file was
|
|
201
|
+
* edited after being applied — its `down()` may no longer invert what
|
|
202
|
+
* actually ran.
|
|
203
|
+
*/
|
|
204
|
+
private verifyChecksums;
|
|
205
|
+
/**
|
|
206
|
+
* Run all pending migrations.
|
|
207
|
+
*
|
|
208
|
+
* With a `lock` configured, the applied set is (re)read INSIDE the lease —
|
|
209
|
+
* a runner that raced another deploy computes pending against the winner's
|
|
210
|
+
* completed writes, not a stale pre-lock snapshot.
|
|
168
211
|
*/
|
|
169
212
|
up(migrations: Migration[]): Promise<void>;
|
|
170
213
|
/**
|
|
@@ -172,9 +215,16 @@ declare class MigrationRunner {
|
|
|
172
215
|
*/
|
|
173
216
|
down(migrations: Migration[]): Promise<void>;
|
|
174
217
|
/**
|
|
175
|
-
* Rollback to specific version
|
|
218
|
+
* Rollback to a specific version.
|
|
219
|
+
*
|
|
220
|
+
* Versions are RESOURCE-scoped, so a bare numeric threshold is only
|
|
221
|
+
* meaningful within one resource. When the applied set spans multiple
|
|
222
|
+
* resources, `options.resource` is required — rolling every resource back
|
|
223
|
+
* across a shared number silently reverts unrelated schemas.
|
|
176
224
|
*/
|
|
177
|
-
downTo(migrations: Migration[], targetVersion: number
|
|
225
|
+
downTo(migrations: Migration[], targetVersion: number, options?: {
|
|
226
|
+
resource?: string;
|
|
227
|
+
}): Promise<void>;
|
|
178
228
|
/**
|
|
179
229
|
* Get all applied migrations
|
|
180
230
|
*/
|
|
@@ -242,4 +292,4 @@ declare class MigrationRegistry {
|
|
|
242
292
|
clear(): void;
|
|
243
293
|
}
|
|
244
294
|
//#endregion
|
|
245
|
-
export { Migration, MigrationLogger, MigrationRecord, MigrationRegistry, MigrationRunner, MigrationRunnerOptions, MigrationStore, MongoMigrationStore, SchemaVersion, defineMigration, withSchemaVersion };
|
|
295
|
+
export { Migration, MigrationLogger, MigrationRecord, MigrationRegistry, MigrationRunner, MigrationRunnerOptions, MigrationStore, MongoMigrationStore, SchemaVersion, computeMigrationChecksum, defineMigration, withSchemaVersion };
|