@classytic/arc 2.41.1 → 2.44.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/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
- package/dist/{EventTransport-DSqD_Xsg.mjs → EventTransport-DZWDsaPa.mjs} +22 -17
- package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-yjEgn7Cm.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-DJxHUpE4.mjs} +44 -13
- package/dist/acquire-BozJ-9yt.mjs +35 -0
- package/dist/actionPermissions-5N04zBL4.mjs +772 -0
- package/dist/audit/index.d.mts +46 -8
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.d.mts +8 -8
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +46 -16
- package/dist/auth/index.mjs +134 -156
- package/dist/auth/redis-session.d.mts +4 -5
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-wX3SA9w_.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-DWiKW7lP.mjs +57 -0
- package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-Bswu_vNb.mjs} +4 -4
- package/dist/cache/index.d.mts +31 -43
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DbCBxlgw.mjs → caching-Drp68DIS.mjs} +3 -3
- package/dist/claimed-DYvQoZLv.mjs +8 -0
- package/dist/cleanup/index.d.mts +18 -22
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +7 -5
- package/dist/cli/commands/describe.mjs +7 -4
- package/dist/cli/commands/docs.d.mts +2 -3
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.d.mts +2 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -3
- package/dist/cli/commands/init.mjs +2 -3
- package/dist/cli/commands/introspect.d.mts +2 -3
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-D5Ubn1fe.mjs → constants-B2QHb6u6.mjs} +4 -2
- package/dist/context/index.d.mts +11 -14
- package/dist/context/index.mjs +18 -38
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +8 -8
- package/dist/{core-5lazhva9.mjs → core-COcqo0Cp.mjs} +15 -5
- package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-zG13rpPJ.mjs} +97 -39
- package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-CWRz96Vr.mjs} +11 -8
- package/dist/{preloadResources-f3boenVZ.mjs → createApp-BGLYtErt.mjs} +305 -394
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-DSztofa9.d.mts} +1 -1
- package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation--3xguLkk.mjs} +1 -1
- package/dist/{defineResource-DxkMTjRn.mjs → defineResource-gUp9ug66.mjs} +118 -1024
- package/dist/discovery/index.d.mts +6 -7
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +11 -5
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-CkvG0LG2.mjs → elevation-BG4gnMsJ.mjs} +41 -12
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/encryption/index.d.mts +5 -5
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-COHw3N1b.mjs} +26 -5
- package/dist/{errors-BH7YU_9O.mjs → errors-CW4anJBy.mjs} +58 -23
- package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
- package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
- package/dist/{eventPlugin-zZY-unhr.mjs → eventPlugin-BweHAVws.mjs} +82 -21
- package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-N4-omQFR.mjs} +151 -17
- package/dist/events/index.d.mts +43 -18
- package/dist/events/index.mjs +49 -53
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +139 -157
- package/dist/events/transports/redis.d.mts +5 -6
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +42 -27
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DRlCIWE9.d.mts} +11 -25
- package/dist/{fields-BHrkoF5y.mjs → fields-A5crhsJb.mjs} +1 -1
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/filter-merge-DGM-GQ1J.mjs +99 -0
- package/dist/{generate-BLRf2m3y.mjs → generate-BXKo2Gc8.mjs} +2 -2
- package/dist/{headers-BIXWKrzQ.mjs → headers-DKZQKL-Z.mjs} +2 -2
- package/dist/health-BYAwA6YS.mjs +22 -0
- package/dist/{health-ZRHeDHXH.mjs → health-CrqUp3sz.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +36 -10
- package/dist/idempotency/index.mjs +230 -111
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +77 -12
- package/dist/{index-D4u76eQW.d.mts → index-Beg3aNnK.d.mts} +23 -101
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-BkV55WlV.d.mts} +37 -38
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/index-CUJvoyw3.d.mts +510 -0
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/{index-DHbyPo5j.d.mts → index-WH7xgvxS.d.mts} +304 -254
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +11 -11
- package/dist/{init-CpLkjU18.mjs → init-BPzK5Lnr.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +11 -7
- package/dist/integrations/event-gateway.mjs +4 -4
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.d.mts +1 -1
- package/dist/integrations/jobs/index.mjs +18 -5
- package/dist/integrations/mcp/index.d.mts +102 -89
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +8 -9
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +85 -144
- package/dist/integrations/streamline.mjs +131 -55
- package/dist/integrations/webhooks.d.mts +83 -143
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +5 -6
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +5 -6
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.d.mts +3 -3
- package/dist/integrations/websocket.mjs +2 -2
- package/dist/{interface-DEWNOMza.d.mts → interface-JJzwH4Ro.d.mts} +34 -2
- package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-YIq-vGLv.mjs} +2 -2
- package/dist/{keys-cpZCtcIE.mjs → keys-CpoiZ8hU.mjs} +7 -18
- package/dist/{loadResources-BPfWiI5s.mjs → loadResources-6jO52-RR.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-MP8xEppu.mjs} +2 -2
- package/dist/logger/index.d.mts +9 -17
- package/dist/logger/index.mjs +2 -153
- package/dist/logger-D4V1zGLI.mjs +160 -0
- package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-srPc7ItN.mjs} +446 -170
- package/dist/{memory-Iotz0cCx.mjs → memory-CTa5pOCW.mjs} +36 -34
- package/dist/{metrics-NMsmpRmY.mjs → metrics-DsZrvnpB.mjs} +39 -72
- package/dist/middleware/index.d.mts +5 -5
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.d.mts +13 -13
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-DpLJG2_N.mjs → module--L0iBIZz.mjs} +8 -8
- package/dist/mongodb-memory-server-CHhYSq5y.mjs +35539 -0
- package/dist/mongoose-CYJCLyi-.mjs +67451 -0
- package/dist/multiTenant-CAtYlW6H.d.mts +104 -0
- package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-Cc7eZ9cq.mjs} +46 -7
- package/dist/node-D8UZv_8c.mjs +41236 -0
- package/dist/{openapi-CmFf3yq_.mjs → openapi-CMegfsm8.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +5 -5
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Ryk270jd.mjs} +203 -33
- package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +8 -6
- package/dist/{permissions-D8TBH44E.mjs → permissions-C5ijIz5Q.mjs} +149 -33
- package/dist/{pipe-FMWNheSO.mjs → pipe-D81zMtWs.mjs} +2 -2
- package/dist/pipeline/index.d.mts +7 -7
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-CJGMKoui.mjs +565 -0
- package/dist/{BaseController-CYEwjyjN.mjs → plugin-DALd9dcF.mjs} +1269 -346
- package/dist/plugins/index.d.mts +17 -14
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.d.mts +5 -6
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-CXWZIzTv.mjs → pluralize-COUNYo3d.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +8 -8
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +35 -40
- package/dist/presets/index.mjs +4 -4
- package/dist/presets/multiTenant.d.mts +2 -107
- package/dist/presets/multiTenant.mjs +22 -162
- package/dist/presets/search.d.mts +9 -10
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-ZJSPgmT2.mjs → presets-CONf2Qem.mjs} +99 -107
- package/dist/{projection-BzIgzoKZ.mjs → projection-wFoLqdss.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-BATECpyh.mjs} +12 -14
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-DVEZ_UpV.mjs} +1 -1
- package/dist/{realtime-8tIQWZz4.mjs → realtime-xyubMKSB.mjs} +25 -21
- package/dist/{redis-Bzi1y3F9.d.mts → redis-BDgv9Glw.d.mts} +17 -24
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +15 -10
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-DNkgGQcK.mjs → registry-Bdzg-hdm.mjs} +13 -10
- package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-BFJxGPuh.mjs} +1 -1
- package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-Bnc6lJyy.mjs} +2 -2
- package/dist/{requestContext-cDkAUr-P.mjs → requestContext-C10UeFe5.mjs} +1 -1
- package/dist/requirement-Bu50QALH.mjs +35 -0
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-urWPJxrt.mjs +180 -0
- package/dist/rolldown-runtime-MYrLNGnq.mjs +45 -0
- package/dist/routeValidator-DHtpzrzk.mjs +70 -0
- package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-DIMKMV2G.mjs} +1 -1
- package/dist/{schedules-j6uXXAeJ.mjs → schedules-CKTs5ys8.mjs} +46 -37
- package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BVjkQ5DD.mjs} +2 -2
- package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-BA0Hb21Y.mjs} +2 -2
- package/dist/schemas/index.d.mts +20 -10
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.d.mts +13 -13
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +4 -4
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-BUi1xV1I.mjs → scope-DINaj7Ax.mjs} +4 -4
- package/dist/shutdown-LpLMH8T1.mjs +59 -0
- package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-DbwIM1f1.mjs} +1 -1
- package/dist/{sse-BeqVKQKu.mjs → sse-BzJFPX_1.mjs} +11 -15
- package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-DQChzGTs.mjs} +2 -2
- package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-Coj9RhOZ.mjs} +1 -1
- package/dist/{streaming-IcRFjWQU.mjs → streaming-JzB5CAga.mjs} +1 -1
- package/dist/subscriberIdentity-DeyppIif.mjs +28 -0
- package/dist/tenancy-DxV8OuX2.mjs +43 -0
- package/dist/tenantPreparation-CBvvAcAj.mjs +67 -0
- package/dist/testing/cleanupStoreContract.d.mts +4 -5
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +215 -197
- package/dist/testing/index.mjs +289 -214
- package/dist/testing/outboxStoreContract.d.mts +4 -5
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +16 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.d.mts +4 -5
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-CntavTlR.mjs +46 -0
- package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-orvnGu4F.mjs} +1 -1
- package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-kEaP_WmO.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CWQn9-IC.d.mts → types-D3Ixsh00.d.mts} +22 -9
- package/dist/{types-CgenB1gv.mjs → types-fvO6Ovzx.mjs} +4 -4
- package/dist/usage/index.d.mts +5 -6
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-C76zD5pa.mjs} +1 -1
- package/dist/utils/index.d.mts +125 -264
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-CxcctrAh.mjs → utils-BpkVZ8rB.mjs} +315 -404
- package/dist/{validate-DrYWnOzI.mjs → validate-CWp-lsrm.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DKkT4hNJ.mjs → versioning-CHhL9P3o.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-CxKqoIFn.mjs → workScope-n9a7d_lZ.mjs} +2 -2
- package/package.json +57 -59
- package/skills/arc/SKILL.md +13 -4
- package/skills/arc/references/auth.md +8 -5
- package/skills/arc/references/integrations.md +1 -1
- package/skills/arc/references/mcp.md +8 -6
- package/skills/arc/references/multi-tenancy.md +19 -14
- package/skills/arc/references/production.md +27 -10
- package/dist/actionPermissions-BoPGQ5-4.mjs +0 -350
- package/dist/index-CFfLKMH4.d.mts +0 -290
- package/dist/interface-YzIbF7Ii.mjs +0 -17
- package/dist/redis-stream-BPKscsTB.d.mts +0 -260
- package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
- package/dist/rolldown-runtime-eysyzFAF.mjs +0 -15
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
|
|
2
|
+
import { G as CrudRouteKey, W as PresetResult } from "./index-WH7xgvxS.mjs";
|
|
3
|
+
//#region src/permissions/tenantPreparation.d.ts
|
|
4
|
+
type TenantFieldSpec = {
|
|
5
|
+
field: string;
|
|
6
|
+
type: "org";
|
|
7
|
+
} | {
|
|
8
|
+
field: string;
|
|
9
|
+
type: "team";
|
|
10
|
+
} | {
|
|
11
|
+
field: string;
|
|
12
|
+
contextKey: string;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/presets/multiTenant.d.ts
|
|
16
|
+
interface MultiTenantOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Single-field form: name of the database field to filter by.
|
|
19
|
+
* Reads `getOrgId(scope)` as the value source.
|
|
20
|
+
*
|
|
21
|
+
* Mutually exclusive with `tenantFields` — pass one or the other, not both.
|
|
22
|
+
*
|
|
23
|
+
* @default 'organizationId'
|
|
24
|
+
*/
|
|
25
|
+
tenantField?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Multi-field form (2.7.1+): list of tenant dimensions to filter by in
|
|
28
|
+
* lockstep. Use this when a resource is scoped by more than just
|
|
29
|
+
* organization — e.g. organization + branch, organization + project,
|
|
30
|
+
* or organization + team + workspace.
|
|
31
|
+
*
|
|
32
|
+
* Each entry is a discriminated `TenantFieldSpec` declaring where the
|
|
33
|
+
* value comes from. Use `type: 'org'` / `type: 'team'` for built-in scope
|
|
34
|
+
* fields, or `contextKey: '...'` to read from `scope.context` (set by
|
|
35
|
+
* your auth function).
|
|
36
|
+
*
|
|
37
|
+
* Fail-closed: if any required dimension is missing, the request is
|
|
38
|
+
* rejected. Elevated scopes apply whatever resolves and skip the rest.
|
|
39
|
+
*
|
|
40
|
+
* Mutually exclusive with `tenantField`.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* multiTenantPreset({
|
|
45
|
+
* tenantFields: [
|
|
46
|
+
* { field: 'organizationId', type: 'org' },
|
|
47
|
+
* { field: 'branchId', contextKey: 'branchId' },
|
|
48
|
+
* ],
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
tenantFields?: readonly TenantFieldSpec[];
|
|
53
|
+
/**
|
|
54
|
+
* Routes that allow public access (no auth required)
|
|
55
|
+
* When a route is in this array:
|
|
56
|
+
* - If no org context: allow through without filtering (public data)
|
|
57
|
+
* - If org context present: require auth and apply filter
|
|
58
|
+
*
|
|
59
|
+
* @default [] (strict mode - all routes require auth)
|
|
60
|
+
* @example
|
|
61
|
+
* multiTenantPreset({ allowPublic: ['list', 'get'] })
|
|
62
|
+
*/
|
|
63
|
+
allowPublic?: CrudRouteKey[];
|
|
64
|
+
/**
|
|
65
|
+
* Reads that are NOT scoped to a tenant at all — a marketplace listing.
|
|
66
|
+
*
|
|
67
|
+
* `allowPublic` answers "may an anonymous caller reach this route?". That is a different
|
|
68
|
+
* question from "is this listing scoped to a tenant?", and until this option there was no way
|
|
69
|
+
* to answer the second one with "no". An `allowPublic` route still filters by the caller's
|
|
70
|
+
* organization whenever they have one, and a signed-in user of a multi-org product ALWAYS has
|
|
71
|
+
* one, because choosing an organization writes it onto the session. So a cross-tenant catalog
|
|
72
|
+
* silently became single-tenant the moment one of its own sellers browsed it: the same URL,
|
|
73
|
+
* with no header and no query parameter to explain the difference, returned a different world
|
|
74
|
+
* depending on who was asking.
|
|
75
|
+
*
|
|
76
|
+
* The only escape was to drop this preset from the resource entirely, which also drops tenant
|
|
77
|
+
* INJECTION on create/update — the thing that stops a member moving a document into another
|
|
78
|
+
* organization. Trading a write guarantee for a read behaviour is not a real choice.
|
|
79
|
+
*
|
|
80
|
+
* Listed routes get no tenant middleware: no filter, and nothing stashed for the repo layer.
|
|
81
|
+
* Everything else keeps its filter, so writes are unaffected.
|
|
82
|
+
*
|
|
83
|
+
* READS ONLY, by type. A cross-tenant `create` or `update` is not a listing, it is an escape
|
|
84
|
+
* from tenancy, and it should never be one option away by accident.
|
|
85
|
+
*
|
|
86
|
+
* THIS DOES NOT AUTHORIZE ANYTHING. Removing the tenant filter widens what the query can
|
|
87
|
+
* return, so the resource owes a row-level `policy` from its permission check saying what a
|
|
88
|
+
* stranger may see (`{ status: 'active' }`, `{ visibility: 'public' }`). Declaring this without
|
|
89
|
+
* one publishes every row in every tenant.
|
|
90
|
+
*
|
|
91
|
+
* @default []
|
|
92
|
+
* @example
|
|
93
|
+
* // A public catalog: anyone may browse every seller's LIVE products,
|
|
94
|
+
* // while create/update/delete stay pinned to the caller's own shop.
|
|
95
|
+
* multiTenantPreset({
|
|
96
|
+
* allowPublic: ['list', 'get'],
|
|
97
|
+
* crossTenant: ['list', 'get'],
|
|
98
|
+
* })
|
|
99
|
+
*/
|
|
100
|
+
crossTenant?: readonly Extract<CrudRouteKey, "list" | "get">[];
|
|
101
|
+
}
|
|
102
|
+
declare function multiTenantPreset(options?: MultiTenantOptions): PresetResult;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { multiTenantPreset as n, TenantFieldSpec as r, MultiTenantOptions as t };
|
|
@@ -1,6 +1,41 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { ARC_ERROR_CODES } from "@classytic/repo-core/errors";
|
|
2
3
|
import sjson from "secure-json-parse";
|
|
3
4
|
//#region src/middleware/multipartBody.ts
|
|
5
|
+
/**
|
|
6
|
+
* Multipart Body Middleware — Opt-in multipart/form-data parsing for CRUD routes.
|
|
7
|
+
*
|
|
8
|
+
* Parses multipart form fields into `req.body` and attaches files to `req.body._files`.
|
|
9
|
+
* Use with a `before:create` or `before:update` hook to process files (upload to S3, etc.)
|
|
10
|
+
* before BaseController persists the record.
|
|
11
|
+
*
|
|
12
|
+
* Requires `@fastify/multipart` to be registered on the Fastify instance
|
|
13
|
+
* (Arc registers it by default via `createApp()` unless `multipart: false`).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { defineResource } from '@classytic/arc';
|
|
18
|
+
* import { multipartBody } from '@classytic/arc/middleware';
|
|
19
|
+
*
|
|
20
|
+
* const productResource = defineResource({
|
|
21
|
+
* name: 'product',
|
|
22
|
+
* adapter,
|
|
23
|
+
* middlewares: {
|
|
24
|
+
* create: [multipartBody()],
|
|
25
|
+
* update: [multipartBody()],
|
|
26
|
+
* },
|
|
27
|
+
* hooks: {
|
|
28
|
+
* 'before:create': async (data) => {
|
|
29
|
+
* if (data._files?.image) {
|
|
30
|
+
* data.imageUrl = await uploadToS3(data._files.image);
|
|
31
|
+
* delete data._files;
|
|
32
|
+
* }
|
|
33
|
+
* return data;
|
|
34
|
+
* },
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
4
39
|
const DEFAULT_MAX_FILE_SIZE = 10485760;
|
|
5
40
|
const DEFAULT_MAX_FILES = 5;
|
|
6
41
|
const DEFAULT_MAX_TOTAL_BYTES = 26214400;
|
|
@@ -78,19 +113,19 @@ function multipartBody(options = {}) {
|
|
|
78
113
|
for await (const part of parts) if (part.type === "file") {
|
|
79
114
|
if (fileCount >= maxFiles) continue;
|
|
80
115
|
if (mimeMatcher && !mimeMatcher.matches(part.mimetype)) return reply.code(415).send({
|
|
81
|
-
code:
|
|
116
|
+
code: ARC_ERROR_CODES.UNSUPPORTED_MEDIA_TYPE,
|
|
82
117
|
message: `File type '${part.mimetype}' not allowed. Accepted: ${mimeMatcher.describe()}`,
|
|
83
118
|
status: 415
|
|
84
119
|
});
|
|
85
120
|
const buffer = await part.toBuffer();
|
|
86
121
|
if (buffer.length > maxFileSize) return reply.code(413).send({
|
|
87
|
-
code:
|
|
122
|
+
code: ARC_ERROR_CODES.PAYLOAD_TOO_LARGE,
|
|
88
123
|
message: `File '${part.filename}' exceeds maximum size of ${Math.round(maxFileSize / 1024 / 1024)}MB`,
|
|
89
124
|
status: 413
|
|
90
125
|
});
|
|
91
126
|
totalBytes += buffer.length;
|
|
92
127
|
if (totalBytes > maxTotalBytes) return reply.code(413).send({
|
|
93
|
-
code:
|
|
128
|
+
code: ARC_ERROR_CODES.PAYLOAD_TOO_LARGE,
|
|
94
129
|
message: `Combined upload size exceeds ${Math.round(maxTotalBytes / 1024 / 1024)}MB per request`,
|
|
95
130
|
status: 413
|
|
96
131
|
});
|
|
@@ -106,7 +141,7 @@ function multipartBody(options = {}) {
|
|
|
106
141
|
} catch (err) {
|
|
107
142
|
request.log.error({ err }, "multipartBody: failed to parse multipart form");
|
|
108
143
|
return reply.code(400).send({
|
|
109
|
-
code:
|
|
144
|
+
code: ARC_ERROR_CODES.BAD_REQUEST,
|
|
110
145
|
message: "Failed to parse multipart form data",
|
|
111
146
|
status: 400
|
|
112
147
|
});
|
|
@@ -114,10 +149,14 @@ function multipartBody(options = {}) {
|
|
|
114
149
|
if (requiredFields) {
|
|
115
150
|
const missing = requiredFields.filter((name) => !(name in files));
|
|
116
151
|
if (missing.length > 0) return reply.code(400).send({
|
|
117
|
-
code:
|
|
152
|
+
code: ARC_ERROR_CODES.VALIDATION_ERROR,
|
|
118
153
|
message: `Missing required file field${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`,
|
|
119
154
|
status: 400,
|
|
120
|
-
details:
|
|
155
|
+
details: missing.map((name) => ({
|
|
156
|
+
path: name,
|
|
157
|
+
code: "required",
|
|
158
|
+
message: `File field "${name}" is required`
|
|
159
|
+
}))
|
|
121
160
|
});
|
|
122
161
|
}
|
|
123
162
|
if (fileCount > 0) body[filesKey] = files;
|