@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
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-BxLgNFF7.mjs";
|
|
3
3
|
import { arcLog } from "./logger/index.mjs";
|
|
4
|
-
import { a as conjoinPolicyFilters } from "./authorizationDecision-
|
|
5
|
-
import { d as createDomainError, f as createError, i as NotFoundError, p as isArcError, r as ForbiddenError } from "./errors-
|
|
6
|
-
import { n as getUserRoles, t as getUserId } from "./userHelpers-
|
|
7
|
-
import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-
|
|
8
|
-
import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-
|
|
9
|
-
import { t as buildQueryKey } from "./keys-
|
|
10
|
-
import { t as transactionContext } from "./transactionContext-
|
|
11
|
-
import {
|
|
12
|
-
import { a as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-
|
|
4
|
+
import { a as conjoinPolicyFilters } from "./authorizationDecision-2YY_Kaj1.mjs";
|
|
5
|
+
import { d as createDomainError, f as createError, i as NotFoundError, p as isArcError, r as ForbiddenError, u as ValidationError } from "./errors-C_YhELhe.mjs";
|
|
6
|
+
import { n as getUserRoles, t as getUserId } from "./userHelpers-DDMOZ_Pm.mjs";
|
|
7
|
+
import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-CDy3-zw1.mjs";
|
|
8
|
+
import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-BiOnyflP.mjs";
|
|
9
|
+
import { t as buildQueryKey } from "./keys-Cmv4cBRa.mjs";
|
|
10
|
+
import { t as transactionContext } from "./transactionContext-DCWMI6hV.mjs";
|
|
11
|
+
import { D as ArcQueryParser, j as scheduleBackground } from "./utils-B-zj_b__.mjs";
|
|
12
|
+
import { a as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-BTyPLHK0.mjs";
|
|
13
13
|
import { policyRecordToFilter } from "@classytic/repo-core/filter";
|
|
14
14
|
import { retryingTransaction } from "@classytic/repo-core/repository";
|
|
15
15
|
import { isProductionEnv } from "@classytic/primitives/environment";
|
|
@@ -179,6 +179,7 @@ var AccessControl = class {
|
|
|
179
179
|
*/
|
|
180
180
|
checkOrgScope(item, arcContext) {
|
|
181
181
|
if (!this.tenantField) return true;
|
|
182
|
+
if (arcContext?._crossTenantRead === true) return true;
|
|
182
183
|
const scope = arcContext?._scope;
|
|
183
184
|
const orgId = scope ? getOrgId(scope) : void 0;
|
|
184
185
|
if (!item || !orgId) return true;
|
|
@@ -423,6 +424,11 @@ function buildTenantRepoOptions(req, tenantField, arcContext) {
|
|
|
423
424
|
if (cached) return cached;
|
|
424
425
|
const out = {};
|
|
425
426
|
const scope = arcContext?._scope;
|
|
427
|
+
if (arcContext?._crossTenantRead === true) {
|
|
428
|
+
out.bypassTenant = true;
|
|
429
|
+
req._tenantRepoOptions = out;
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
426
432
|
if (tenantField) {
|
|
427
433
|
const orgId = scope ? getOrgId(scope) : void 0;
|
|
428
434
|
if (orgId) out[tenantField] = orgId;
|
|
@@ -650,7 +656,7 @@ async function executeAfterHook(ctx, op, data, meta) {
|
|
|
650
656
|
* Every mixin used to spell out its own narrowing cast inline:
|
|
651
657
|
*
|
|
652
658
|
* ```ts
|
|
653
|
-
* const repo = this.repository as
|
|
659
|
+
* const repo = this.repository as {
|
|
654
660
|
* getTree?: (options?: unknown) => Promise<AnyRecord[]>;
|
|
655
661
|
* };
|
|
656
662
|
* ```
|
|
@@ -717,7 +723,7 @@ async function runDistinctVerb(repository, field, resolveScope) {
|
|
|
717
723
|
const repo = withRepoFeatures(repository);
|
|
718
724
|
if (typeof repo.distinct !== "function") throw createError(501, "_distinct is not supported: the resource's storage adapter does not implement repo.distinct()");
|
|
719
725
|
const { filter, options } = resolveScope();
|
|
720
|
-
return await repo.distinct(field, filter, options);
|
|
726
|
+
return { values: await repo.distinct(field, filter, options) };
|
|
721
727
|
}
|
|
722
728
|
/**
|
|
723
729
|
* `?_exists=true` → `repo.exists(filter)`. Returns the RAW repo result —
|
|
@@ -789,6 +795,27 @@ function noteDeniedJoins(kind) {
|
|
|
789
795
|
notedDeniedJoins = true;
|
|
790
796
|
log.warn(`client requested ${kind} but no allowlist is configured — DENIED (2.24 default flip; it was allow-all). Set schemaOptions.query.allowedPopulate / allowedLookups to enable client-driven joins. (dev-only note, shown once)`);
|
|
791
797
|
}
|
|
798
|
+
/**
|
|
799
|
+
* A `$lookup` reaches ANOTHER collection, and the tenant filter arc conjoins applies to the
|
|
800
|
+
* BASE one only. The joined side carries whatever predicate the lookup itself names and
|
|
801
|
+
* nothing more — no tenant scope, no soft-delete filter — so a resource that opens
|
|
802
|
+
* `allowedLookups` can return rows from a collection the caller could not query directly.
|
|
803
|
+
*
|
|
804
|
+
* Unreachable by default (2.24 denies `lookup=` with no allowlist) and no resource in this
|
|
805
|
+
* fleet configures one, so this is a WARNING at the moment a deployment first opts in
|
|
806
|
+
* rather than machinery for a door nobody has opened. Warned once per collection set: the
|
|
807
|
+
* decision is made when the allowlist is written, not per request.
|
|
808
|
+
*
|
|
809
|
+
* Scope the join with the lookup's own `where`, which mongokit compiles into a `$match`
|
|
810
|
+
* inside the lookup pipeline.
|
|
811
|
+
*/
|
|
812
|
+
const notedUnscopedJoins = /* @__PURE__ */ new Set();
|
|
813
|
+
function noteUnscopedJoin(lookups) {
|
|
814
|
+
const key = lookups.map((l) => l.from).filter((f) => typeof f === "string").sort().join(",");
|
|
815
|
+
if (key.length === 0 || notedUnscopedJoins.has(key)) return;
|
|
816
|
+
notedUnscopedJoins.add(key);
|
|
817
|
+
log.warn(`client-driven $lookup on [${key}] — the JOINED side carries no tenant or soft-delete filter (arc scopes the base collection only). Narrow each lookup with its own \`where\`, or the join can surface rows the caller cannot query directly. (shown once)`);
|
|
818
|
+
}
|
|
792
819
|
var QueryResolver = class {
|
|
793
820
|
queryParser;
|
|
794
821
|
maxLimit;
|
|
@@ -912,6 +939,12 @@ var QueryResolver = class {
|
|
|
912
939
|
query = rest;
|
|
913
940
|
}
|
|
914
941
|
const parsed = this.queryParser.parse(query);
|
|
942
|
+
/**
|
|
943
|
+
* Client-supplied keys only, and BEFORE the policy conjunction below: trusted policy
|
|
944
|
+
* legitimately filters on hidden fields (`requireOwnership('userId')` emits exactly that),
|
|
945
|
+
* so checking the conjoined result would reject the framework's own restrictions.
|
|
946
|
+
*/
|
|
947
|
+
this.assertNoBlockedReadKeys(parsed.filters, parsed.sort);
|
|
915
948
|
const arcContext = meta ?? req.metadata;
|
|
916
949
|
const limit = Math.min(Math.max(1, parsed.limit || this.defaultLimit), this.maxLimit);
|
|
917
950
|
const page = parsed.after ? void 0 : parsed.page ? Math.max(1, parsed.page) : 1;
|
|
@@ -921,7 +954,7 @@ var QueryResolver = class {
|
|
|
921
954
|
let filters = conjoinPolicyFilters(parsed.filters, policyFilters);
|
|
922
955
|
const scope = arcContext?._scope;
|
|
923
956
|
const orgId = scope ? getOrgId(scope) : void 0;
|
|
924
|
-
if (this.tenantField && orgId && !policyFilters?.[this.tenantField]) filters = conjoinPolicyFilters(filters, { [this.tenantField]: orgId });
|
|
957
|
+
if (this.tenantField && orgId && !arcContext?._crossTenantRead && !policyFilters?.[this.tenantField]) filters = conjoinPolicyFilters(filters, { [this.tenantField]: orgId });
|
|
925
958
|
const portableFilters = toRepositoryFilter(filters);
|
|
926
959
|
return {
|
|
927
960
|
page,
|
|
@@ -950,20 +983,14 @@ var QueryResolver = class {
|
|
|
950
983
|
if (!select) return exclusion();
|
|
951
984
|
if (typeof select === "object" && !Array.isArray(select)) {
|
|
952
985
|
const sanitized = {};
|
|
953
|
-
for (const [field, val] of Object.entries(select)) if (!
|
|
986
|
+
for (const [field, val] of Object.entries(select)) if (!this.isBlockedProjection(field, blockedFields)) sanitized[field] = val;
|
|
954
987
|
return Object.keys(sanitized).length > 0 ? sanitized : exclusion();
|
|
955
988
|
}
|
|
956
989
|
if (Array.isArray(select)) {
|
|
957
|
-
const sanitized = select.filter((f) =>
|
|
958
|
-
const fieldName = f.replace(/^-/, "");
|
|
959
|
-
return !blockedFields.includes(fieldName);
|
|
960
|
-
});
|
|
990
|
+
const sanitized = select.filter((f) => !this.isBlockedProjection(f, blockedFields));
|
|
961
991
|
return sanitized.length > 0 ? sanitized : exclusion();
|
|
962
992
|
}
|
|
963
|
-
const sanitized = select.split(/[\s,]+/).filter(Boolean).filter((f) =>
|
|
964
|
-
const fieldName = f.replace(/^-/, "");
|
|
965
|
-
return !blockedFields.includes(fieldName);
|
|
966
|
-
});
|
|
993
|
+
const sanitized = select.split(/[\s,]+/).filter(Boolean).filter((f) => !this.isBlockedProjection(f, blockedFields));
|
|
967
994
|
return sanitized.length > 0 ? sanitized.join(" ") : exclusion();
|
|
968
995
|
}
|
|
969
996
|
/** Sanitize populate fields */
|
|
@@ -1011,6 +1038,7 @@ var QueryResolver = class {
|
|
|
1011
1038
|
if (!allowedLookups.includes(lookup.from)) return false;
|
|
1012
1039
|
return true;
|
|
1013
1040
|
});
|
|
1041
|
+
if (sanitized.length > 0) noteUnscopedJoin(sanitized);
|
|
1014
1042
|
return sanitized.length > 0 ? sanitized : void 0;
|
|
1015
1043
|
}
|
|
1016
1044
|
/**
|
|
@@ -1023,6 +1051,68 @@ var QueryResolver = class {
|
|
|
1023
1051
|
const blocked = collectReadBlockedFields(schemaOptions);
|
|
1024
1052
|
return blocked ? Array.from(blocked) : [];
|
|
1025
1053
|
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Is this projection path blocked — by its own name, or by a hidden ANCESTOR?
|
|
1056
|
+
*
|
|
1057
|
+
* `hidden: { secret: true }` hides the object, and an exact-name match left its children
|
|
1058
|
+
* reachable: `?select=secret.value` projected the very field the rule exists to withhold.
|
|
1059
|
+
* A rule on a parent covers everything under it, which is what "hidden" already means to
|
|
1060
|
+
* whoever wrote it.
|
|
1061
|
+
*/
|
|
1062
|
+
isBlockedProjection(path, blockedFields) {
|
|
1063
|
+
const field = path.replace(/^-/, "");
|
|
1064
|
+
return blockedFields.some((blocked) => field === blocked || field.startsWith(`${blocked}.`));
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Refuse a read that FILTERS or SORTS on a `hidden` field.
|
|
1068
|
+
*
|
|
1069
|
+
* `hidden` was enforced on the surfaces that RETURN a value — the response, `select=`,
|
|
1070
|
+
* `_distinct`, aggregations — but a filter key never returns the field, it INTERROGATES it:
|
|
1071
|
+
* `?passwordHash[like]=^ab` narrows the result set, so the row count answers a question about
|
|
1072
|
+
* a value the caller may not read. A resource with no `allowedFilterFields` was a blind
|
|
1073
|
+
* existence oracle over every hidden field it declared.
|
|
1074
|
+
*
|
|
1075
|
+
* Rejected rather than dropped, unlike `select`. Dropping a projection narrows what comes
|
|
1076
|
+
* back; dropping a filter WIDENS the rows, which is the silent-permissive shape this
|
|
1077
|
+
* framework fails loudly on everywhere else.
|
|
1078
|
+
*
|
|
1079
|
+
* Compound keys (`$or`, `$and`) are walked, because the offending key can be nested inside
|
|
1080
|
+
* one. A dotted path is blocked by its own name or by a hidden ancestor, so a rule on
|
|
1081
|
+
* `secret` also covers `secret.value`.
|
|
1082
|
+
*/
|
|
1083
|
+
assertNoBlockedReadKeys(filters, sort) {
|
|
1084
|
+
const blocked = collectReadBlockedFields(this.schemaOptions);
|
|
1085
|
+
if (!blocked) return;
|
|
1086
|
+
const offenders = /* @__PURE__ */ new Set();
|
|
1087
|
+
const isBlocked = (key) => {
|
|
1088
|
+
if (blocked.has(key)) return true;
|
|
1089
|
+
const dot = key.indexOf(".");
|
|
1090
|
+
return dot > 0 && blocked.has(key.slice(0, dot));
|
|
1091
|
+
};
|
|
1092
|
+
const walk = (node, depth) => {
|
|
1093
|
+
if (depth > 8 || !node || typeof node !== "object") return;
|
|
1094
|
+
if (Array.isArray(node)) {
|
|
1095
|
+
for (const item of node) walk(item, depth + 1);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
for (const [key, value] of Object.entries(node)) {
|
|
1099
|
+
if (key.startsWith("$")) {
|
|
1100
|
+
walk(value, depth + 1);
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
if (isBlocked(key)) offenders.add(key);
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
walk(filters, 0);
|
|
1107
|
+
const sortKeys = typeof sort === "string" ? sort.split(",").map((s) => s.trim().replace(/^-/, "")).filter(Boolean) : Object.keys(sort ?? {});
|
|
1108
|
+
for (const key of sortKeys) if (isBlocked(key)) offenders.add(key);
|
|
1109
|
+
if (offenders.size === 0) return;
|
|
1110
|
+
const fields = [...offenders];
|
|
1111
|
+
throw new ValidationError(`Cannot filter or sort on hidden field(s): ${fields.join(", ")}`, fields.map((field) => ({
|
|
1112
|
+
field,
|
|
1113
|
+
message: "Field is not readable"
|
|
1114
|
+
})));
|
|
1115
|
+
}
|
|
1026
1116
|
};
|
|
1027
1117
|
//#endregion
|
|
1028
1118
|
//#region src/core/BaseCrudController.ts
|
|
@@ -1550,7 +1640,7 @@ var BaseCrudController = class {
|
|
|
1550
1640
|
}
|
|
1551
1641
|
async list(req) {
|
|
1552
1642
|
const dispatch = this.dispatchResourceVerb(req);
|
|
1553
|
-
if (dispatch) return dispatch;
|
|
1643
|
+
if (dispatch !== null) return dispatch;
|
|
1554
1644
|
const options = this.queryResolver.resolve(req, this.meta(req));
|
|
1555
1645
|
const cached = await this.withListCache(req, options);
|
|
1556
1646
|
if (cached) return cached;
|
|
@@ -42,6 +42,30 @@ interface MemoryEventTransportOptions {
|
|
|
42
42
|
* QUEUE, not of the work.
|
|
43
43
|
*/
|
|
44
44
|
handlerDispatch?: "sequential" | "parallel";
|
|
45
|
+
/**
|
|
46
|
+
* Called when a published event matched ZERO subscribers.
|
|
47
|
+
*
|
|
48
|
+
* Silence is the default because a fire-and-forget bus legitimately carries
|
|
49
|
+
* events nobody wants. It stops being legitimate the moment an OUTBOX is
|
|
50
|
+
* wired: the relay acknowledges a row it delivered to nobody, so the numbers
|
|
51
|
+
* report success and the instruction the event carried was never carried
|
|
52
|
+
* out. A host that expects its events to be consumed passes this and finds
|
|
53
|
+
* out.
|
|
54
|
+
*
|
|
55
|
+
* Runs inside the publish's work scope, after handler resolution and before
|
|
56
|
+
* the (empty) dispatch. It must not throw — a reporter that fails must not
|
|
57
|
+
* decide whether an event was published.
|
|
58
|
+
*
|
|
59
|
+
* MEMORY TRANSPORT ONLY, and deliberately so — this is not an oversight to be
|
|
60
|
+
* fixed by adding it to the Redis transports. In-process, "no handler
|
|
61
|
+
* matched" IS "nobody consumed it": this process is the whole bus. On a
|
|
62
|
+
* broker, no LOCAL handler matched says nothing about consumption — another
|
|
63
|
+
* replica, another consumer group, or a consumer not yet started may own the
|
|
64
|
+
* event, and firing there would report a healthy fan-out as a fault every
|
|
65
|
+
* time work is routed away from this node. The broker's own pending/lag
|
|
66
|
+
* metrics answer that question; this one cannot.
|
|
67
|
+
*/
|
|
68
|
+
onUnroutedEvent?: (event: DomainEvent) => void;
|
|
45
69
|
/**
|
|
46
70
|
* Cap on handlers running at once under `'parallel'`. Default: unlimited.
|
|
47
71
|
*
|
|
@@ -69,8 +93,17 @@ declare class MemoryEventTransport implements EventTransport {
|
|
|
69
93
|
private onHandlerError;
|
|
70
94
|
private handlerDispatch;
|
|
71
95
|
private handlerConcurrency;
|
|
96
|
+
private onUnroutedEvent;
|
|
72
97
|
constructor(options?: MemoryEventTransportOptions);
|
|
98
|
+
/**
|
|
99
|
+
* One work scope per publish, around the WHOLE handler set — so every
|
|
100
|
+
* handler of this event, parallel or not, shares one `requestScopedCache()`
|
|
101
|
+
* and one correlation id. Inherits an active request scope (a live
|
|
102
|
+
* in-request dispatch keeps the request's reads); a relay- or job-driven
|
|
103
|
+
* publish gets its own.
|
|
104
|
+
*/
|
|
73
105
|
publish(event: DomainEvent): Promise<void>;
|
|
106
|
+
private dispatch;
|
|
74
107
|
/**
|
|
75
108
|
* Reference `publishMany` implementation — delegates to `publish()` in order.
|
|
76
109
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { r as workSeedFromEvent, t as runWorkScope } from "./workScope-BhsWde4k.mjs";
|
|
2
3
|
import { createEvent, matchEventPattern } from "@classytic/primitives/events";
|
|
3
4
|
//#region src/events/EventTransport.ts
|
|
4
5
|
/**
|
|
@@ -81,17 +82,36 @@ var MemoryEventTransport = class {
|
|
|
81
82
|
onHandlerError;
|
|
82
83
|
handlerDispatch;
|
|
83
84
|
handlerConcurrency;
|
|
85
|
+
onUnroutedEvent;
|
|
84
86
|
constructor(options) {
|
|
85
87
|
this.logger = options?.logger ?? console;
|
|
86
88
|
this.onHandlerError = options?.onHandlerError ?? "log";
|
|
87
89
|
this.handlerDispatch = options?.handlerDispatch ?? "sequential";
|
|
90
|
+
this.onUnroutedEvent = options?.onUnroutedEvent;
|
|
88
91
|
const cap = options?.handlerConcurrency;
|
|
89
92
|
if (cap !== void 0 && (!Number.isInteger(cap) || cap < 1)) throw new TypeError(`MemoryEventTransport: handlerConcurrency must be a positive integer, received ${String(cap)}`);
|
|
90
93
|
this.handlerConcurrency = cap ?? Number.POSITIVE_INFINITY;
|
|
91
94
|
}
|
|
92
|
-
|
|
95
|
+
/**
|
|
96
|
+
* One work scope per publish, around the WHOLE handler set — so every
|
|
97
|
+
* handler of this event, parallel or not, shares one `requestScopedCache()`
|
|
98
|
+
* and one correlation id. Inherits an active request scope (a live
|
|
99
|
+
* in-request dispatch keeps the request's reads); a relay- or job-driven
|
|
100
|
+
* publish gets its own.
|
|
101
|
+
*/
|
|
102
|
+
publish(event) {
|
|
103
|
+
return runWorkScope(workSeedFromEvent(event), () => this.dispatch(event));
|
|
104
|
+
}
|
|
105
|
+
async dispatch(event) {
|
|
93
106
|
const allHandlers = /* @__PURE__ */ new Set();
|
|
94
107
|
for (const [pattern, handlers] of this.handlers) if (matchEventPattern(pattern, event.type)) for (const h of handlers) allHandlers.add(h);
|
|
108
|
+
if (allHandlers.size === 0 && this.onUnroutedEvent) try {
|
|
109
|
+
this.onUnroutedEvent(event);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
try {
|
|
112
|
+
this.logger.error(`[EventTransport] onUnroutedEvent threw for ${event.type}:`, err);
|
|
113
|
+
} catch {}
|
|
114
|
+
}
|
|
95
115
|
const failures = [];
|
|
96
116
|
const run = async (handler) => {
|
|
97
117
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as buildRequestScopeProjection } from "./projection-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as buildRequestScopeProjection } from "./projection-Uvgp-EEj.mjs";
|
|
3
3
|
//#region src/hooks/HookSystem.ts
|
|
4
4
|
/**
|
|
5
5
|
* Hook System
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import "./constants-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import "./constants-BxLgNFF7.mjs";
|
|
3
3
|
//#region src/registry/ResourceRegistry.ts
|
|
4
4
|
/**
|
|
5
5
|
* Resource Registry
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { n as getUserRoles } from "./userHelpers-
|
|
3
|
-
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-
|
|
4
|
-
import { a as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-
|
|
5
|
-
import { t as buildRequestScopeProjection } from "./projection-
|
|
6
|
-
import { t as executePipeline } from "./pipe-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { n as getUserRoles } from "./userHelpers-DDMOZ_Pm.mjs";
|
|
3
|
+
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-CDy3-zw1.mjs";
|
|
4
|
+
import { a as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-BTyPLHK0.mjs";
|
|
5
|
+
import { t as buildRequestScopeProjection } from "./projection-Uvgp-EEj.mjs";
|
|
6
|
+
import { t as executePipeline } from "./pipe-CBVmUJ-j.mjs";
|
|
7
7
|
import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
|
|
8
8
|
//#region src/core/fastifyAdapter.ts
|
|
9
9
|
/** Type guard for Mongoose-like documents with toObject() */
|
|
@@ -82,6 +82,7 @@ function createRequestContext(req) {
|
|
|
82
82
|
arc: reqWithExtras.arc,
|
|
83
83
|
_scope: rawScope,
|
|
84
84
|
_ownershipCheck: reqWithExtras._ownershipCheck,
|
|
85
|
+
_crossTenantRead: reqWithExtras._crossTenantRead,
|
|
85
86
|
_policyFilters: reqWithExtras._policyFilters ?? {},
|
|
86
87
|
log: reqWithExtras.log
|
|
87
88
|
},
|
package/dist/audit/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { d as UserBase } from "../base-
|
|
3
|
-
import "../index-
|
|
2
|
+
import { d as UserBase } from "../base-CxrV1LmG.mjs";
|
|
3
|
+
import "../index-DHbyPo5j.mjs";
|
|
4
4
|
import { FastifyPluginAsync } from "fastify";
|
|
5
5
|
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
6
6
|
//#region src/audit/stores/interface.d.ts
|
package/dist/audit/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as transactionContext } from "../transactionContext-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as transactionContext } from "../transactionContext-DCWMI6hV.mjs";
|
|
3
|
+
import { n as declareRuntimeCapability } from "../runtimeCapabilities-DSA_dH1_.mjs";
|
|
3
4
|
import { and, anyOf, eq, gte, lt, lte } from "@classytic/repo-core/filter";
|
|
4
5
|
import fp from "fastify-plugin";
|
|
5
6
|
//#region src/audit/repository-audit-adapter.ts
|
|
@@ -187,7 +188,13 @@ const auditPlugin = async (fastify, opts = {}) => {
|
|
|
187
188
|
const stores = [];
|
|
188
189
|
if (repository) stores.push(repositoryAsAuditStore(repository, { sessionProvider: sessionProvider === false ? void 0 : sessionProvider }));
|
|
189
190
|
stores.push(...customStores);
|
|
190
|
-
|
|
191
|
+
const memoryFallback = stores.length === 0;
|
|
192
|
+
if (memoryFallback) stores.push(new MemoryAuditStore());
|
|
193
|
+
declareRuntimeCapability(fastify, {
|
|
194
|
+
subsystem: "audit.store",
|
|
195
|
+
durability: memoryFallback ? "memory" : "shared",
|
|
196
|
+
detail: "default MemoryAuditStore: the trail is replica-local and lost on restart"
|
|
197
|
+
});
|
|
191
198
|
async function logToStores(entry) {
|
|
192
199
|
await Promise.all(stores.map((store) => store.log(entry)));
|
|
193
200
|
}
|
package/dist/auth/audit.mjs
CHANGED
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { l as PermissionCheck } from "../base-
|
|
3
|
-
import { Ft as AuthPluginOptions, Pt as AuthHelpers } from "../index-
|
|
2
|
+
import { l as PermissionCheck } from "../base-CxrV1LmG.mjs";
|
|
3
|
+
import { Ft as AuthPluginOptions, Pt as AuthHelpers } from "../index-DHbyPo5j.mjs";
|
|
4
4
|
import { t as ExternalOpenApiPaths } from "../externalPaths-CmGOxOtD.mjs";
|
|
5
5
|
import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-qimlKV0P.mjs";
|
|
6
6
|
import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest } from "fastify";
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-
|
|
3
|
-
import { n as requireSingleHeaderValue } from "../headers-
|
|
4
|
-
import { n as getUserRoles, r as normalizeRoles } from "../userHelpers-
|
|
5
|
-
import { o as toJsonSchema } from "../schemaConverter-
|
|
6
|
-
import {
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-C_YhELhe.mjs";
|
|
3
|
+
import { n as requireSingleHeaderValue } from "../headers-CNECDE6q.mjs";
|
|
4
|
+
import { n as getUserRoles, r as normalizeRoles } from "../userHelpers-DDMOZ_Pm.mjs";
|
|
5
|
+
import { o as toJsonSchema } from "../schemaConverter-BLCSmNbr.mjs";
|
|
6
|
+
import { n as declareRuntimeCapability } from "../runtimeCapabilities-DSA_dH1_.mjs";
|
|
7
|
+
import { E as requireOrgRole, T as requireOrgMembership, k as requireTeamMembership } from "../permissions-DiHXy8Ck.mjs";
|
|
7
8
|
import { isProductionEnv } from "@classytic/primitives/environment";
|
|
8
9
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
9
10
|
import fp from "fastify-plugin";
|
|
@@ -1298,6 +1299,11 @@ function createSessionManager(options) {
|
|
|
1298
1299
|
}
|
|
1299
1300
|
};
|
|
1300
1301
|
const sessionPlugin = async (fastify) => {
|
|
1302
|
+
declareRuntimeCapability(fastify, {
|
|
1303
|
+
subsystem: "auth.sessions",
|
|
1304
|
+
durability: store instanceof MemorySessionStore ? "memory" : "shared",
|
|
1305
|
+
detail: "MemorySessionStore: sessions exist on the replica that created them"
|
|
1306
|
+
});
|
|
1301
1307
|
const authenticate = async (request, reply) => {
|
|
1302
1308
|
const cookieHeader = request.headers.cookie;
|
|
1303
1309
|
const signedValue = parseCookies(typeof cookieHeader === "string" ? cookieHeader : void 0).get(cookieName);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { t as ArcError } from "./errors-
|
|
3
|
+
import { t as ArcError } from "./errors-C_YhELhe.mjs";
|
|
4
4
|
//#region src/permissions/filter-merge.ts
|
|
5
5
|
/**
|
|
6
6
|
* Policy-filter composition — logical AND, never last-writer-wins.
|
|
@@ -294,6 +294,16 @@ declare module "fastify" {
|
|
|
294
294
|
document?: unknown;
|
|
295
295
|
/** Ownership check context (field name + user field) */
|
|
296
296
|
_ownershipCheck?: Record<string, unknown>;
|
|
297
|
+
/**
|
|
298
|
+
* This read is deliberately NOT scoped to a tenant — a marketplace listing.
|
|
299
|
+
*
|
|
300
|
+
* Set by `multiTenantPreset({ crossTenant: [...] })`. Tenancy is applied in four separate
|
|
301
|
+
* places for a read (the preset middleware, `QueryResolver`, `buildTenantRepoOptions` and the
|
|
302
|
+
* post-fetch `checkOrgScope`), each deriving the org from the scope on its own, so a decision
|
|
303
|
+
* made in one of them is silently re-made by the other three. This flag is how that decision
|
|
304
|
+
* travels. Never set from user input.
|
|
305
|
+
*/
|
|
306
|
+
_crossTenantRead?: boolean;
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
type AnyRecord = Record<string, unknown>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { a as conjoinPolicyFilters } from "./authorizationDecision-
|
|
3
|
-
import { g as toRepositoryFilter } from "./BaseController-
|
|
4
|
-
import { i as compileAggRequest, t as adapterSupportsAggregate } from "./validate-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { a as conjoinPolicyFilters } from "./authorizationDecision-2YY_Kaj1.mjs";
|
|
3
|
+
import { g as toRepositoryFilter } from "./BaseController-DYJRZ0EJ.mjs";
|
|
4
|
+
import { i as compileAggRequest, t as adapterSupportsAggregate } from "./validate-R0G_JrnK.mjs";
|
|
5
5
|
//#region src/core/aggregation/buildHandler.ts
|
|
6
6
|
/**
|
|
7
7
|
* Framework-agnostic aggregation execution. Runs safety guards,
|
package/dist/cache/index.d.mts
CHANGED
|
@@ -180,13 +180,15 @@ declare class RedisCacheStore<TValue = unknown> implements CacheStore<TValue> {
|
|
|
180
180
|
}
|
|
181
181
|
/**
|
|
182
182
|
* Minimal ioredis shape we depend on. We don't import ioredis itself so the
|
|
183
|
-
* cache subpath stays peer-dep-free.
|
|
183
|
+
* cache subpath stays peer-dep-free. The `scan` tail is `unknown[]` so a real
|
|
184
|
+
* ioredis `Redis` (overloads with literal tokens + optional callback) is
|
|
185
|
+
* assignable — see the same note on the idempotency `IoredisLike`.
|
|
184
186
|
*/
|
|
185
187
|
interface IoredisLike {
|
|
186
188
|
get(key: string): Promise<string | null>;
|
|
187
189
|
set(...args: unknown[]): Promise<string | null>;
|
|
188
190
|
del(...keys: string[]): Promise<number>;
|
|
189
|
-
scan(cursor: string | number, ...args:
|
|
191
|
+
scan(cursor: string | number, ...args: unknown[]): Promise<[string, string[]]>;
|
|
190
192
|
pipeline?(): {
|
|
191
193
|
del(key: string): unknown;
|
|
192
194
|
exec(): Promise<unknown>;
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-
|
|
3
|
-
import { t as MemoryCacheStore } from "../memory-
|
|
4
|
-
import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-Cmv4cBRa.mjs";
|
|
3
|
+
import { t as MemoryCacheStore } from "../memory-CpDZ9NPw.mjs";
|
|
4
|
+
import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-B5F09bYr.mjs";
|
|
5
5
|
//#region src/cache/redis.ts
|
|
6
6
|
/**
|
|
7
7
|
* Redis-backed cache store.
|
|
@@ -1,5 +1,5 @@
|
|
|
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 fp from "fastify-plugin";
|
|
4
4
|
//#region src/plugins/caching.ts
|
|
5
5
|
var caching_exports = /* @__PURE__ */ __exportAll({
|
package/dist/cleanup/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
import { L as RequestWithExtras } from "../index-
|
|
2
|
+
import { L as RequestWithExtras } from "../index-DHbyPo5j.mjs";
|
|
3
3
|
import { A as CleanupStepResult, B as VerificationResult, C as CleanupResult, D as CleanupRunStore, E as CleanupRunStatus, F as PurgeEvidence, I as PurgeResourceResult, L as PurgeStrategyKind, M as CleanupWriteFence, N as DEFAULT_CLEANUP_LIMITS, O as CleanupRunTransitionPatch, P as PurgeActor, R as PurgeVerificationSummary, S as CleanupRecipe, T as CleanupRunCreateResult, _ as CleanupPermissions, a as CleanupEvidenceStore, b as CleanupPlanItem, c as CleanupInput, d as CleanupLease, f as CleanupLimits, g as CleanupPermissionCheck, h as CleanupOutcomeStatus, i as CleanupContext, j as CleanupStepRunStatus, k as CleanupStepProgress, l as CleanupJob, m as CleanupManifest, n as CLEANUP_TERMINAL_STATUSES, o as CleanupExecutionContext, p as CleanupLogger, r as CleanupCancelRequest, s as CleanupFinalizationPayload, t as Availability, u as CleanupJobQueue, v as CleanupPlan, w as CleanupRun, x as CleanupProgressSummary, y as CleanupPlanDraft, z as VerificationCheck } from "../types-DVLddu2q.mjs";
|
|
4
|
-
import { t as ArcModule } from "../types-
|
|
4
|
+
import { t as ArcModule } from "../types-DfVDvkdP.mjs";
|
|
5
5
|
import { PurgeStrategyKind as PurgeStrategyKind$1 } from "@classytic/primitives/retention";
|
|
6
6
|
import { canonicalJson } from "@classytic/primitives/canonical";
|
|
7
7
|
import { CleanupStep } from "@classytic/repo-core/cleanup";
|
package/dist/cleanup/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { g as getUserId } from "../types-
|
|
3
|
-
import { t as defineResource } from "../defineResource-
|
|
4
|
-
import { t as defineModule } from "../module-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { g as getUserId } from "../types-CDy3-zw1.mjs";
|
|
3
|
+
import { t as defineResource } from "../defineResource-DzGT-2LP.mjs";
|
|
4
|
+
import { t as defineModule } from "../module-BkQEesNQ.mjs";
|
|
5
5
|
import { createPurgeEvidence } from "@classytic/primitives/retention";
|
|
6
6
|
import { canonicalJson, sha256Hex } from "@classytic/primitives/canonical";
|
|
7
7
|
//#region src/cleanup/errors.ts
|
|
@@ -723,9 +723,25 @@ function createCleanupService(deps) {
|
|
|
723
723
|
const logger = deps.logger;
|
|
724
724
|
const generateId = deps.generateId ?? (() => globalThis.crypto.randomUUID());
|
|
725
725
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
726
|
+
/**
|
|
727
|
+
* Undefined-valued keys are DROPPED, not spread.
|
|
728
|
+
*
|
|
729
|
+
* `limits` is `Partial<CleanupLimits>`, which permits an EXPLICIT `undefined`
|
|
730
|
+
* — and a plain spread lets that overwrite the default rather than fall back
|
|
731
|
+
* to it. Every limit is then read in a comparison, and `n > undefined` is
|
|
732
|
+
* always `false`, so `{ limits: { maxPlanItems: undefined } }` did not raise
|
|
733
|
+
* the cap: it silently REMOVED it, on a framework whose whole job is deleting
|
|
734
|
+
* data. Same for `maxReasonLength` / `maxParamDepth`, and `maxResults` turned
|
|
735
|
+
* `slice(0, undefined)` into "keep everything".
|
|
736
|
+
*
|
|
737
|
+
* A host reaches this by forwarding an optional config through
|
|
738
|
+
* (`limits: { maxPlanItems: cfg.planCap }`) — no cast, no obvious mistake.
|
|
739
|
+
* `permissions/presets.ts` already filters for exactly this reason; this is
|
|
740
|
+
* the same rule applied to the destructive path.
|
|
741
|
+
*/
|
|
726
742
|
const limits = {
|
|
727
743
|
...DEFAULT_CLEANUP_LIMITS,
|
|
728
|
-
...deps.limits
|
|
744
|
+
...Object.fromEntries(Object.entries(deps.limits ?? {}).filter(([, v]) => v !== void 0))
|
|
729
745
|
};
|
|
730
746
|
const leaseMs = deps.leaseMs ?? 3e5;
|
|
731
747
|
const progressThrottleMs = deps.progressThrottleMs ?? 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-
|
|
2
|
+
import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-DHbyPo5j.mjs";
|
|
3
3
|
//#region src/cli/commands/describe.d.ts
|
|
4
4
|
interface DescribedResource {
|
|
5
5
|
name: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as CRUD_OPERATIONS } from "../../constants-
|
|
3
|
-
import { n as stringifyMeasureMap } from "../../ResourceRegistry-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as CRUD_OPERATIONS } from "../../constants-BxLgNFF7.mjs";
|
|
3
|
+
import { n as stringifyMeasureMap } from "../../ResourceRegistry-Cu7uTJp9.mjs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
//#region src/cli/commands/describe.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as buildOpenApiSpec } from "../../openapi-
|
|
3
|
-
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as buildOpenApiSpec } from "../../openapi-C3LFRzPq.mjs";
|
|
3
|
+
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-99fIMC3p.mjs";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
6
6
|
//#region src/cli/commands/docs.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
2
|
import { dirname, join, parse, resolve } from "node:path";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
//#region src/cli/commands/doctor.ts
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as generate } from "../../generate-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as generate } from "../../generate-D_YmiCNf.mjs";
|
|
3
3
|
export { generate };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as init } from "../../init-
|
|
1
|
+
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as init } from "../../init-Y2MlSN9n.mjs";
|
|
3
3
|
export { init };
|