@classytic/arc 2.38.0 → 2.41.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/dist/{BaseController-CYaDvmOB.mjs → BaseController-DYJRZ0EJ.mjs} +114 -24
- package/dist/{EventTransport-Cx60_s-6.d.mts → EventTransport-CbUKDzZZ.d.mts} +33 -0
- package/dist/{EventTransport-Gq7JWbWw.mjs → EventTransport-n7qO5Cz3.mjs} +22 -2
- package/dist/{HookSystem-C0ikzqQf.mjs → HookSystem-CxvZBo77.mjs} +2 -2
- package/dist/{ResourceRegistry-DPcX--zk.mjs → ResourceRegistry-Cu7uTJp9.mjs} +2 -2
- package/dist/{actionPermissions-jlFZ1it4.mjs → actionPermissions-DPwPWPOV.mjs} +7 -6
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +10 -3
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +12 -6
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-BP8GHtjc.mjs → authorizationDecision-2YY_Kaj1.mjs} +2 -2
- package/dist/{base-C-NL2fTi.d.mts → base-CxrV1LmG.d.mts} +10 -0
- package/dist/{buildHandler-DjXOqIvX.mjs → buildHandler-CUhjGHGZ.mjs} +4 -4
- package/dist/cache/index.d.mts +4 -2
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-a3ZEcJJs.mjs → caching-DsWDjIxx.mjs} +2 -2
- package/dist/cleanup/index.d.mts +2 -2
- package/dist/cleanup/index.mjs +21 -5
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/describe.mjs +3 -3
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -2
- package/dist/cli/commands/init.mjs +2 -2
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-BONghE1Y.mjs → constants-BxLgNFF7.mjs} +1 -1
- package/dist/context/index.d.mts +34 -6
- package/dist/context/index.mjs +42 -24
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +7 -7
- package/dist/{core-8She7FgF.mjs → core-iNGqYerO.mjs} +3 -3
- package/dist/{createActionRouter-Bqi6Bhhx.mjs → createActionRouter-BBlbPs3k.mjs} +7 -7
- package/dist/{createAggregationRouter-BsYO7ktd.mjs → createAggregationRouter-Dv5SuK2x.mjs} +7 -7
- package/dist/createMcpServer-CbDWkZ1T.d.mts +48 -0
- package/dist/{defineAggregation-0KBvqEEC.mjs → defineAggregation-UyeurGkp.mjs} +1 -1
- package/dist/{defineResource-BMdyRelW.mjs → defineResource-DzGT-2LP.mjs} +20 -22
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +3 -3
- package/dist/{elevation-C6woysGC.mjs → elevation-BF-vhGxY.mjs} +4 -4
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-DI5AN2b5.d.mts → errorHandler--4rSMMvq.d.mts} +7 -1
- package/dist/{errorHandler-BdmIWPba.mjs → errorHandler-CIMkCOWm.mjs} +3 -3
- package/dist/{errors-2ExmOgIJ.mjs → errors-C_YhELhe.mjs} +1 -1
- package/dist/{eventPlugin-DFvHa4Nf.d.mts → eventPlugin-B52abJ8W.d.mts} +1 -1
- package/dist/{eventPlugin-DJwpGMzx.mjs → eventPlugin-CmXaPWLv.mjs} +5 -5
- package/dist/events/index.d.mts +4 -4
- package/dist/events/index.mjs +5 -5
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +34 -24
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +11 -8
- package/dist/factory/index.d.mts +5 -5
- package/dist/factory/index.mjs +5 -5
- package/dist/{fastify-augmentation-jYA2oHyL.d.mts → fastify-augmentation-Cq2nHVaW.d.mts} +3 -3
- package/dist/{fields-Cha2QCKH.mjs → fields-BTyPLHK0.mjs} +1 -1
- package/dist/{generate-BUP4Oydg.mjs → generate-D_YmiCNf.mjs} +4 -3
- package/dist/{headers-DeXNifVj.mjs → headers-CNECDE6q.mjs} +2 -2
- package/dist/{health-Bh0N_FLk.mjs → health-CxQzbJmr.mjs} +10 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +2 -2
- package/dist/idempotency/index.d.mts +1 -1
- package/dist/idempotency/index.mjs +4 -4
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +1 -1
- package/dist/{index-BtYoMip1.d.mts → index-BHdZD8zK.d.mts} +10 -2
- package/dist/{index-D2uIUSQk.d.mts → index-CFfLKMH4.d.mts} +2 -2
- package/dist/{index-eyg5WEcy.d.mts → index-CbJiYrfv.d.mts} +1 -1
- package/dist/{index-BRoDtjek.d.mts → index-D4u76eQW.d.mts} +3 -3
- package/dist/{index-B78WZom8.d.mts → index-DHbyPo5j.d.mts} +52 -12
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +11 -11
- package/dist/{init-Dg_Am2o_.mjs → init-Y2MlSN9n.mjs} +9 -9
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +2 -2
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.d.mts +1 -1
- package/dist/integrations/jobs/index.mjs +2 -2
- package/dist/integrations/mcp/index.d.mts +4 -47
- package/dist/integrations/mcp/index.mjs +2 -2
- package/dist/integrations/mcp/testing.d.mts +22 -2
- package/dist/integrations/mcp/testing.mjs +55 -33
- package/dist/integrations/streamline.mjs +3 -3
- package/dist/integrations/webhooks.d.mts +2 -2
- package/dist/integrations/webhooks.mjs +3 -3
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.mjs +24 -6
- package/dist/{interface-DYPVscCU.mjs → interface-BIpgzbZl.mjs} +1 -1
- package/dist/{jsonBody-DVui0uJl.mjs → jsonBody-Cq9KYl2Z.mjs} +2 -2
- package/dist/{keys-BSuxGS-B.mjs → keys-Cmv4cBRa.mjs} +1 -1
- package/dist/{loadResources-CNd1DWnK.mjs → loadResources-CblF7xt2.mjs} +2 -2
- package/dist/{loadResourcesFromEntry-DNj8cpiY.mjs → loadResourcesFromEntry-99fIMC3p.mjs} +2 -2
- package/dist/logger/index.mjs +1 -1
- package/dist/{mcpPlugin-KWsezMx7.mjs → mcpPlugin-xF3Gw-HW.mjs} +75 -31
- package/dist/{memory-BiPVaCPg.mjs → memory-CpDZ9NPw.mjs} +2 -2
- package/dist/{metrics-CsWaIVTa.mjs → metrics-BaKnq8ag.mjs} +2 -2
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-BQXHxMEk.mjs → module-BkQEesNQ.mjs} +1 -1
- package/dist/{multipartBody-u6VrS_iH.mjs → multipartBody-C92aJ8am.mjs} +1 -1
- package/dist/{openapi-Dxc8RLy_.mjs → openapi-C3LFRzPq.mjs} +6 -6
- package/dist/outbox-admin/index.d.mts +2 -2
- package/dist/outbox-admin/index.mjs +4 -4
- package/dist/{outbox-module-CQXu_O6o.d.mts → outbox-module-BHdUnuuK.d.mts} +15 -2
- package/dist/{outbox-module-DyIqA5vo.mjs → outbox-module-dsCyLxXH.mjs} +43 -21
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +5 -5
- package/dist/{permissions-H7Zcb82Z.mjs → permissions-DiHXy8Ck.mjs} +12 -6
- package/dist/{pipe-SunhEaZ6.mjs → pipe-CBVmUJ-j.mjs} +2 -2
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plugins/index.d.mts +7 -37
- package/dist/plugins/index.mjs +34 -22
- package/dist/plugins/response-cache.mjs +9 -2
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-Da7IT_qJ.mjs → pluralize-2_U6QZQg.mjs} +1 -1
- package/dist/{preloadResources-DlhH81py.mjs → preloadResources-Bbp4QJ8V.mjs} +82 -26
- package/dist/{preloadResources-BbBkNkqK.d.mts → preloadResources-CjS2OfpU.d.mts} +14 -9
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +2 -2
- package/dist/presets/index.mjs +2 -2
- package/dist/presets/multiTenant.d.mts +38 -1
- package/dist/presets/multiTenant.mjs +24 -6
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-CNyfaxGL.mjs → presets-BdlugYTc.mjs} +4 -4
- package/dist/{projection-DSfEoooA.mjs → projection-Uvgp-EEj.mjs} +2 -2
- package/dist/{queryCachePlugin-oTsmVyEQ.mjs → queryCachePlugin-B5F09bYr.mjs} +11 -5
- package/dist/rateLimitKey-CK-EU2L8.mjs +41 -0
- package/dist/{realtime-CPmjjgqd.mjs → realtime-FnnD0n3Z.mjs} +9 -9
- package/dist/{redis-CduhKsJi.d.mts → redis-Bzi1y3F9.d.mts} +10 -2
- package/dist/{redis-stream-eh0eOtjh.d.mts → redis-stream-BPKscsTB.d.mts} +12 -7
- package/dist/{registerRoute-XvcI_3sh.mjs → registerRoute-DRsSHPSM.mjs} +6 -6
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +3 -3
- package/dist/{registry-UFEJQN8R.mjs → registry-D3y8VEJ1.mjs} +2 -2
- package/dist/{renewingLease-DuRGVOMe.mjs → renewingLease-B3VYh5aU.mjs} +1 -1
- package/dist/{replyHelpers-BHebarf0.mjs → replyHelpers-DPHsdw-5.mjs} +2 -2
- package/dist/{requestContext-DpmntRZy.mjs → requestContext-cSVKnYPk.mjs} +1 -1
- package/dist/{rolldown-runtime-Add2L50I.mjs → rolldown-runtime-DjDuyglE.mjs} +1 -1
- package/dist/{runtimeCapabilities-D793Pxci.mjs → runtimeCapabilities-DSA_dH1_.mjs} +31 -5
- package/dist/{schedules-DthM8Qag.mjs → schedules-BktJb3Y3.mjs} +8 -4
- package/dist/{schemaConverter-Db-MMsH6.mjs → schemaConverter-BLCSmNbr.mjs} +1 -1
- package/dist/{schemaIR-CIXShch7.mjs → schemaIR-K16Bb5BX.mjs} +2 -2
- package/dist/schemas/index.mjs +1 -1
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.mjs +5 -5
- package/dist/{scope-D03sk17n.mjs → scope-jSxuBTFM.mjs} +4 -4
- package/dist/{simpleEqualityMatcher-B7qVjQBv.mjs → simpleEqualityMatcher-BiOnyflP.mjs} +1 -1
- package/dist/{sse-D4MYqkic.mjs → sse-CH7Slxc1.mjs} +5 -5
- package/dist/{sseStream-BuMNPR6r.mjs → sseStream-CN-Lh1Tu.mjs} +2 -2
- package/dist/{store-helpers-CIF4RG8s.mjs → store-helpers-9DX9-1j_.mjs} +1 -1
- package/dist/{streaming-DkpW1HwJ.mjs → streaming-dvppnxsr.mjs} +1 -1
- package/dist/{subscribe-helpers-CBZOi6pk.mjs → subscribe-helpers-AVYPgqTl.mjs} +2 -2
- package/dist/sync/changelog.mjs +1 -1
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +5 -5
- package/dist/testing/index.mjs +10 -10
- package/dist/testing/outboxStoreContract.mjs +14 -3
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/{transactionContext-DMmvcfdo.mjs → transactionContext-DCWMI6hV.mjs} +1 -1
- package/dist/{typeGuards-DHTnEs5X.mjs → typeGuards-D7tHFdH6.mjs} +1 -1
- package/dist/types/index.d.mts +2 -2
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-BqLyyH0o.mjs → types-CDy3-zw1.mjs} +4 -4
- package/dist/{types-LQFwC37d.d.mts → types-CWQn9-IC.d.mts} +46 -1
- package/dist/{types-BRU-Kd3t.d.mts → types-DfVDvkdP.d.mts} +20 -2
- package/dist/usage/index.d.mts +58 -2
- package/dist/usage/index.mjs +0 -0
- package/dist/{userHelpers-CXySQFRg.mjs → userHelpers-DDMOZ_Pm.mjs} +1 -1
- package/dist/utils/index.d.mts +110 -8
- package/dist/utils/index.mjs +12 -12
- package/dist/{utils-BbGG8XNT.mjs → utils-B-zj_b__.mjs} +61 -12
- package/dist/{validate-CidSeUrx.mjs → validate-R0G_JrnK.mjs} +2 -2
- package/dist/{versioning-B843jOYX.d.mts → versioning-D6JIK8Tk.d.mts} +58 -2
- package/dist/{versioning-C2r0eOZ4.mjs → versioning-DuVWcu-F.mjs} +3 -3
- package/dist/workScope-BhsWde4k.mjs +41 -0
- package/package.json +574 -565
- package/skills/arc/SKILL.md +8 -4
- package/skills/arc/references/mcp.md +20 -0
- package/skills/arc/references/production.md +94 -9
- package/skills/arc/references/testing.md +0 -12
- package/skills/arc-code-review/references/anti-patterns.md +1 -1
- package/skills/arc-code-review/references/migration-recipes.md +0 -60
- package/skills/arc-code-review/references/mongokit-migration.md +0 -27
- package/skills/arc-module-publishing/references/package-manifest.md +3 -1
- package/dist/rateLimitKey-CewdR-m6.mjs +0 -15
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { p as MUTATION_OPERATIONS } from "../constants-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { p as MUTATION_OPERATIONS } from "../constants-BxLgNFF7.mjs";
|
|
3
3
|
import { arcLog } from "../logger/index.mjs";
|
|
4
|
-
import { c as getOrgId } from "../types-
|
|
5
|
-
import { n as requestContext } from "../requestContext-
|
|
6
|
-
import { t as hasEvents } from "../typeGuards-
|
|
7
|
-
import { t as HookSystem } from "../HookSystem-
|
|
8
|
-
import { t as ResourceRegistry } from "../ResourceRegistry-
|
|
9
|
-
import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_default, o as requestId_default, s as sanitizeRequestId, t as healthPlugin } from "../health-
|
|
10
|
-
import { n as caching_default, t as cachingPlugin } from "../caching-
|
|
11
|
-
import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-
|
|
12
|
-
import { n as metrics_default, t as metricsPlugin } from "../metrics-
|
|
13
|
-
import { n as realtime_default, t as realtimePlugin } from "../realtime-
|
|
14
|
-
import { t as replyHelpersPlugin } from "../replyHelpers-
|
|
15
|
-
import { n as schedules_default, t as schedulesPlugin } from "../schedules-
|
|
16
|
-
import { n as sse_default, t as ssePlugin } from "../sse-
|
|
17
|
-
import { n as versioning_default, t as versioningPlugin } from "../versioning-
|
|
4
|
+
import { c as getOrgId } from "../types-CDy3-zw1.mjs";
|
|
5
|
+
import { n as requestContext } from "../requestContext-cSVKnYPk.mjs";
|
|
6
|
+
import { t as hasEvents } from "../typeGuards-D7tHFdH6.mjs";
|
|
7
|
+
import { t as HookSystem } from "../HookSystem-CxvZBo77.mjs";
|
|
8
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-Cu7uTJp9.mjs";
|
|
9
|
+
import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_default, o as requestId_default, s as sanitizeRequestId, t as healthPlugin } from "../health-CxQzbJmr.mjs";
|
|
10
|
+
import { n as caching_default, t as cachingPlugin } from "../caching-DsWDjIxx.mjs";
|
|
11
|
+
import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-CIMkCOWm.mjs";
|
|
12
|
+
import { n as metrics_default, t as metricsPlugin } from "../metrics-BaKnq8ag.mjs";
|
|
13
|
+
import { n as realtime_default, t as realtimePlugin } from "../realtime-FnnD0n3Z.mjs";
|
|
14
|
+
import { t as replyHelpersPlugin } from "../replyHelpers-DPHsdw-5.mjs";
|
|
15
|
+
import { n as schedules_default, t as schedulesPlugin } from "../schedules-BktJb3Y3.mjs";
|
|
16
|
+
import { n as sse_default, t as ssePlugin } from "../sse-CH7Slxc1.mjs";
|
|
17
|
+
import { n as versioning_default, t as versioningPlugin } from "../versioning-DuVWcu-F.mjs";
|
|
18
18
|
import fp from "fastify-plugin";
|
|
19
19
|
import { performance as performance$1 } from "node:perf_hooks";
|
|
20
20
|
//#region src/core/arcCorePlugin.ts
|
|
@@ -35,12 +35,14 @@ const arcCorePlugin = async (fastify, opts = {}) => {
|
|
|
35
35
|
for (const field of [
|
|
36
36
|
"_policyFilters",
|
|
37
37
|
"_ownershipCheck",
|
|
38
|
+
"_crossTenantRead",
|
|
38
39
|
"fieldMask",
|
|
39
40
|
"policyMetadata",
|
|
40
41
|
"document"
|
|
41
42
|
]) if (!fastify.hasRequestDecorator(field)) fastify.decorateRequest(field, void 0);
|
|
42
43
|
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
43
44
|
const store = {
|
|
45
|
+
kind: "request",
|
|
44
46
|
requestId: request.id,
|
|
45
47
|
startTime: performance.now()
|
|
46
48
|
};
|
|
@@ -153,18 +155,20 @@ function createPlugin(name, definition) {
|
|
|
153
155
|
//#endregion
|
|
154
156
|
//#region src/plugins/gracefulShutdown.ts
|
|
155
157
|
const gracefulShutdownPlugin = async (fastify, opts = {}) => {
|
|
156
|
-
const { timeout = 3e4, onShutdown, signals = ["SIGTERM", "SIGINT"], logEvents = true, onForceExit = () => process.exit(1) } = opts;
|
|
157
|
-
|
|
158
|
+
const { timeout = 3e4, onShutdown, signals = ["SIGTERM", "SIGINT"], logEvents = true, drainDelayMs = 0, onForceExit = () => process.exit(1) } = opts;
|
|
159
|
+
const state = { draining: false };
|
|
158
160
|
const signalHandlers = /* @__PURE__ */ new Map();
|
|
159
161
|
const shutdown = async (signal) => {
|
|
160
|
-
if (
|
|
162
|
+
if (state.draining) {
|
|
161
163
|
if (logEvents) fastify.log?.warn?.({ signal }, "Shutdown already in progress, ignoring signal");
|
|
162
164
|
return;
|
|
163
165
|
}
|
|
164
|
-
|
|
166
|
+
state.draining = true;
|
|
167
|
+
state.since = /* @__PURE__ */ new Date();
|
|
165
168
|
if (logEvents) fastify.log?.info?.({
|
|
166
169
|
signal,
|
|
167
|
-
timeout
|
|
170
|
+
timeout,
|
|
171
|
+
drainDelayMs
|
|
168
172
|
}, "Shutdown signal received, starting graceful shutdown");
|
|
169
173
|
const forceExitTimer = setTimeout(() => {
|
|
170
174
|
if (logEvents) fastify.log?.error?.("Graceful shutdown timeout exceeded, forcing exit");
|
|
@@ -172,6 +176,10 @@ const gracefulShutdownPlugin = async (fastify, opts = {}) => {
|
|
|
172
176
|
}, timeout);
|
|
173
177
|
forceExitTimer.unref();
|
|
174
178
|
try {
|
|
179
|
+
if (drainDelayMs > 0) {
|
|
180
|
+
if (logEvents) fastify.log?.info?.({ drainDelayMs }, "Draining — readiness failing, still serving");
|
|
181
|
+
await new Promise((resolve) => setTimeout(resolve, drainDelayMs));
|
|
182
|
+
}
|
|
175
183
|
if (logEvents) fastify.log?.info?.("Closing server to new connections");
|
|
176
184
|
await fastify.close();
|
|
177
185
|
if (onShutdown) {
|
|
@@ -200,7 +208,11 @@ const gracefulShutdownPlugin = async (fastify, opts = {}) => {
|
|
|
200
208
|
fastify.decorate("shutdown", async () => {
|
|
201
209
|
await shutdown("MANUAL");
|
|
202
210
|
});
|
|
203
|
-
|
|
211
|
+
fastify.decorate("shutdownState", state);
|
|
212
|
+
if (logEvents) fastify.log?.debug?.({
|
|
213
|
+
signals,
|
|
214
|
+
drainDelayMs
|
|
215
|
+
}, "Graceful shutdown plugin registered");
|
|
204
216
|
};
|
|
205
217
|
var gracefulShutdown_default = fp(gracefulShutdownPlugin, {
|
|
206
218
|
name: "arc-graceful-shutdown",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import {
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { n as declareRuntimeCapability } from "../runtimeCapabilities-DSA_dH1_.mjs";
|
|
3
|
+
import { t as hasEvents } from "../typeGuards-D7tHFdH6.mjs";
|
|
3
4
|
import fp from "fastify-plugin";
|
|
4
5
|
//#region src/plugins/response-cache.ts
|
|
5
6
|
/**
|
|
@@ -96,6 +97,12 @@ const responseCachePluginImpl = async (fastify, opts = {}) => {
|
|
|
96
97
|
if (maxEntries > 5e3) fastify.log.warn(`[arc] responseCache maxEntries=${maxEntries}: prefix invalidation scans all entries synchronously on every mutating request — expect event-loop pauses at this size. This cache is an instance-local micro-cache; for large shared caching use the queryCache with a Redis store instead.`);
|
|
97
98
|
const cache = new LRUCache(maxEntries);
|
|
98
99
|
const invalidateMethods = new Set(invalidateOn.map((m) => m.toUpperCase()));
|
|
100
|
+
declareRuntimeCapability(fastify, {
|
|
101
|
+
subsystem: "http.response-cache",
|
|
102
|
+
durability: "memory",
|
|
103
|
+
accepted: true,
|
|
104
|
+
detail: "per-replica micro-cache; correctness comes from short TTLs, not shared state"
|
|
105
|
+
});
|
|
99
106
|
if (!fastify.hasRequestDecorator("__arcCacheTTL")) fastify.decorateRequest("__arcCacheTTL", void 0);
|
|
100
107
|
/** Find TTL for a given URL path (seconds) */
|
|
101
108
|
function getTTL(url) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/plugins/tracing.ts
|
|
@@ -59,7 +59,7 @@ try {
|
|
|
59
59
|
function createTracerProvider(options) {
|
|
60
60
|
if (!isAvailable || !NodeTracerProvider || !BatchSpanProcessor || !OTLPTraceExporter) return null;
|
|
61
61
|
const { serviceName = "@classytic/arc", serviceVersion, exporterUrl = "http://localhost:4318/v1/traces" } = options;
|
|
62
|
-
const resolvedVersion = serviceVersion ?? "2.
|
|
62
|
+
const resolvedVersion = serviceVersion ?? "2.41.0";
|
|
63
63
|
const exporter = new OTLPTraceExporter({ url: exporterUrl });
|
|
64
64
|
const provider = new NodeTracerProvider({ resource: { attributes: {
|
|
65
65
|
"service.name": serviceName,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-DjDuyglE.mjs";
|
|
3
3
|
import { arcLog, configureArcLogger, createPinoWriter, enterArcLoggerScope, runWithArcLogger } from "./logger/index.mjs";
|
|
4
|
-
import { n as PUBLIC_SCOPE } from "./types-
|
|
5
|
-
import { t as auditRuntimeCapabilities } from "./runtimeCapabilities-
|
|
6
|
-
import { t as createTenantKeyGenerator } from "./rateLimitKey-
|
|
7
|
-
import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-
|
|
8
|
-
import { t as parseJsonBody } from "./jsonBody-
|
|
9
|
-
import { r as wrapWithBoundary } from "./subscribe-helpers-
|
|
4
|
+
import { n as PUBLIC_SCOPE } from "./types-CDy3-zw1.mjs";
|
|
5
|
+
import { r as initRuntimeCapabilityRegistry, t as auditRuntimeCapabilities } from "./runtimeCapabilities-DSA_dH1_.mjs";
|
|
6
|
+
import { n as isScopeAwareKeyGenerator, t as createTenantKeyGenerator } from "./rateLimitKey-CK-EU2L8.mjs";
|
|
7
|
+
import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-CxQzbJmr.mjs";
|
|
8
|
+
import { t as parseJsonBody } from "./jsonBody-Cq9KYl2Z.mjs";
|
|
9
|
+
import { r as wrapWithBoundary } from "./subscribe-helpers-AVYPgqTl.mjs";
|
|
10
10
|
import { isProductionEnv } from "@classytic/primitives/environment";
|
|
11
11
|
import Fastify from "fastify";
|
|
12
12
|
import qs from "qs";
|
|
@@ -851,7 +851,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
851
851
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
852
852
|
trackPlugin("arc-core");
|
|
853
853
|
if (config.arcPlugins?.events !== false) {
|
|
854
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
854
|
+
const { default: eventPlugin } = await import("./eventPlugin-CmXaPWLv.mjs").then((n) => n.n);
|
|
855
855
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
856
856
|
await fastify.register(eventPlugin, {
|
|
857
857
|
/**
|
|
@@ -906,19 +906,20 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
|
|
|
906
906
|
trackPlugin("arc-health", { checkCount: healthOpts.checks?.length ?? 0 });
|
|
907
907
|
}
|
|
908
908
|
if (config.arcPlugins?.gracefulShutdown !== false) {
|
|
909
|
-
|
|
910
|
-
|
|
909
|
+
const shutdownOpts = typeof config.arcPlugins?.gracefulShutdown === "object" ? config.arcPlugins.gracefulShutdown : {};
|
|
910
|
+
await fastify.register(gracefulShutdownPlugin, shutdownOpts);
|
|
911
|
+
trackPlugin("arc-graceful-shutdown", shutdownOpts);
|
|
911
912
|
}
|
|
912
913
|
if (config.arcPlugins?.caching) {
|
|
913
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
914
|
+
const { default: cachingPlugin } = await import("./caching-DsWDjIxx.mjs").then((n) => n.r);
|
|
914
915
|
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
915
916
|
await fastify.register(cachingPlugin, opts);
|
|
916
917
|
trackPlugin("arc-caching", opts);
|
|
917
918
|
}
|
|
918
919
|
if (config.arcPlugins?.queryCache) {
|
|
919
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
920
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-B5F09bYr.mjs").then((n) => n.n);
|
|
920
921
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
921
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
922
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-CpDZ9NPw.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
922
923
|
await fastify.register(queryCachePlugin, {
|
|
923
924
|
store,
|
|
924
925
|
...opts
|
|
@@ -928,7 +929,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
|
|
|
928
929
|
if (config.arcPlugins?.sse) {
|
|
929
930
|
if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
930
931
|
else {
|
|
931
|
-
const { default: ssePlugin } = await import("./sse-
|
|
932
|
+
const { default: ssePlugin } = await import("./sse-CH7Slxc1.mjs").then((n) => n.r);
|
|
932
933
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
933
934
|
await fastify.register(ssePlugin, opts);
|
|
934
935
|
trackPlugin("arc-sse", opts);
|
|
@@ -937,20 +938,20 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
|
|
|
937
938
|
if (config.arcPlugins?.realtime) {
|
|
938
939
|
if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
|
|
939
940
|
else {
|
|
940
|
-
const { default: realtimePlugin } = await import("./realtime-
|
|
941
|
+
const { default: realtimePlugin } = await import("./realtime-FnnD0n3Z.mjs").then((n) => n.r);
|
|
941
942
|
const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
|
|
942
943
|
await fastify.register(realtimePlugin, opts);
|
|
943
944
|
trackPlugin("arc-realtime", opts);
|
|
944
945
|
}
|
|
945
946
|
}
|
|
946
947
|
if (config.arcPlugins?.metrics) {
|
|
947
|
-
const { default: metricsPlugin } = await import("./metrics-
|
|
948
|
+
const { default: metricsPlugin } = await import("./metrics-BaKnq8ag.mjs").then((n) => n.r);
|
|
948
949
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
949
950
|
await fastify.register(metricsPlugin, opts);
|
|
950
951
|
trackPlugin("arc-metrics", opts);
|
|
951
952
|
}
|
|
952
953
|
if (config.arcPlugins?.versioning) {
|
|
953
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
954
|
+
const { default: versioningPlugin } = await import("./versioning-DuVWcu-F.mjs").then((n) => n.r);
|
|
954
955
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
955
956
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
956
957
|
}
|
|
@@ -1027,7 +1028,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
1027
1028
|
*/
|
|
1028
1029
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
1029
1030
|
if (!config.elevation) return;
|
|
1030
|
-
const { elevationPlugin } = await import("./elevation-
|
|
1031
|
+
const { elevationPlugin } = await import("./elevation-BF-vhGxY.mjs").then((n) => n.r);
|
|
1031
1032
|
await fastify.register(elevationPlugin, config.elevation);
|
|
1032
1033
|
trackPlugin("arc-elevation", config.elevation);
|
|
1033
1034
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -1042,7 +1043,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
1042
1043
|
*/
|
|
1043
1044
|
async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
|
|
1044
1045
|
if (config.errorHandler === false) return;
|
|
1045
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
1046
|
+
const { errorHandlerPlugin } = await import("./errorHandler-CIMkCOWm.mjs").then((n) => n.r);
|
|
1046
1047
|
/**
|
|
1047
1048
|
* DISCLOSURE DEFAULTS MERGE — they used to be REPLACED, and that was two bugs.
|
|
1048
1049
|
*
|
|
@@ -1266,7 +1267,7 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
1266
1267
|
let discoveryPath;
|
|
1267
1268
|
let discoveryYieldedZero = false;
|
|
1268
1269
|
if (resolvedResources === void 0 && config.resourceDir) {
|
|
1269
|
-
const { loadResources } = await import("./loadResources-
|
|
1270
|
+
const { loadResources } = await import("./loadResources-CblF7xt2.mjs").then((n) => n.n);
|
|
1270
1271
|
const { resolve, dirname } = await import("node:path");
|
|
1271
1272
|
const { fileURLToPath } = await import("node:url");
|
|
1272
1273
|
const rawDir = config.resourceDir;
|
|
@@ -1392,7 +1393,7 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1392
1393
|
if (fastify.hasDecorator("getScheduleStats")) throw new Error("[arc] modules declare scheduledJobs but schedulesPlugin was already registered manually. Configure their runner through arcPlugins.schedules so Arc can compose one schedule table.");
|
|
1393
1394
|
const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
|
|
1394
1395
|
const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
|
|
1395
|
-
const { default: schedulesPlugin } = await import("./schedules-
|
|
1396
|
+
const { default: schedulesPlugin } = await import("./schedules-BktJb3Y3.mjs").then((n) => n.r);
|
|
1396
1397
|
await fastify.register(schedulesPlugin, {
|
|
1397
1398
|
...scheduleOptions,
|
|
1398
1399
|
schedules: frozenJobs
|
|
@@ -1566,6 +1567,7 @@ function buildPlanOpts(rest, plan) {
|
|
|
1566
1567
|
*/
|
|
1567
1568
|
function buildRateLimitOpts(input) {
|
|
1568
1569
|
const { skipPaths, allowList, plan, ...bare } = input;
|
|
1570
|
+
applyScopeAwareHook(bare);
|
|
1569
1571
|
const rest = plan ? buildPlanOpts(bare, plan) : bare;
|
|
1570
1572
|
if (!skipPaths || skipPaths.length === 0) return allowList === void 0 ? rest : {
|
|
1571
1573
|
...rest,
|
|
@@ -1584,6 +1586,27 @@ function buildRateLimitOpts(input) {
|
|
|
1584
1586
|
allowList: combined
|
|
1585
1587
|
};
|
|
1586
1588
|
}
|
|
1589
|
+
/**
|
|
1590
|
+
* A scope-keyed generator must run AFTER authentication, so arc places the hook itself.
|
|
1591
|
+
*
|
|
1592
|
+
* `@fastify/rate-limit` defaults to `onRequest`; arc authenticates in a `preHandler`. A
|
|
1593
|
+
* generator from `createTenantKeyGenerator` therefore saw `PUBLIC_SCOPE` and returned the
|
|
1594
|
+
* caller's IP for EVERY request — and behind a proxy with `trustProxy` unset that is one
|
|
1595
|
+
* address, so a whole deployment shared a single bucket. Nothing threw: the limiter ran,
|
|
1596
|
+
* the host's key function ran, and the answer was silently wrong.
|
|
1597
|
+
*
|
|
1598
|
+
* Defaulted rather than merely warned, because the correct value is knowable here and the
|
|
1599
|
+
* failure is unobservable in production. An explicit `onRequest` is REFUSED rather than
|
|
1600
|
+
* honoured — it cannot do what the caller is asking for.
|
|
1601
|
+
*/
|
|
1602
|
+
function applyScopeAwareHook(bare) {
|
|
1603
|
+
if (!isScopeAwareKeyGenerator(bare.keyGenerator)) return;
|
|
1604
|
+
if (bare.hook === void 0) {
|
|
1605
|
+
bare.hook = "preHandler";
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
if (bare.hook === "onRequest") throw new Error("[arc] rateLimit.hook: 'onRequest' cannot be combined with a scope-aware keyGenerator (createTenantKeyGenerator). Authentication runs in a preHandler, so the generator would see PUBLIC_SCOPE and key every caller by IP — behind a proxy that is one bucket for the whole deployment. Use hook: 'preHandler' (arc's default for this generator), or supply a keyGenerator that derives identity without the scope.");
|
|
1609
|
+
}
|
|
1587
1610
|
function compilePathMatcher(patterns) {
|
|
1588
1611
|
const prefixes = [];
|
|
1589
1612
|
const exact = /* @__PURE__ */ new Set();
|
|
@@ -1626,8 +1649,9 @@ async function registerSecurityPlugins(fastify, config) {
|
|
|
1626
1649
|
timeWindow: "1 minute"
|
|
1627
1650
|
});
|
|
1628
1651
|
await fastify.register(rateLimit, rateLimitOpts);
|
|
1629
|
-
|
|
1630
|
-
|
|
1652
|
+
const rateLimitRecord = typeof rateLimitOpts === "object" && rateLimitOpts !== null ? rateLimitOpts : void 0;
|
|
1653
|
+
if (!(rateLimitRecord !== void 0 && (rateLimitRecord.store != null || rateLimitRecord.redis != null))) {
|
|
1654
|
+
if (config.runtime === "distributed") throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { redis: ioredisClient } (or { store: CustomStore }) or disable rate limiting: rateLimit: false");
|
|
1631
1655
|
else if (config.preset === "production") fastify.log.warn("Rate limiting is using in-memory store. In multi-instance deployments, each instance tracks limits independently. Configure a Redis store for distributed rate limiting.");
|
|
1632
1656
|
}
|
|
1633
1657
|
fastify.log.debug("Rate limiting enabled");
|
|
@@ -1738,6 +1762,17 @@ var createApp_exports = /* @__PURE__ */ __exportAll({
|
|
|
1738
1762
|
createApp: () => createApp,
|
|
1739
1763
|
resolveLoggerConfig: () => resolveLoggerConfig
|
|
1740
1764
|
});
|
|
1765
|
+
/**
|
|
1766
|
+
* The fail-loud switches, and what each one costs while OFF.
|
|
1767
|
+
*
|
|
1768
|
+
* Every one is a SPECIFIC instruction defeated by a GENERAL default, which is
|
|
1769
|
+
* why they are reported together rather than one at a time.
|
|
1770
|
+
*/
|
|
1771
|
+
const STRICT_ENV_FLAGS = [
|
|
1772
|
+
["ARC_STRICT_PERMISSIONS", "An ungated write route warns instead of failing boot."],
|
|
1773
|
+
["ARC_STRICT_QUERY_PARAMS", "An unknown filter key is DROPPED, which WIDENS the read."],
|
|
1774
|
+
["ARC_STRICT_IMMUTABLE_WRITES", "A PATCH of an immutable field returns 200, unchanged."]
|
|
1775
|
+
];
|
|
1741
1776
|
const MEMORY_STORE_NAMES = /* @__PURE__ */ new Set(["memory", "memory-cache"]);
|
|
1742
1777
|
/**
|
|
1743
1778
|
* Default redact paths layered into Fastify's pino logger when the host
|
|
@@ -1757,6 +1792,7 @@ const DEFAULT_LOGGER_REDACT_PATHS = [
|
|
|
1757
1792
|
"req.headers.authorization",
|
|
1758
1793
|
"req.headers[\"x-api-key\"]",
|
|
1759
1794
|
"req.headers[\"x-internal-api-key\"]",
|
|
1795
|
+
"req.headers[\"x-device-token\"]",
|
|
1760
1796
|
"req.headers.cookie",
|
|
1761
1797
|
"req.headers[\"set-cookie\"]",
|
|
1762
1798
|
"res.headers[\"set-cookie\"]",
|
|
@@ -1828,7 +1864,10 @@ function validateDistributedRuntime(options) {
|
|
|
1828
1864
|
});
|
|
1829
1865
|
}
|
|
1830
1866
|
const schedules = options.arcPlugins?.schedules;
|
|
1831
|
-
if (schedules && typeof schedules === "object" && schedules.enabled !== false && !schedules.lock
|
|
1867
|
+
if (schedules && typeof schedules === "object" && schedules.enabled !== false && !schedules.lock && !schedules.singleReplica) missing.push({
|
|
1868
|
+
key: "arcPlugins.schedules.lock",
|
|
1869
|
+
hint: "a LockAdapter for single-firer leases (e.g. createMongoLockAdapter) — or `arcPlugins.schedules.singleReplica: true` if exactly one replica arms schedules"
|
|
1870
|
+
});
|
|
1832
1871
|
if (missing.length > 0) {
|
|
1833
1872
|
const lines = missing.map((m) => ` • ${m.key.padEnd(20)} → ${m.hint}`).join("\n");
|
|
1834
1873
|
throw new Error(`[Arc] runtime: 'distributed' requires durable (Redis/MongoDB) adapters, not memory.
|
|
@@ -1953,6 +1992,7 @@ async function buildApp(options, loggerScope, onInstance) {
|
|
|
1953
1992
|
} }
|
|
1954
1993
|
});
|
|
1955
1994
|
onInstance(fastify);
|
|
1995
|
+
initRuntimeCapabilityRegistry(fastify);
|
|
1956
1996
|
if (resolvedLogger !== false) {
|
|
1957
1997
|
const writer = createPinoWriter(fastify.log);
|
|
1958
1998
|
loggerScope.writer = writer;
|
|
@@ -1967,6 +2007,22 @@ async function buildApp(options, loggerScope, onInstance) {
|
|
|
1967
2007
|
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
1968
2008
|
if (numericTrustProxy) fastify.log.warn(`[arc] trustProxy: ${String(config.trustProxy)} (a hop COUNT) trusts nothing on Fastify >= 5.12.1 — hop-count trust cannot validate the immediate peer, so it fails closed upstream to stop clients spoofing X-Forwarded-* with padded hops. \`request.ip\` is therefore the PROXY's address: rate-limit keys collapse onto one bucket and audit rows record the wrong client. Name the proxies instead — trustProxy: 'loopback', '10.0.0.0/8', a comma-separated list, or \`true\` if every upstream hop is genuinely trusted.`);
|
|
1969
2009
|
if (inheritedTrustProxyDefault) fastify.log.warn("[arc] preset: 'production' now defaults to trustProxy: false (fail-closed; was true before 2.24). If this app runs behind a load balancer or reverse proxy, request.ip is currently the PROXY's address — rate-limit keys collapse onto one bucket and audit logs record the wrong client. Set trustProxy explicitly: a hop count (trustProxy: 1), a CIDR allow-list ('10.0.0.0/8'), a resolver function, or false if clients truly connect directly.");
|
|
2010
|
+
/**
|
|
2011
|
+
* `preset: 'production'` REPORTS the fail-loud switches it cannot set.
|
|
2012
|
+
*
|
|
2013
|
+
* All three are read where the object is CONSTRUCTED — `defineResource` at
|
|
2014
|
+
* define time, `QueryParser` and `BodySanitizer` in their constructors — and a
|
|
2015
|
+
* host imports its resources before it calls `createApp`. So setting
|
|
2016
|
+
* `process.env` here would be too late for everything already built: wired to
|
|
2017
|
+
* read, doing nothing. Reporting cannot fail that way.
|
|
2018
|
+
*
|
|
2019
|
+
* Set them in the process entrypoint, BEFORE the first resource import, with
|
|
2020
|
+
* `??=` so an operator's explicit `false` still wins.
|
|
2021
|
+
*/
|
|
2022
|
+
if (config.preset === "production") {
|
|
2023
|
+
const off = STRICT_ENV_FLAGS.filter(([name]) => process.env[name] !== "true");
|
|
2024
|
+
if (off.length > 0) fastify.log.warn(`[arc] preset: 'production' with fail-loud OFF: ${off.map(([n]) => n).join(", ")}. ${off.map(([, what]) => what).join(" ")} Set them in the entrypoint BEFORE any resource import — they are read when resources, parsers and sanitizers are CONSTRUCTED, so setting them later is silently too late. Per-resource options still override.`);
|
|
2025
|
+
}
|
|
1970
2026
|
fastify.removeContentTypeParser("application/json");
|
|
1971
2027
|
fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
1972
2028
|
try {
|
|
@@ -1999,7 +2055,7 @@ async function buildApp(options, loggerScope, onInstance) {
|
|
|
1999
2055
|
await registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers);
|
|
2000
2056
|
await registerResources(fastify, config, resolvedModules);
|
|
2001
2057
|
if (config.replyHelpers) {
|
|
2002
|
-
const { replyHelpersPlugin } = await import("./replyHelpers-
|
|
2058
|
+
const { replyHelpersPlugin } = await import("./replyHelpers-DPHsdw-5.mjs").then((n) => n.n);
|
|
2003
2059
|
await fastify.register(replyHelpersPlugin);
|
|
2004
2060
|
}
|
|
2005
2061
|
if (config.serializeBigInt) {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
|
|
2
|
-
import { It as Authenticator } from "./index-
|
|
2
|
+
import { It as Authenticator } from "./index-DHbyPo5j.mjs";
|
|
3
3
|
import { r as CacheStore } from "./interface-BOk3YnTz.mjs";
|
|
4
4
|
import { n as ElevationOptions } from "./elevation-BXCfVcNG.mjs";
|
|
5
|
-
import { a as EventTransport } from "./EventTransport-
|
|
5
|
+
import { a as EventTransport } from "./EventTransport-CbUKDzZZ.mjs";
|
|
6
6
|
import { t as ExternalOpenApiPaths } from "./externalPaths-CmGOxOtD.mjs";
|
|
7
7
|
import { r as QueryCachePluginOptions } from "./queryCachePlugin-BzqxconG.mjs";
|
|
8
|
-
import { t as ErrorHandlerOptions } from "./errorHandler
|
|
9
|
-
import { l as ResourceLike, m as SchedulesPluginOptions, n as ArcModuleInput, v as HealthOptions } from "./types-
|
|
10
|
-
import "./index-
|
|
11
|
-
import { t as EventPluginOptions } from "./eventPlugin-
|
|
12
|
-
import { g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions } from "./versioning-
|
|
8
|
+
import { t as ErrorHandlerOptions } from "./errorHandler--4rSMMvq.mjs";
|
|
9
|
+
import { l as ResourceLike, m as SchedulesPluginOptions, n as ArcModuleInput, v as HealthOptions } from "./types-DfVDvkdP.mjs";
|
|
10
|
+
import "./index-CFfLKMH4.mjs";
|
|
11
|
+
import { t as EventPluginOptions } from "./eventPlugin-B52abJ8W.mjs";
|
|
12
|
+
import { b as GracefulShutdownOptions, g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions } from "./versioning-D6JIK8Tk.mjs";
|
|
13
13
|
import { r as IdempotencyStore } from "./interface-DEWNOMza.mjs";
|
|
14
14
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
|
|
15
15
|
//#region src/factory/types/assets.d.ts
|
|
@@ -652,8 +652,13 @@ interface CreateAppOptions {
|
|
|
652
652
|
* ```
|
|
653
653
|
*/
|
|
654
654
|
health?: boolean | HealthOptions;
|
|
655
|
-
/**
|
|
656
|
-
|
|
655
|
+
/**
|
|
656
|
+
* Graceful shutdown handling (default: true). Pass `GracefulShutdownOptions`
|
|
657
|
+
* to configure it inline — in particular `drainDelayMs`, the lame-duck
|
|
658
|
+
* window behind a load balancer (readiness fails first, the server keeps
|
|
659
|
+
* serving until the LB has deregistered the instance, then closes).
|
|
660
|
+
*/
|
|
661
|
+
gracefulShutdown?: boolean | GracefulShutdownOptions;
|
|
657
662
|
/** Emit events for CRUD operations (default: true) */
|
|
658
663
|
emitEvents?: boolean;
|
|
659
664
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
|
|
3
|
-
import { l as PermissionCheck } from "../base-
|
|
4
|
-
import { Y as PresetResult } from "../index-
|
|
3
|
+
import { l as PermissionCheck } from "../base-CxrV1LmG.mjs";
|
|
4
|
+
import { Y as PresetResult } from "../index-DHbyPo5j.mjs";
|
|
5
5
|
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-Cx9Sz3Ou.mjs";
|
|
6
6
|
//#region src/presets/filesUpload.d.ts
|
|
7
7
|
interface FilesUploadPresetRoutes {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { i as NotFoundError, u as ValidationError } from "../errors-
|
|
3
|
-
import { c as getOrgId, g as getUserId } from "../types-
|
|
4
|
-
import { B as requireAuth, F as allowPublic } from "../permissions-
|
|
5
|
-
import { t as multipartBody } from "../multipartBody-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { i as NotFoundError, u as ValidationError } from "../errors-C_YhELhe.mjs";
|
|
3
|
+
import { c as getOrgId, g as getUserId } from "../types-CDy3-zw1.mjs";
|
|
4
|
+
import { B as requireAuth, F as allowPublic } from "../permissions-DiHXy8Ck.mjs";
|
|
5
|
+
import { t as multipartBody } from "../multipartBody-C92aJ8am.mjs";
|
|
6
6
|
//#region src/presets/filesUpload.ts
|
|
7
7
|
const DEFAULT_FIELD_NAME = "file";
|
|
8
8
|
const DEFAULT_MAX_FILE_SIZE = 10485760;
|
package/dist/presets/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { t as AnyRecord } from "../base-
|
|
3
|
-
import { Fn as IControllerResponse, In as IRequestContext, U as ResourceConfig, Y as PresetResult, d as PaginationResult } from "../index-
|
|
2
|
+
import { t as AnyRecord } from "../base-CxrV1LmG.mjs";
|
|
3
|
+
import { Fn as IControllerResponse, In as IRequestContext, U as ResourceConfig, Y as PresetResult, d as PaginationResult } from "../index-DHbyPo5j.mjs";
|
|
4
4
|
import { FilesUploadPresetOptions, FilesUploadPresetPermissions, FilesUploadPresetRoutes, filesUploadPreset } from "./filesUpload.mjs";
|
|
5
5
|
import { MultiTenantOptions, TenantFieldSpec, multiTenantPreset } from "./multiTenant.mjs";
|
|
6
6
|
import { SearchHandler, SearchPresetOptions, SearchRouteConfig, searchPreset } from "./search.mjs";
|
package/dist/presets/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
2
|
import { multiTenantPreset } from "./multiTenant.mjs";
|
|
3
|
-
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-
|
|
3
|
+
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-BdlugYTc.mjs";
|
|
4
4
|
import { filesUploadPreset } from "./filesUpload.mjs";
|
|
5
5
|
import { searchPreset } from "./search.mjs";
|
|
6
6
|
export { applyPresets, auditedPreset, bulkPreset, filesUploadPreset, flexibleMultiTenantPreset, getAvailablePresets, getPreset, multiTenantPreset, ownedByUserPreset, registerPreset, searchPreset, slugLookupPreset, softDeletePreset, treePreset };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { X as CrudRouteKey, Y as PresetResult } from "../index-
|
|
2
|
+
import { X as CrudRouteKey, Y as PresetResult } from "../index-DHbyPo5j.mjs";
|
|
3
3
|
//#region src/presets/multiTenant.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* One tenant dimension for multi-field filtering. Discriminated by source:
|
|
@@ -65,6 +65,43 @@ interface MultiTenantOptions {
|
|
|
65
65
|
* multiTenantPreset({ allowPublic: ['list', 'get'] })
|
|
66
66
|
*/
|
|
67
67
|
allowPublic?: CrudRouteKey[];
|
|
68
|
+
/**
|
|
69
|
+
* Reads that are NOT scoped to a tenant at all — a marketplace listing.
|
|
70
|
+
*
|
|
71
|
+
* `allowPublic` answers "may an anonymous caller reach this route?". That is a different
|
|
72
|
+
* question from "is this listing scoped to a tenant?", and until this option there was no way
|
|
73
|
+
* to answer the second one with "no". An `allowPublic` route still filters by the caller's
|
|
74
|
+
* organization whenever they have one, and a signed-in user of a multi-org product ALWAYS has
|
|
75
|
+
* one, because choosing an organization writes it onto the session. So a cross-tenant catalog
|
|
76
|
+
* silently became single-tenant the moment one of its own sellers browsed it: the same URL,
|
|
77
|
+
* with no header and no query parameter to explain the difference, returned a different world
|
|
78
|
+
* depending on who was asking.
|
|
79
|
+
*
|
|
80
|
+
* The only escape was to drop this preset from the resource entirely, which also drops tenant
|
|
81
|
+
* INJECTION on create/update — the thing that stops a member moving a document into another
|
|
82
|
+
* organization. Trading a write guarantee for a read behaviour is not a real choice.
|
|
83
|
+
*
|
|
84
|
+
* Listed routes get no tenant middleware: no filter, and nothing stashed for the repo layer.
|
|
85
|
+
* Everything else keeps its filter, so writes are unaffected.
|
|
86
|
+
*
|
|
87
|
+
* READS ONLY, by type. A cross-tenant `create` or `update` is not a listing, it is an escape
|
|
88
|
+
* from tenancy, and it should never be one option away by accident.
|
|
89
|
+
*
|
|
90
|
+
* THIS DOES NOT AUTHORIZE ANYTHING. Removing the tenant filter widens what the query can
|
|
91
|
+
* return, so the resource owes a row-level `policy` from its permission check saying what a
|
|
92
|
+
* stranger may see (`{ status: 'active' }`, `{ visibility: 'public' }`). Declaring this without
|
|
93
|
+
* one publishes every row in every tenant.
|
|
94
|
+
*
|
|
95
|
+
* @default []
|
|
96
|
+
* @example
|
|
97
|
+
* // A public catalog: anyone may browse every seller's LIVE products,
|
|
98
|
+
* // while create/update/delete stay pinned to the caller's own shop.
|
|
99
|
+
* multiTenantPreset({
|
|
100
|
+
* allowPublic: ['list', 'get'],
|
|
101
|
+
* crossTenant: ['list', 'get'],
|
|
102
|
+
* })
|
|
103
|
+
*/
|
|
104
|
+
crossTenant?: readonly Extract<CrudRouteKey, "list" | "get">[];
|
|
68
105
|
}
|
|
69
106
|
declare function multiTenantPreset(options?: MultiTenantOptions): PresetResult;
|
|
70
107
|
//#endregion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import "../constants-
|
|
3
|
-
import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import "../constants-BxLgNFF7.mjs";
|
|
3
|
+
import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-CDy3-zw1.mjs";
|
|
4
4
|
//#region src/presets/multiTenant.ts
|
|
5
5
|
/**
|
|
6
6
|
* Resolve a single TenantFieldSpec against the current scope.
|
|
@@ -162,7 +162,7 @@ function createTenantInjection(specs) {
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
function multiTenantPreset(options = {}) {
|
|
165
|
-
const { tenantField, tenantFields, allowPublic = [] } = options;
|
|
165
|
+
const { tenantField, tenantFields, allowPublic = [], crossTenant = [] } = options;
|
|
166
166
|
if (tenantField !== void 0 && tenantFields !== void 0) throw new Error("multiTenantPreset: pass either `tenantField` (single-field) or `tenantFields` (multi-field), not both");
|
|
167
167
|
const specs = tenantFields ?? [{
|
|
168
168
|
field: tenantField ?? "organizationId",
|
|
@@ -173,6 +173,24 @@ function multiTenantPreset(options = {}) {
|
|
|
173
173
|
const flexibleTenantFilter = createFlexibleTenantFilter(specs);
|
|
174
174
|
const tenantInjection = createTenantInjection(specs);
|
|
175
175
|
const getFilter = (route) => allowPublic.includes(route) ? flexibleTenantFilter : strictTenantFilter;
|
|
176
|
+
/**
|
|
177
|
+
* A cross-tenant read MARKS the request rather than simply skipping the filter.
|
|
178
|
+
*
|
|
179
|
+
* Skipping is not enough, because this middleware is only the first of four places that scope a
|
|
180
|
+
* read, and the other three each re-derive the organization from the request scope on their own:
|
|
181
|
+
* `QueryResolver` adds the tenant filter for `list`, `buildTenantRepoOptions` forwards it to the
|
|
182
|
+
* repository as tenant context, and `checkOrgScope` re-checks it post-fetch on `get`. Removing
|
|
183
|
+
* one of four changes nothing observable, which is exactly what made this look unfixable from the
|
|
184
|
+
* preset: the listing stayed single-tenant no matter what the preset decided.
|
|
185
|
+
*
|
|
186
|
+
* So the decision travels on the request instead, and each of those layers honours it. The
|
|
187
|
+
* repository layer needs no change of its own — kits already accept `bypassTenant`, which is the
|
|
188
|
+
* same door elevated cross-tenant reads have always used.
|
|
189
|
+
*/
|
|
190
|
+
const markCrossTenantRead = async (request) => {
|
|
191
|
+
request._crossTenantRead = true;
|
|
192
|
+
};
|
|
193
|
+
const readMiddleware = (route) => crossTenant.includes(route) ? [markCrossTenantRead] : [getFilter(route)];
|
|
176
194
|
const fieldRules = {};
|
|
177
195
|
for (const spec of specs) fieldRules[spec.field] = {
|
|
178
196
|
systemManaged: true,
|
|
@@ -182,8 +200,8 @@ function multiTenantPreset(options = {}) {
|
|
|
182
200
|
name: "multiTenant",
|
|
183
201
|
schemaOptions: { fieldRules },
|
|
184
202
|
middlewares: {
|
|
185
|
-
list:
|
|
186
|
-
get:
|
|
203
|
+
list: readMiddleware("list"),
|
|
204
|
+
get: readMiddleware("get"),
|
|
187
205
|
create: [tenantInjection],
|
|
188
206
|
update: [getFilter("update"), tenantInjection],
|
|
189
207
|
delete: [getFilter("delete")],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { l as PermissionCheck } from "../base-
|
|
3
|
-
import { At as RouteDefinition, Y as PresetResult, jn as ControllerHandler, jt as RouteMcpConfig } from "../index-
|
|
2
|
+
import { l as PermissionCheck } from "../base-CxrV1LmG.mjs";
|
|
3
|
+
import { At as RouteDefinition, Y as PresetResult, jn as ControllerHandler, jt as RouteMcpConfig } from "../index-DHbyPo5j.mjs";
|
|
4
4
|
//#region src/presets/search.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* Search Preset — backend-agnostic search / vector / embed routes
|
package/dist/presets/search.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { B as requireAuth, F as allowPublic } from "../permissions-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { B as requireAuth, F as allowPublic } from "../permissions-DiHXy8Ck.mjs";
|
|
3
3
|
//#region src/presets/search.ts
|
|
4
4
|
const BUILTINS = [
|
|
5
5
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { l as UnauthorizedError } from "./errors-
|
|
3
|
-
import { b as isElevated, n as PUBLIC_SCOPE } from "./types-
|
|
4
|
-
import { B as requireAuth, F as allowPublic, U as requireRoles } from "./permissions-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { l as UnauthorizedError } from "./errors-C_YhELhe.mjs";
|
|
3
|
+
import { b as isElevated, n as PUBLIC_SCOPE } from "./types-CDy3-zw1.mjs";
|
|
4
|
+
import { B as requireAuth, F as allowPublic, U as requireRoles } from "./permissions-DiHXy8Ck.mjs";
|
|
5
5
|
import { multiTenantPreset } from "./presets/multiTenant.mjs";
|
|
6
6
|
//#region src/presets/ownedByUser.ts
|
|
7
7
|
/**
|