@classytic/arc 1.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -794
- package/bin/arc.js +91 -52
- package/dist/EventTransport-BD2U0BTc.d.mts +100 -0
- package/dist/EventTransport-BD2U0BTc.d.mts.map +1 -0
- package/dist/HookSystem-BsGV-j2l.mjs +405 -0
- package/dist/HookSystem-BsGV-j2l.mjs.map +1 -0
- package/dist/ResourceRegistry-DsN4KJjV.mjs +250 -0
- package/dist/ResourceRegistry-DsN4KJjV.mjs.map +1 -0
- package/dist/adapters/index.d.mts +5 -0
- package/dist/adapters/index.mjs +3 -0
- package/dist/audit/index.d.mts +82 -0
- package/dist/audit/index.d.mts.map +1 -0
- package/dist/audit/index.mjs +276 -0
- package/dist/audit/index.mjs.map +1 -0
- package/dist/audit/mongodb.d.mts +5 -0
- package/dist/audit/mongodb.mjs +3 -0
- package/dist/audited-C3T5DTUx.mjs +141 -0
- package/dist/audited-C3T5DTUx.mjs.map +1 -0
- package/dist/auth/index.d.mts +189 -0
- package/dist/auth/index.d.mts.map +1 -0
- package/dist/auth/index.mjs +1102 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/auth/redis-session.d.mts +44 -0
- package/dist/auth/redis-session.d.mts.map +1 -0
- package/dist/auth/redis-session.mjs +76 -0
- package/dist/auth/redis-session.mjs.map +1 -0
- package/dist/betterAuthOpenApi-BrHKeSAx.mjs +250 -0
- package/dist/betterAuthOpenApi-BrHKeSAx.mjs.map +1 -0
- package/dist/cache/index.d.mts +146 -0
- package/dist/cache/index.d.mts.map +1 -0
- package/dist/cache/index.mjs +92 -0
- package/dist/cache/index.mjs.map +1 -0
- package/dist/caching-Bl28lYsR.mjs +94 -0
- package/dist/caching-Bl28lYsR.mjs.map +1 -0
- package/dist/chunk-C7Uep-_p.mjs +20 -0
- package/dist/circuitBreaker-DeY4FCjs.mjs +1097 -0
- package/dist/circuitBreaker-DeY4FCjs.mjs.map +1 -0
- package/dist/cli/commands/describe.d.mts +19 -0
- package/dist/cli/commands/describe.d.mts.map +1 -0
- package/dist/cli/commands/describe.mjs +239 -0
- package/dist/cli/commands/describe.mjs.map +1 -0
- package/dist/cli/commands/docs.d.mts +14 -0
- package/dist/cli/commands/docs.d.mts.map +1 -0
- package/dist/cli/commands/docs.mjs +53 -0
- package/dist/cli/commands/docs.mjs.map +1 -0
- package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -1
- package/dist/cli/commands/generate.d.mts.map +1 -0
- package/dist/cli/commands/generate.mjs +358 -0
- package/dist/cli/commands/generate.mjs.map +1 -0
- package/dist/cli/commands/{init.d.ts → init.d.mts} +12 -8
- package/dist/cli/commands/init.d.mts.map +1 -0
- package/dist/cli/commands/{init.js → init.mjs} +807 -616
- package/dist/cli/commands/init.mjs.map +1 -0
- package/dist/cli/commands/introspect.d.mts +11 -0
- package/dist/cli/commands/introspect.d.mts.map +1 -0
- package/dist/cli/commands/introspect.mjs +76 -0
- package/dist/cli/commands/introspect.mjs.map +1 -0
- package/dist/cli/index.d.mts +17 -0
- package/dist/cli/index.d.mts.map +1 -0
- package/dist/cli/index.mjs +157 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/constants-DdXFXQtN.mjs +85 -0
- package/dist/constants-DdXFXQtN.mjs.map +1 -0
- package/dist/core/index.d.mts +5 -0
- package/dist/core/index.mjs +4 -0
- package/dist/createApp-CUgNqegw.mjs +560 -0
- package/dist/createApp-CUgNqegw.mjs.map +1 -0
- package/dist/defineResource-k0_BDn8v.mjs +2197 -0
- package/dist/defineResource-k0_BDn8v.mjs.map +1 -0
- package/dist/discovery/index.d.mts +47 -0
- package/dist/discovery/index.d.mts.map +1 -0
- package/dist/discovery/index.mjs +110 -0
- package/dist/discovery/index.mjs.map +1 -0
- package/dist/docs/index.d.mts +163 -0
- package/dist/docs/index.d.mts.map +1 -0
- package/dist/docs/index.mjs +73 -0
- package/dist/docs/index.mjs.map +1 -0
- package/dist/elevation-BRy3yFWT.mjs +113 -0
- package/dist/elevation-BRy3yFWT.mjs.map +1 -0
- package/dist/elevation-B_2dRLVP.d.mts +88 -0
- package/dist/elevation-B_2dRLVP.d.mts.map +1 -0
- package/dist/errorHandler-BbcgBmIH.d.mts +73 -0
- package/dist/errorHandler-BbcgBmIH.d.mts.map +1 -0
- package/dist/errorHandler-C1okiriz.mjs +109 -0
- package/dist/errorHandler-C1okiriz.mjs.map +1 -0
- package/dist/errors-B9bZok84.mjs +212 -0
- package/dist/errors-B9bZok84.mjs.map +1 -0
- package/dist/errors-ChKiFz62.d.mts +125 -0
- package/dist/errors-ChKiFz62.d.mts.map +1 -0
- package/dist/eventPlugin-CTrLH3mt.d.mts +125 -0
- package/dist/eventPlugin-CTrLH3mt.d.mts.map +1 -0
- package/dist/eventPlugin-DGR_B2on.mjs +230 -0
- package/dist/eventPlugin-DGR_B2on.mjs.map +1 -0
- package/dist/events/index.d.mts +54 -0
- package/dist/events/index.d.mts.map +1 -0
- package/dist/events/index.mjs +52 -0
- package/dist/events/index.mjs.map +1 -0
- package/dist/events/transports/redis-stream-entry.d.mts +2 -0
- package/dist/events/transports/redis-stream-entry.mjs +178 -0
- package/dist/events/transports/redis-stream-entry.mjs.map +1 -0
- package/dist/events/transports/redis.d.mts +77 -0
- package/dist/events/transports/redis.d.mts.map +1 -0
- package/dist/events/transports/redis.mjs +125 -0
- package/dist/events/transports/redis.mjs.map +1 -0
- package/dist/externalPaths-DlINfKbP.d.mts +51 -0
- package/dist/externalPaths-DlINfKbP.d.mts.map +1 -0
- package/dist/factory/index.d.mts +64 -0
- package/dist/factory/index.d.mts.map +1 -0
- package/dist/factory/index.mjs +3 -0
- package/dist/fastifyAdapter-BkrGrlFi.d.mts +217 -0
- package/dist/fastifyAdapter-BkrGrlFi.d.mts.map +1 -0
- package/dist/fields-DyaDVX4J.d.mts +110 -0
- package/dist/fields-DyaDVX4J.d.mts.map +1 -0
- package/dist/fields-iagOozy0.mjs +115 -0
- package/dist/fields-iagOozy0.mjs.map +1 -0
- package/dist/hooks/index.d.mts +4 -0
- package/dist/hooks/index.mjs +3 -0
- package/dist/idempotency/index.d.mts +97 -0
- package/dist/idempotency/index.d.mts.map +1 -0
- package/dist/idempotency/index.mjs +320 -0
- package/dist/idempotency/index.mjs.map +1 -0
- package/dist/idempotency/mongodb.d.mts +2 -0
- package/dist/idempotency/mongodb.mjs +115 -0
- package/dist/idempotency/mongodb.mjs.map +1 -0
- package/dist/idempotency/redis.d.mts +2 -0
- package/dist/idempotency/redis.mjs +104 -0
- package/dist/idempotency/redis.mjs.map +1 -0
- package/dist/index.d.mts +261 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +105 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/event-gateway.d.mts +47 -0
- package/dist/integrations/event-gateway.d.mts.map +1 -0
- package/dist/integrations/event-gateway.mjs +44 -0
- package/dist/integrations/event-gateway.mjs.map +1 -0
- package/dist/integrations/index.d.mts +5 -0
- package/dist/integrations/index.mjs +1 -0
- package/dist/integrations/jobs.d.mts +104 -0
- package/dist/integrations/jobs.d.mts.map +1 -0
- package/dist/integrations/jobs.mjs +124 -0
- package/dist/integrations/jobs.mjs.map +1 -0
- package/dist/integrations/streamline.d.mts +61 -0
- package/dist/integrations/streamline.d.mts.map +1 -0
- package/dist/integrations/streamline.mjs +126 -0
- package/dist/integrations/streamline.mjs.map +1 -0
- package/dist/integrations/websocket.d.mts +83 -0
- package/dist/integrations/websocket.d.mts.map +1 -0
- package/dist/integrations/websocket.mjs +289 -0
- package/dist/integrations/websocket.mjs.map +1 -0
- package/dist/interface-B01JvPVc.d.mts +78 -0
- package/dist/interface-B01JvPVc.d.mts.map +1 -0
- package/dist/interface-CZe8IkMf.d.mts +55 -0
- package/dist/interface-CZe8IkMf.d.mts.map +1 -0
- package/dist/interface-Ch8HU9uM.d.mts +1098 -0
- package/dist/interface-Ch8HU9uM.d.mts.map +1 -0
- package/dist/introspectionPlugin-rFdO8ZUa.mjs +54 -0
- package/dist/introspectionPlugin-rFdO8ZUa.mjs.map +1 -0
- package/dist/keys-BqNejWup.mjs +43 -0
- package/dist/keys-BqNejWup.mjs.map +1 -0
- package/dist/logger-Df2O2WsW.mjs +79 -0
- package/dist/logger-Df2O2WsW.mjs.map +1 -0
- package/dist/memory-cQgelFOj.mjs +144 -0
- package/dist/memory-cQgelFOj.mjs.map +1 -0
- package/dist/migrations/index.d.mts +157 -0
- package/dist/migrations/index.d.mts.map +1 -0
- package/dist/migrations/index.mjs +261 -0
- package/dist/migrations/index.mjs.map +1 -0
- package/dist/mongodb-BfJVlUJH.mjs +94 -0
- package/dist/mongodb-BfJVlUJH.mjs.map +1 -0
- package/dist/mongodb-CGzRbfAK.d.mts +119 -0
- package/dist/mongodb-CGzRbfAK.d.mts.map +1 -0
- package/dist/mongodb-JN-9JA7K.d.mts +72 -0
- package/dist/mongodb-JN-9JA7K.d.mts.map +1 -0
- package/dist/openapi-G3Cw7XuM.mjs +524 -0
- package/dist/openapi-G3Cw7XuM.mjs.map +1 -0
- package/dist/org/index.d.mts +69 -0
- package/dist/org/index.d.mts.map +1 -0
- package/dist/org/index.mjs +514 -0
- package/dist/org/index.mjs.map +1 -0
- package/dist/org/types.d.mts +83 -0
- package/dist/org/types.d.mts.map +1 -0
- package/dist/org/types.mjs +1 -0
- package/dist/permissions/index.d.mts +279 -0
- package/dist/permissions/index.d.mts.map +1 -0
- package/dist/permissions/index.mjs +579 -0
- package/dist/permissions/index.mjs.map +1 -0
- package/dist/plugins/index.d.mts +173 -0
- package/dist/plugins/index.d.mts.map +1 -0
- package/dist/plugins/index.mjs +523 -0
- package/dist/plugins/index.mjs.map +1 -0
- package/dist/plugins/response-cache.d.mts +88 -0
- package/dist/plugins/response-cache.d.mts.map +1 -0
- package/dist/plugins/response-cache.mjs +284 -0
- package/dist/plugins/response-cache.mjs.map +1 -0
- package/dist/plugins/tracing-entry.d.mts +2 -0
- package/dist/plugins/tracing-entry.mjs +186 -0
- package/dist/plugins/tracing-entry.mjs.map +1 -0
- package/dist/pluralize-CEweyOEm.mjs +87 -0
- package/dist/pluralize-CEweyOEm.mjs.map +1 -0
- package/dist/policies/{index.d.ts → index.d.mts} +204 -169
- package/dist/policies/index.d.mts.map +1 -0
- package/dist/policies/index.mjs +322 -0
- package/dist/policies/index.mjs.map +1 -0
- package/dist/presets/{index.d.ts → index.d.mts} +63 -131
- package/dist/presets/index.d.mts.map +1 -0
- package/dist/presets/index.mjs +144 -0
- package/dist/presets/index.mjs.map +1 -0
- package/dist/presets/multiTenant.d.mts +25 -0
- package/dist/presets/multiTenant.d.mts.map +1 -0
- package/dist/presets/multiTenant.mjs +114 -0
- package/dist/presets/multiTenant.mjs.map +1 -0
- package/dist/presets-BITljm96.mjs +120 -0
- package/dist/presets-BITljm96.mjs.map +1 -0
- package/dist/presets-DzSMwlKj.d.mts +58 -0
- package/dist/presets-DzSMwlKj.d.mts.map +1 -0
- package/dist/prisma-DJbMt3yf.mjs +628 -0
- package/dist/prisma-DJbMt3yf.mjs.map +1 -0
- package/dist/prisma-Dg9GoVdj.d.mts +275 -0
- package/dist/prisma-Dg9GoVdj.d.mts.map +1 -0
- package/dist/queryCachePlugin-7THaI5mt.d.mts +72 -0
- package/dist/queryCachePlugin-7THaI5mt.d.mts.map +1 -0
- package/dist/queryCachePlugin-DMBnp2Q0.mjs +139 -0
- package/dist/queryCachePlugin-DMBnp2Q0.mjs.map +1 -0
- package/dist/redis-D-JAeLtm.d.mts +50 -0
- package/dist/redis-D-JAeLtm.d.mts.map +1 -0
- package/dist/redis-stream-Bdh_vUU8.d.mts +104 -0
- package/dist/redis-stream-Bdh_vUU8.d.mts.map +1 -0
- package/dist/registry/index.d.mts +12 -0
- package/dist/registry/index.d.mts.map +1 -0
- package/dist/registry/index.mjs +4 -0
- package/dist/requestContext-QQD6ROJc.mjs +56 -0
- package/dist/requestContext-QQD6ROJc.mjs.map +1 -0
- package/dist/schemaConverter-BwrmWroW.mjs +99 -0
- package/dist/schemaConverter-BwrmWroW.mjs.map +1 -0
- package/dist/schemas/index.d.mts +64 -0
- package/dist/schemas/index.d.mts.map +1 -0
- package/dist/schemas/index.mjs +83 -0
- package/dist/schemas/index.mjs.map +1 -0
- package/dist/scope/index.d.mts +22 -0
- package/dist/scope/index.d.mts.map +1 -0
- package/dist/scope/index.mjs +66 -0
- package/dist/scope/index.mjs.map +1 -0
- package/dist/sessionManager-jPKLbHE0.d.mts +187 -0
- package/dist/sessionManager-jPKLbHE0.d.mts.map +1 -0
- package/dist/sse-B3c3_yZp.mjs +124 -0
- package/dist/sse-B3c3_yZp.mjs.map +1 -0
- package/dist/testing/index.d.mts +908 -0
- package/dist/testing/index.d.mts.map +1 -0
- package/dist/testing/index.mjs +1977 -0
- package/dist/testing/index.mjs.map +1 -0
- package/dist/tracing-Cc7vVQPp.d.mts +71 -0
- package/dist/tracing-Cc7vVQPp.d.mts.map +1 -0
- package/dist/typeGuards-DhMNLuvU.mjs +10 -0
- package/dist/typeGuards-DhMNLuvU.mjs.map +1 -0
- package/dist/types/index.d.mts +947 -0
- package/dist/types/index.d.mts.map +1 -0
- package/dist/types/index.mjs +15 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/types-Beqn1Un7.mjs +39 -0
- package/dist/types-Beqn1Un7.mjs.map +1 -0
- package/dist/types-CIgB7UUl.d.mts +446 -0
- package/dist/types-CIgB7UUl.d.mts.map +1 -0
- package/dist/types-aYB4V7uN.d.mts +87 -0
- package/dist/types-aYB4V7uN.d.mts.map +1 -0
- package/dist/utils/index.d.mts +748 -0
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +6 -0
- package/package.json +194 -68
- package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
- package/dist/adapters/index.d.ts +0 -237
- package/dist/adapters/index.js +0 -668
- package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
- package/dist/audit/index.d.ts +0 -195
- package/dist/audit/index.js +0 -319
- package/dist/auth/index.d.ts +0 -47
- package/dist/auth/index.js +0 -174
- package/dist/cli/commands/docs.d.ts +0 -11
- package/dist/cli/commands/docs.js +0 -474
- package/dist/cli/commands/generate.js +0 -334
- package/dist/cli/commands/introspect.d.ts +0 -8
- package/dist/cli/commands/introspect.js +0 -338
- package/dist/cli/index.d.ts +0 -4
- package/dist/cli/index.js +0 -3269
- package/dist/core/index.d.ts +0 -220
- package/dist/core/index.js +0 -2786
- package/dist/createApp-Ce9wl8W9.d.ts +0 -77
- package/dist/docs/index.d.ts +0 -166
- package/dist/docs/index.js +0 -658
- package/dist/errors-8WIxGS_6.d.ts +0 -122
- package/dist/events/index.d.ts +0 -117
- package/dist/events/index.js +0 -89
- package/dist/factory/index.d.ts +0 -38
- package/dist/factory/index.js +0 -1652
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/index.js +0 -199
- package/dist/idempotency/index.d.ts +0 -323
- package/dist/idempotency/index.js +0 -500
- package/dist/index-B4t03KQ0.d.ts +0 -1366
- package/dist/index.d.ts +0 -135
- package/dist/index.js +0 -4756
- package/dist/migrations/index.d.ts +0 -185
- package/dist/migrations/index.js +0 -274
- package/dist/org/index.d.ts +0 -129
- package/dist/org/index.js +0 -220
- package/dist/permissions/index.d.ts +0 -144
- package/dist/permissions/index.js +0 -103
- package/dist/plugins/index.d.ts +0 -46
- package/dist/plugins/index.js +0 -1069
- package/dist/policies/index.js +0 -196
- package/dist/presets/index.js +0 -384
- package/dist/presets/multiTenant.d.ts +0 -39
- package/dist/presets/multiTenant.js +0 -112
- package/dist/registry/index.d.ts +0 -16
- package/dist/registry/index.js +0 -253
- package/dist/testing/index.d.ts +0 -618
- package/dist/testing/index.js +0 -48020
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.js +0 -8
- package/dist/types-B99TBmFV.d.ts +0 -76
- package/dist/types-BvckRbs2.d.ts +0 -143
- package/dist/utils/index.d.ts +0 -679
- package/dist/utils/index.js +0 -931
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { a as softDeletePreset, i as slugLookupPreset, n as treePreset, r as ownedByUserPreset, t as auditedPreset } from "../audited-C3T5DTUx.mjs";
|
|
2
|
+
import multiTenantPreset from "./multiTenant.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/presets/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Convenience alias for multiTenantPreset with public list/get routes
|
|
7
|
+
* Equivalent to: multiTenantPreset({ allowPublic: ['list', 'get'] })
|
|
8
|
+
*/
|
|
9
|
+
const flexibleMultiTenantPreset = (options = {}) => multiTenantPreset({
|
|
10
|
+
...options,
|
|
11
|
+
allowPublic: ["list", "get"]
|
|
12
|
+
});
|
|
13
|
+
const presetRegistry = {
|
|
14
|
+
softDelete: softDeletePreset,
|
|
15
|
+
slugLookup: slugLookupPreset,
|
|
16
|
+
ownedByUser: ownedByUserPreset,
|
|
17
|
+
multiTenant: multiTenantPreset,
|
|
18
|
+
tree: treePreset,
|
|
19
|
+
audited: auditedPreset
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Get preset by name with options
|
|
23
|
+
*/
|
|
24
|
+
function getPreset(nameOrConfig) {
|
|
25
|
+
if (typeof nameOrConfig === "object" && nameOrConfig.name) {
|
|
26
|
+
const { name, ...options } = nameOrConfig;
|
|
27
|
+
return resolvePreset(name, options);
|
|
28
|
+
}
|
|
29
|
+
return resolvePreset(nameOrConfig);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve preset by name
|
|
33
|
+
*/
|
|
34
|
+
function resolvePreset(name, options = {}) {
|
|
35
|
+
const factory = presetRegistry[name];
|
|
36
|
+
if (!factory) {
|
|
37
|
+
const available = Object.keys(presetRegistry).join(", ");
|
|
38
|
+
throw new Error(`Unknown preset: '${name}'\nAvailable presets: ${available}\nDocs: https://github.com/classytic/arc#presets`);
|
|
39
|
+
}
|
|
40
|
+
return factory(options);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Register a custom preset
|
|
44
|
+
*/
|
|
45
|
+
function registerPreset(name, factory, options) {
|
|
46
|
+
if (presetRegistry[name] && !options?.override) throw new Error(`Preset '${name}' already exists. Pass { override: true } to replace.`);
|
|
47
|
+
presetRegistry[name] = factory;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get all available preset names
|
|
51
|
+
*/
|
|
52
|
+
function getAvailablePresets() {
|
|
53
|
+
return Object.keys(presetRegistry);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validate that preset combinations don't conflict.
|
|
57
|
+
* Detects route collisions (same method + path from different presets).
|
|
58
|
+
*/
|
|
59
|
+
function validatePresetCombination(presets) {
|
|
60
|
+
const conflicts = [];
|
|
61
|
+
const routeMap = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const preset of presets) {
|
|
63
|
+
const name = preset.name ?? "unknown";
|
|
64
|
+
const routes = typeof preset.additionalRoutes === "function" ? preset.additionalRoutes({}) : preset.additionalRoutes ?? [];
|
|
65
|
+
for (const route of routes) {
|
|
66
|
+
const key = `${route.method} ${route.path}`;
|
|
67
|
+
const existing = routeMap.get(key);
|
|
68
|
+
if (existing) conflicts.push({
|
|
69
|
+
presets: [existing, name],
|
|
70
|
+
message: `Both '${existing}' and '${name}' define route ${key}`,
|
|
71
|
+
severity: "error"
|
|
72
|
+
});
|
|
73
|
+
routeMap.set(key, name);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return conflicts;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Apply presets to resource config.
|
|
80
|
+
* Validates preset combinations for conflicts before merging.
|
|
81
|
+
*/
|
|
82
|
+
function applyPresets(config, presets = []) {
|
|
83
|
+
let result = { ...config };
|
|
84
|
+
const resolved = presets.map(resolvePresetInput);
|
|
85
|
+
const errors = validatePresetCombination(resolved).filter((c) => c.severity === "error");
|
|
86
|
+
if (errors.length > 0) throw new Error(`[Arc] Resource '${config.name}' preset conflicts:\n` + errors.map((c) => ` - ${c.message}`).join("\n"));
|
|
87
|
+
for (const preset of resolved) result = mergePreset(result, preset);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Resolve preset input to PresetResult
|
|
92
|
+
*/
|
|
93
|
+
function resolvePresetInput(preset) {
|
|
94
|
+
if (typeof preset === "object" && ("middlewares" in preset || "additionalRoutes" in preset)) return preset;
|
|
95
|
+
if (typeof preset === "object" && "name" in preset) {
|
|
96
|
+
const { name, ...options } = preset;
|
|
97
|
+
return resolvePreset(name, options);
|
|
98
|
+
}
|
|
99
|
+
return resolvePreset(preset);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Merge preset into config
|
|
103
|
+
*/
|
|
104
|
+
function mergePreset(config, preset) {
|
|
105
|
+
const result = { ...config };
|
|
106
|
+
if (preset.additionalRoutes) {
|
|
107
|
+
const routes = typeof preset.additionalRoutes === "function" ? preset.additionalRoutes(config.permissions ?? {}) : preset.additionalRoutes;
|
|
108
|
+
result.additionalRoutes = [...result.additionalRoutes ?? [], ...routes];
|
|
109
|
+
}
|
|
110
|
+
if (preset.middlewares) {
|
|
111
|
+
result.middlewares = result.middlewares ?? {};
|
|
112
|
+
for (const [op, mws] of Object.entries(preset.middlewares)) {
|
|
113
|
+
const key = op;
|
|
114
|
+
result.middlewares[key] = [...result.middlewares[key] ?? [], ...mws ?? []];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (preset.schemaOptions) result.schemaOptions = {
|
|
118
|
+
...result.schemaOptions,
|
|
119
|
+
...preset.schemaOptions,
|
|
120
|
+
fieldRules: {
|
|
121
|
+
...result.schemaOptions?.fieldRules,
|
|
122
|
+
...preset.schemaOptions?.fieldRules
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
if (preset.controllerOptions) result._controllerOptions = {
|
|
126
|
+
...result._controllerOptions,
|
|
127
|
+
...preset.controllerOptions
|
|
128
|
+
};
|
|
129
|
+
if (preset.hooks && preset.hooks.length > 0) {
|
|
130
|
+
result._hooks = result._hooks ?? [];
|
|
131
|
+
for (const hook of preset.hooks) result._hooks.push({
|
|
132
|
+
presetName: preset.name,
|
|
133
|
+
operation: hook.operation,
|
|
134
|
+
phase: hook.phase,
|
|
135
|
+
handler: hook.handler,
|
|
136
|
+
priority: hook.priority
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
export { applyPresets, auditedPreset, flexibleMultiTenantPreset, getAvailablePresets, getPreset, multiTenantPreset, ownedByUserPreset, registerPreset, slugLookupPreset, softDeletePreset, treePreset };
|
|
144
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/presets/index.ts"],"sourcesContent":["/**\n * Presets Module\n *\n * Reusable resource configurations that add routes, middlewares, and schema options.\n *\n * @example\n * import { defineResource } from '@classytic/arc';\n *\n * // Using preset strings (resolved internally)\n * defineResource({\n * presets: ['softDelete', 'slugLookup'],\n * });\n *\n * // Using preset functions with options\n * import { softDeletePreset, treePreset } from '@classytic/arc/presets';\n *\n * defineResource({\n * presets: [\n * softDeletePreset(),\n * treePreset({ parentField: 'parentSlug' }),\n * ],\n * });\n */\n\nimport type {\n AdditionalRoute,\n AnyRecord,\n MiddlewareConfig,\n PresetResult,\n ResourceConfig,\n RouteSchemaOptions,\n} from '../types/index.js';\n\n// Export preset functions\nexport { softDeletePreset } from './softDelete.js';\n\n\nexport { slugLookupPreset } from './slugLookup.js';\nexport type { SlugLookupOptions } from './slugLookup.js';\n\nexport { ownedByUserPreset } from './ownedByUser.js';\nexport type { OwnedByUserOptions } from './ownedByUser.js';\n\nexport { multiTenantPreset } from './multiTenant.js';\nexport type { MultiTenantOptions } from './multiTenant.js';\n\n/**\n * Convenience alias for multiTenantPreset with public list/get routes\n * Equivalent to: multiTenantPreset({ allowPublic: ['list', 'get'] })\n */\nexport const flexibleMultiTenantPreset = (\n options: Omit<import('./multiTenant.js').MultiTenantOptions, 'allowPublic'> = {}\n) => multiTenantPreset({ ...options, allowPublic: ['list', 'get'] });\n\nexport { treePreset } from './tree.js';\nexport type { TreeOptions } from './tree.js';\n\nexport { auditedPreset } from './audited.js';\nexport type { AuditedPresetOptions } from './audited.js';\n\n// Export preset type interfaces for type safety\nexport type {\n ISoftDeleteController,\n ISlugLookupController,\n ITreeController,\n IOwnedByUserPreset,\n IMultiTenantPreset,\n IAuditedPreset,\n IPresetController,\n} from './types.js';\n\n// Import preset implementations for sync resolution\nimport { softDeletePreset } from './softDelete.js';\nimport { slugLookupPreset } from './slugLookup.js';\nimport { ownedByUserPreset } from './ownedByUser.js';\nimport { multiTenantPreset } from './multiTenant.js';\nimport { treePreset } from './tree.js';\nimport { auditedPreset } from './audited.js';\n\n// ============================================================================\n// Preset Registry\n// ============================================================================\n\ntype PresetFactory = (options?: AnyRecord) => PresetResult;\n\nconst presetRegistry: Record<string, PresetFactory> = {\n softDelete: softDeletePreset,\n slugLookup: slugLookupPreset,\n ownedByUser: ownedByUserPreset,\n multiTenant: multiTenantPreset,\n tree: treePreset,\n audited: auditedPreset,\n};\n\n/**\n * Get preset by name with options\n */\nexport function getPreset(nameOrConfig: string | { name: string; [key: string]: unknown }): PresetResult {\n if (typeof nameOrConfig === 'object' && nameOrConfig.name) {\n const { name, ...options } = nameOrConfig;\n return resolvePreset(name, options);\n }\n\n return resolvePreset(nameOrConfig as string);\n}\n\n/**\n * Resolve preset by name\n */\nfunction resolvePreset(name: string, options: AnyRecord = {}): PresetResult {\n const factory = presetRegistry[name];\n\n if (!factory) {\n const available = Object.keys(presetRegistry).join(', ');\n throw new Error(\n `Unknown preset: '${name}'\\n` +\n `Available presets: ${available}\\n` +\n `Docs: https://github.com/classytic/arc#presets`\n );\n }\n\n return factory(options);\n}\n\n/**\n * Register a custom preset\n */\nexport function registerPreset(\n name: string,\n factory: PresetFactory,\n options?: { override?: boolean },\n): void {\n if (presetRegistry[name] && !options?.override) {\n throw new Error(\n `Preset '${name}' already exists. Pass { override: true } to replace.`,\n );\n }\n presetRegistry[name] = factory;\n}\n\n/**\n * Get all available preset names\n */\nexport function getAvailablePresets(): string[] {\n return Object.keys(presetRegistry);\n}\n\n// ============================================================================\n// Apply Presets\n// ============================================================================\n\ntype PresetInput = string | PresetResult | { name: string; [key: string]: unknown };\n\n// ============================================================================\n// Preset Conflict Detection\n// ============================================================================\n\ninterface PresetConflict {\n presets: [string, string];\n message: string;\n severity: 'error' | 'warning';\n}\n\n/**\n * Validate that preset combinations don't conflict.\n * Detects route collisions (same method + path from different presets).\n */\nfunction validatePresetCombination(presets: PresetResult[]): PresetConflict[] {\n const conflicts: PresetConflict[] = [];\n const routeMap = new Map<string, string>(); // \"METHOD /path\" -> preset name\n\n for (const preset of presets) {\n const name = preset.name ?? 'unknown';\n const routes: AdditionalRoute[] = typeof preset.additionalRoutes === 'function'\n ? preset.additionalRoutes({})\n : (preset.additionalRoutes ?? []);\n\n for (const route of routes) {\n const key = `${route.method} ${route.path}`;\n const existing = routeMap.get(key);\n if (existing) {\n conflicts.push({\n presets: [existing, name],\n message: `Both '${existing}' and '${name}' define route ${key}`,\n severity: 'error',\n });\n }\n routeMap.set(key, name);\n }\n }\n\n return conflicts;\n}\n\n/**\n * Apply presets to resource config.\n * Validates preset combinations for conflicts before merging.\n */\nexport function applyPresets<TDoc = AnyRecord>(\n config: ResourceConfig<TDoc>,\n presets: PresetInput[] = []\n): ResourceConfig<TDoc> {\n let result = { ...config };\n\n // Resolve all presets first for validation\n const resolved = presets.map(resolvePresetInput);\n\n // Validate combinations — fail-fast on route collisions\n const conflicts = validatePresetCombination(resolved);\n const errors = conflicts.filter((c) => c.severity === 'error');\n if (errors.length > 0) {\n throw new Error(\n `[Arc] Resource '${config.name}' preset conflicts:\\n` +\n errors.map((c) => ` - ${c.message}`).join('\\n'),\n );\n }\n\n for (const preset of resolved) {\n result = mergePreset(result, preset) as ResourceConfig<TDoc>;\n }\n\n return result;\n}\n\n/**\n * Resolve preset input to PresetResult\n */\nfunction resolvePresetInput(preset: PresetInput): PresetResult {\n // Check if already a fully-resolved PresetResult (has middlewares or additionalRoutes)\n // This allows custom presets to be passed directly without registry lookup\n if (typeof preset === 'object' && ('middlewares' in preset || 'additionalRoutes' in preset)) {\n return preset as PresetResult;\n }\n\n // Object with name and options (for registry lookup)\n if (typeof preset === 'object' && 'name' in preset) {\n const { name, ...options } = preset as { name: string; [key: string]: unknown };\n return resolvePreset(name, options);\n }\n\n // String preset name\n return resolvePreset(preset);\n}\n\n/** Extended config with internal fields */\ninterface ExtendedResourceConfig<TDoc = AnyRecord> extends ResourceConfig<TDoc> {\n _controllerOptions?: {\n slugField?: string;\n parentField?: string;\n [key: string]: unknown;\n };\n _hooks?: Array<{\n presetName: string;\n operation: 'create' | 'update' | 'delete' | 'read' | 'list';\n phase: 'before' | 'after';\n handler: (ctx: {\n resource: string;\n operation: string;\n phase: string;\n data?: AnyRecord;\n result?: AnyRecord | AnyRecord[];\n user?: { id: string; email: string; [key: string]: unknown };\n context?: { organizationId?: string | null; [key: string]: unknown };\n meta?: AnyRecord;\n }) => void | Promise<void> | AnyRecord | Promise<AnyRecord>;\n priority?: number;\n }>;\n}\n\n/**\n * Merge preset into config\n */\nfunction mergePreset<TDoc = AnyRecord>(\n config: ResourceConfig<TDoc>,\n preset: PresetResult\n): ResourceConfig<TDoc> {\n const result = { ...config } as ExtendedResourceConfig<TDoc>;\n\n // Merge additional routes\n if (preset.additionalRoutes) {\n const routes: AdditionalRoute[] = typeof preset.additionalRoutes === 'function'\n ? preset.additionalRoutes(config.permissions ?? {})\n : preset.additionalRoutes;\n\n result.additionalRoutes = [\n ...(result.additionalRoutes ?? []),\n ...routes,\n ];\n }\n\n // Merge middlewares\n if (preset.middlewares) {\n result.middlewares = result.middlewares ?? {};\n for (const [op, mws] of Object.entries(preset.middlewares)) {\n const key = op as keyof MiddlewareConfig;\n result.middlewares[key] = [\n ...(result.middlewares[key] ?? []),\n ...(mws ?? []),\n ];\n }\n }\n\n // Merge schema options (deep-merge fieldRules so presets accumulate rules)\n if (preset.schemaOptions) {\n result.schemaOptions = {\n ...result.schemaOptions,\n ...preset.schemaOptions,\n fieldRules: {\n ...result.schemaOptions?.fieldRules,\n ...preset.schemaOptions?.fieldRules,\n },\n } as RouteSchemaOptions;\n }\n\n // Merge controller options (slugField, parentField, etc.)\n if (preset.controllerOptions) {\n result._controllerOptions = {\n ...result._controllerOptions,\n ...preset.controllerOptions,\n };\n }\n\n // Collect hooks from preset\n if (preset.hooks && preset.hooks.length > 0) {\n result._hooks = result._hooks ?? [];\n for (const hook of preset.hooks) {\n result._hooks.push({\n presetName: preset.name,\n operation: hook.operation,\n phase: hook.phase,\n handler: hook.handler,\n priority: hook.priority,\n });\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;AAkDA,MAAa,6BACX,UAA8E,EAAE,KAC7E,kBAAkB;CAAE,GAAG;CAAS,aAAa,CAAC,QAAQ,MAAM;CAAE,CAAC;AAiCpE,MAAM,iBAAgD;CACpD,YAAY;CACZ,YAAY;CACZ,aAAa;CACb,aAAa;CACb,MAAM;CACN,SAAS;CACV;;;;AAKD,SAAgB,UAAU,cAA+E;AACvG,KAAI,OAAO,iBAAiB,YAAY,aAAa,MAAM;EACzD,MAAM,EAAE,MAAM,GAAG,YAAY;AAC7B,SAAO,cAAc,MAAM,QAAQ;;AAGrC,QAAO,cAAc,aAAuB;;;;;AAM9C,SAAS,cAAc,MAAc,UAAqB,EAAE,EAAgB;CAC1E,MAAM,UAAU,eAAe;AAE/B,KAAI,CAAC,SAAS;EACZ,MAAM,YAAY,OAAO,KAAK,eAAe,CAAC,KAAK,KAAK;AACxD,QAAM,IAAI,MACR,oBAAoB,KAAK,wBACH,UAAU,kDAEjC;;AAGH,QAAO,QAAQ,QAAQ;;;;;AAMzB,SAAgB,eACd,MACA,SACA,SACM;AACN,KAAI,eAAe,SAAS,CAAC,SAAS,SACpC,OAAM,IAAI,MACR,WAAW,KAAK,uDACjB;AAEH,gBAAe,QAAQ;;;;;AAMzB,SAAgB,sBAAgC;AAC9C,QAAO,OAAO,KAAK,eAAe;;;;;;AAuBpC,SAAS,0BAA0B,SAA2C;CAC5E,MAAM,YAA8B,EAAE;CACtC,MAAM,2BAAW,IAAI,KAAqB;AAE1C,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,OAAO,QAAQ;EAC5B,MAAM,SAA4B,OAAO,OAAO,qBAAqB,aACjE,OAAO,iBAAiB,EAAE,CAAC,GAC1B,OAAO,oBAAoB,EAAE;AAElC,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,MAAM,GAAG,MAAM,OAAO,GAAG,MAAM;GACrC,MAAM,WAAW,SAAS,IAAI,IAAI;AAClC,OAAI,SACF,WAAU,KAAK;IACb,SAAS,CAAC,UAAU,KAAK;IACzB,SAAS,SAAS,SAAS,SAAS,KAAK,iBAAiB;IAC1D,UAAU;IACX,CAAC;AAEJ,YAAS,IAAI,KAAK,KAAK;;;AAI3B,QAAO;;;;;;AAOT,SAAgB,aACd,QACA,UAAyB,EAAE,EACL;CACtB,IAAI,SAAS,EAAE,GAAG,QAAQ;CAG1B,MAAM,WAAW,QAAQ,IAAI,mBAAmB;CAIhD,MAAM,SADY,0BAA0B,SAAS,CAC5B,QAAQ,MAAM,EAAE,aAAa,QAAQ;AAC9D,KAAI,OAAO,SAAS,EAClB,OAAM,IAAI,MACR,mBAAmB,OAAO,KAAK,yBAC/B,OAAO,KAAK,MAAM,OAAO,EAAE,UAAU,CAAC,KAAK,KAAK,CACjD;AAGH,MAAK,MAAM,UAAU,SACnB,UAAS,YAAY,QAAQ,OAAO;AAGtC,QAAO;;;;;AAMT,SAAS,mBAAmB,QAAmC;AAG7D,KAAI,OAAO,WAAW,aAAa,iBAAiB,UAAU,sBAAsB,QAClF,QAAO;AAIT,KAAI,OAAO,WAAW,YAAY,UAAU,QAAQ;EAClD,MAAM,EAAE,MAAM,GAAG,YAAY;AAC7B,SAAO,cAAc,MAAM,QAAQ;;AAIrC,QAAO,cAAc,OAAO;;;;;AA+B9B,SAAS,YACP,QACA,QACsB;CACtB,MAAM,SAAS,EAAE,GAAG,QAAQ;AAG5B,KAAI,OAAO,kBAAkB;EAC3B,MAAM,SAA4B,OAAO,OAAO,qBAAqB,aACjE,OAAO,iBAAiB,OAAO,eAAe,EAAE,CAAC,GACjD,OAAO;AAEX,SAAO,mBAAmB,CACxB,GAAI,OAAO,oBAAoB,EAAE,EACjC,GAAG,OACJ;;AAIH,KAAI,OAAO,aAAa;AACtB,SAAO,cAAc,OAAO,eAAe,EAAE;AAC7C,OAAK,MAAM,CAAC,IAAI,QAAQ,OAAO,QAAQ,OAAO,YAAY,EAAE;GAC1D,MAAM,MAAM;AACZ,UAAO,YAAY,OAAO,CACxB,GAAI,OAAO,YAAY,QAAQ,EAAE,EACjC,GAAI,OAAO,EAAE,CACd;;;AAKL,KAAI,OAAO,cACT,QAAO,gBAAgB;EACrB,GAAG,OAAO;EACV,GAAG,OAAO;EACV,YAAY;GACV,GAAG,OAAO,eAAe;GACzB,GAAG,OAAO,eAAe;GAC1B;EACF;AAIH,KAAI,OAAO,kBACT,QAAO,qBAAqB;EAC1B,GAAG,OAAO;EACV,GAAG,OAAO;EACX;AAIH,KAAI,OAAO,SAAS,OAAO,MAAM,SAAS,GAAG;AAC3C,SAAO,SAAS,OAAO,UAAU,EAAE;AACnC,OAAK,MAAM,QAAQ,OAAO,MACxB,QAAO,OAAO,KAAK;GACjB,YAAY,OAAO;GACnB,WAAW,KAAK;GAChB,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,UAAU,KAAK;GAChB,CAAC;;AAIN,QAAO"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "../elevation-B_2dRLVP.mjs";
|
|
2
|
+
import "../interface-Ch8HU9uM.mjs";
|
|
3
|
+
import "../types-aYB4V7uN.mjs";
|
|
4
|
+
import { CrudRouteKey, PresetResult } from "../types/index.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/presets/multiTenant.d.ts
|
|
7
|
+
interface MultiTenantOptions {
|
|
8
|
+
/** Field name in database (default: 'organizationId') */
|
|
9
|
+
tenantField?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Routes that allow public access (no auth required)
|
|
12
|
+
* When a route is in this array:
|
|
13
|
+
* - If no org context: allow through without filtering (public data)
|
|
14
|
+
* - If org context present: require auth and apply filter
|
|
15
|
+
*
|
|
16
|
+
* @default [] (strict mode - all routes require auth)
|
|
17
|
+
* @example
|
|
18
|
+
* multiTenantPreset({ allowPublic: ['list', 'get'] })
|
|
19
|
+
*/
|
|
20
|
+
allowPublic?: CrudRouteKey[];
|
|
21
|
+
}
|
|
22
|
+
declare function multiTenantPreset(options?: MultiTenantOptions): PresetResult;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { MultiTenantOptions, multiTenantPreset as default, multiTenantPreset };
|
|
25
|
+
//# sourceMappingURL=multiTenant.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiTenant.d.mts","names":[],"sources":["../../src/presets/multiTenant.ts"],"mappings":";;;;;;UAmBiB,kBAAA;EAAkB;EAEjC,WAAA;EAY0B;;;;;;AA6H5B;;;;EA7HE,WAAA,GAAc,YAAA;AAAA;AAAA,iBA6HA,iBAAA,CAAkB,OAAA,GAAS,kBAAA,GAA0B,YAAA"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { o as DEFAULT_TENANT_FIELD } from "../constants-DdXFXQtN.mjs";
|
|
2
|
+
import { c as isElevated, l as isMember, n as PUBLIC_SCOPE, r as getOrgId } from "../types-Beqn1Un7.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/presets/multiTenant.ts
|
|
5
|
+
/** Read request.scope safely */
|
|
6
|
+
function getScope(request) {
|
|
7
|
+
return request.scope ?? PUBLIC_SCOPE;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Create tenant filter middleware
|
|
11
|
+
* Adds tenant filter to query for list/get operations.
|
|
12
|
+
* Reads `request.scope` for org context and elevation bypass.
|
|
13
|
+
*/
|
|
14
|
+
function createTenantFilter(tenantField) {
|
|
15
|
+
return async (request, reply) => {
|
|
16
|
+
const scope = getScope(request);
|
|
17
|
+
if (isElevated(scope)) {
|
|
18
|
+
const orgId = getOrgId(scope);
|
|
19
|
+
if (orgId) request._policyFilters = {
|
|
20
|
+
...request._policyFilters ?? {},
|
|
21
|
+
[tenantField]: orgId
|
|
22
|
+
};
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (isMember(scope)) {
|
|
26
|
+
request._policyFilters = {
|
|
27
|
+
...request._policyFilters ?? {},
|
|
28
|
+
[tenantField]: scope.organizationId
|
|
29
|
+
};
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (scope.kind === "public") {
|
|
33
|
+
reply.code(401).send({
|
|
34
|
+
success: false,
|
|
35
|
+
error: "Unauthorized",
|
|
36
|
+
message: "Authentication required for multi-tenant resources"
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
reply.code(403).send({
|
|
41
|
+
success: false,
|
|
42
|
+
error: "Forbidden",
|
|
43
|
+
message: "Organization context required for this operation"
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create flexible tenant filter middleware
|
|
49
|
+
* For routes in allowPublic: only filter when org context is present
|
|
50
|
+
* No org context = allow through (public data)
|
|
51
|
+
* Org context present = require auth and apply filter
|
|
52
|
+
*/
|
|
53
|
+
function createFlexibleTenantFilter(tenantField) {
|
|
54
|
+
return async (request, reply) => {
|
|
55
|
+
const scope = getScope(request);
|
|
56
|
+
if (isElevated(scope)) {
|
|
57
|
+
const orgId = getOrgId(scope);
|
|
58
|
+
if (orgId) request._policyFilters = {
|
|
59
|
+
...request._policyFilters ?? {},
|
|
60
|
+
[tenantField]: orgId
|
|
61
|
+
};
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (isMember(scope)) {
|
|
65
|
+
request._policyFilters = {
|
|
66
|
+
...request._policyFilters ?? {},
|
|
67
|
+
[tenantField]: scope.organizationId
|
|
68
|
+
};
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create tenant injection middleware
|
|
75
|
+
* Injects tenant ID into request body on create.
|
|
76
|
+
* Reads `request.scope` for org context.
|
|
77
|
+
*/
|
|
78
|
+
function createTenantInjection(tenantField) {
|
|
79
|
+
return async (request, reply) => {
|
|
80
|
+
const scope = getScope(request);
|
|
81
|
+
const orgId = getOrgId(scope);
|
|
82
|
+
if (isElevated(scope) && !orgId) return;
|
|
83
|
+
if (!orgId) {
|
|
84
|
+
reply.code(403).send({
|
|
85
|
+
success: false,
|
|
86
|
+
error: "Forbidden",
|
|
87
|
+
message: "Organization context required to create resources"
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (request.body) request.body[tenantField] = orgId;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function multiTenantPreset(options = {}) {
|
|
95
|
+
const { tenantField = DEFAULT_TENANT_FIELD, allowPublic = [] } = options;
|
|
96
|
+
const strictTenantFilter = createTenantFilter(tenantField);
|
|
97
|
+
const flexibleTenantFilter = createFlexibleTenantFilter(tenantField);
|
|
98
|
+
const tenantInjection = createTenantInjection(tenantField);
|
|
99
|
+
const getFilter = (route) => allowPublic.includes(route) ? flexibleTenantFilter : strictTenantFilter;
|
|
100
|
+
return {
|
|
101
|
+
name: "multiTenant",
|
|
102
|
+
middlewares: {
|
|
103
|
+
list: [getFilter("list")],
|
|
104
|
+
get: [getFilter("get")],
|
|
105
|
+
create: [tenantInjection],
|
|
106
|
+
update: [getFilter("update")],
|
|
107
|
+
delete: [getFilter("delete")]
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//#endregion
|
|
113
|
+
export { multiTenantPreset as default, multiTenantPreset };
|
|
114
|
+
//# sourceMappingURL=multiTenant.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiTenant.mjs","names":[],"sources":["../../src/presets/multiTenant.ts"],"sourcesContent":["/**\n * Multi-Tenant Preset\n *\n * Adds tenant (organization) filtering and injection middlewares.\n */\n\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type {\n AnyRecord,\n CrudRouteKey,\n MiddlewareConfig,\n PresetResult,\n RequestWithExtras,\n RouteHandler,\n} from '../types/index.js';\nimport { DEFAULT_TENANT_FIELD } from '../constants.js';\nimport type { RequestScope } from '../scope/types.js';\nimport { isMember, isElevated, getOrgId, PUBLIC_SCOPE } from '../scope/types.js';\n\nexport interface MultiTenantOptions {\n /** Field name in database (default: 'organizationId') */\n tenantField?: string;\n\n /**\n * Routes that allow public access (no auth required)\n * When a route is in this array:\n * - If no org context: allow through without filtering (public data)\n * - If org context present: require auth and apply filter\n *\n * @default [] (strict mode - all routes require auth)\n * @example\n * multiTenantPreset({ allowPublic: ['list', 'get'] })\n */\n allowPublic?: CrudRouteKey[];\n}\n\n/** Read request.scope safely */\nfunction getScope(request: FastifyRequest): RequestScope {\n return request.scope ?? PUBLIC_SCOPE;\n}\n\n/**\n * Create tenant filter middleware\n * Adds tenant filter to query for list/get operations.\n * Reads `request.scope` for org context and elevation bypass.\n */\nfunction createTenantFilter(tenantField: string): RouteHandler {\n return async (request: RequestWithExtras, reply: FastifyReply): Promise<void> => {\n const scope = getScope(request);\n\n // Elevated without org → no filter (admin viewing all)\n // Elevated with org → filter by that org\n if (isElevated(scope)) {\n const orgId = getOrgId(scope);\n if (orgId) {\n request._policyFilters = {\n ...(request._policyFilters ?? {}),\n [tenantField]: orgId,\n };\n }\n return;\n }\n\n // Member → filter by scope.organizationId\n if (isMember(scope)) {\n request._policyFilters = {\n ...(request._policyFilters ?? {}),\n [tenantField]: scope.organizationId,\n };\n return;\n }\n\n // authenticated / public → 403 (multi-tenant requires org context)\n if (scope.kind === 'public') {\n reply.code(401).send({\n success: false,\n error: 'Unauthorized',\n message: 'Authentication required for multi-tenant resources',\n });\n return;\n }\n\n // authenticated but no org → 403\n reply.code(403).send({\n success: false,\n error: 'Forbidden',\n message: 'Organization context required for this operation',\n });\n };\n}\n\n/**\n * Create flexible tenant filter middleware\n * For routes in allowPublic: only filter when org context is present\n * No org context = allow through (public data)\n * Org context present = require auth and apply filter\n */\nfunction createFlexibleTenantFilter(tenantField: string): RouteHandler {\n return async (request: RequestWithExtras, reply: FastifyReply): Promise<void> => {\n const scope = getScope(request);\n\n // Elevated without org → no filter (admin viewing all)\n if (isElevated(scope)) {\n const orgId = getOrgId(scope);\n if (orgId) {\n request._policyFilters = {\n ...(request._policyFilters ?? {}),\n [tenantField]: orgId,\n };\n }\n return;\n }\n\n // Member → filter by scope.organizationId\n if (isMember(scope)) {\n request._policyFilters = {\n ...(request._policyFilters ?? {}),\n [tenantField]: scope.organizationId,\n };\n return;\n }\n\n // authenticated / public with no org context → allow through (public data)\n return;\n };\n}\n\n/**\n * Create tenant injection middleware\n * Injects tenant ID into request body on create.\n * Reads `request.scope` for org context.\n */\nfunction createTenantInjection(tenantField: string): RouteHandler {\n return async (request: RequestWithExtras, reply: FastifyReply): Promise<void> => {\n const scope = getScope(request);\n const orgId = getOrgId(scope);\n\n // Elevated without org → skip injection (admin cross-org operation)\n if (isElevated(scope) && !orgId) {\n return;\n }\n\n // Fail-closed: Require orgId to prevent orphaned data\n if (!orgId) {\n reply.code(403).send({\n success: false,\n error: 'Forbidden',\n message: 'Organization context required to create resources',\n });\n return;\n }\n\n if (request.body) {\n (request.body as AnyRecord)[tenantField] = orgId;\n }\n };\n}\n\nexport function multiTenantPreset(options: MultiTenantOptions = {}): PresetResult {\n const {\n tenantField = DEFAULT_TENANT_FIELD,\n allowPublic = [],\n } = options;\n\n // Create middleware variants\n const strictTenantFilter = createTenantFilter(tenantField);\n const flexibleTenantFilter = createFlexibleTenantFilter(tenantField);\n const tenantInjection = createTenantInjection(tenantField);\n\n // Helper to select appropriate filter based on allowPublic\n const getFilter = (route: CrudRouteKey): RouteHandler =>\n allowPublic.includes(route) ? flexibleTenantFilter : strictTenantFilter;\n\n return {\n name: 'multiTenant',\n middlewares: {\n list: [getFilter('list')],\n get: [getFilter('get')],\n create: [tenantInjection],\n update: [getFilter('update')],\n delete: [getFilter('delete')],\n } as MiddlewareConfig,\n };\n}\n\nexport default multiTenantPreset;\n"],"mappings":";;;;;AAqCA,SAAS,SAAS,SAAuC;AACvD,QAAO,QAAQ,SAAS;;;;;;;AAQ1B,SAAS,mBAAmB,aAAmC;AAC7D,QAAO,OAAO,SAA4B,UAAuC;EAC/E,MAAM,QAAQ,SAAS,QAAQ;AAI/B,MAAI,WAAW,MAAM,EAAE;GACrB,MAAM,QAAQ,SAAS,MAAM;AAC7B,OAAI,MACF,SAAQ,iBAAiB;IACvB,GAAI,QAAQ,kBAAkB,EAAE;KAC/B,cAAc;IAChB;AAEH;;AAIF,MAAI,SAAS,MAAM,EAAE;AACnB,WAAQ,iBAAiB;IACvB,GAAI,QAAQ,kBAAkB,EAAE;KAC/B,cAAc,MAAM;IACtB;AACD;;AAIF,MAAI,MAAM,SAAS,UAAU;AAC3B,SAAM,KAAK,IAAI,CAAC,KAAK;IACnB,SAAS;IACT,OAAO;IACP,SAAS;IACV,CAAC;AACF;;AAIF,QAAM,KAAK,IAAI,CAAC,KAAK;GACnB,SAAS;GACT,OAAO;GACP,SAAS;GACV,CAAC;;;;;;;;;AAUN,SAAS,2BAA2B,aAAmC;AACrE,QAAO,OAAO,SAA4B,UAAuC;EAC/E,MAAM,QAAQ,SAAS,QAAQ;AAG/B,MAAI,WAAW,MAAM,EAAE;GACrB,MAAM,QAAQ,SAAS,MAAM;AAC7B,OAAI,MACF,SAAQ,iBAAiB;IACvB,GAAI,QAAQ,kBAAkB,EAAE;KAC/B,cAAc;IAChB;AAEH;;AAIF,MAAI,SAAS,MAAM,EAAE;AACnB,WAAQ,iBAAiB;IACvB,GAAI,QAAQ,kBAAkB,EAAE;KAC/B,cAAc,MAAM;IACtB;AACD;;;;;;;;;AAaN,SAAS,sBAAsB,aAAmC;AAChE,QAAO,OAAO,SAA4B,UAAuC;EAC/E,MAAM,QAAQ,SAAS,QAAQ;EAC/B,MAAM,QAAQ,SAAS,MAAM;AAG7B,MAAI,WAAW,MAAM,IAAI,CAAC,MACxB;AAIF,MAAI,CAAC,OAAO;AACV,SAAM,KAAK,IAAI,CAAC,KAAK;IACnB,SAAS;IACT,OAAO;IACP,SAAS;IACV,CAAC;AACF;;AAGF,MAAI,QAAQ,KACV,CAAC,QAAQ,KAAmB,eAAe;;;AAKjD,SAAgB,kBAAkB,UAA8B,EAAE,EAAgB;CAChF,MAAM,EACJ,cAAc,sBACd,cAAc,EAAE,KACd;CAGJ,MAAM,qBAAqB,mBAAmB,YAAY;CAC1D,MAAM,uBAAuB,2BAA2B,YAAY;CACpE,MAAM,kBAAkB,sBAAsB,YAAY;CAG1D,MAAM,aAAa,UACjB,YAAY,SAAS,MAAM,GAAG,uBAAuB;AAEvD,QAAO;EACL,MAAM;EACN,aAAa;GACX,MAAM,CAAC,UAAU,OAAO,CAAC;GACzB,KAAK,CAAC,UAAU,MAAM,CAAC;GACvB,QAAQ,CAAC,gBAAgB;GACzB,QAAQ,CAAC,UAAU,SAAS,CAAC;GAC7B,QAAQ,CAAC,UAAU,SAAS,CAAC;GAC9B;EACF"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
|
|
2
|
+
import { allowPublic, anyOf, requireAuth, requireOwnership, requireRoles } from "./permissions/index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/permissions/presets.ts
|
|
5
|
+
var presets_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
adminOnly: () => adminOnly,
|
|
7
|
+
authenticated: () => authenticated,
|
|
8
|
+
fullPublic: () => fullPublic,
|
|
9
|
+
ownerWithAdminBypass: () => ownerWithAdminBypass,
|
|
10
|
+
publicRead: () => publicRead,
|
|
11
|
+
publicReadAdminWrite: () => publicReadAdminWrite,
|
|
12
|
+
readOnly: () => readOnly
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Merge a base preset with user overrides.
|
|
16
|
+
* Overrides replace individual operations — undefined values don't clear them.
|
|
17
|
+
*/
|
|
18
|
+
function withOverrides(base, overrides) {
|
|
19
|
+
if (!overrides) return base;
|
|
20
|
+
const filtered = Object.fromEntries(Object.entries(overrides).filter(([, v]) => v !== void 0));
|
|
21
|
+
return {
|
|
22
|
+
...base,
|
|
23
|
+
...filtered
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Public read, authenticated write.
|
|
28
|
+
* list + get = allowPublic(), create + update + delete = requireAuth()
|
|
29
|
+
*/
|
|
30
|
+
function publicRead(overrides) {
|
|
31
|
+
return withOverrides({
|
|
32
|
+
list: allowPublic(),
|
|
33
|
+
get: allowPublic(),
|
|
34
|
+
create: requireAuth(),
|
|
35
|
+
update: requireAuth(),
|
|
36
|
+
delete: requireAuth()
|
|
37
|
+
}, overrides);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Public read, admin write.
|
|
41
|
+
* list + get = allowPublic(), create + update + delete = requireRoles(['admin'])
|
|
42
|
+
*/
|
|
43
|
+
function publicReadAdminWrite(roles = ["admin"], overrides) {
|
|
44
|
+
return withOverrides({
|
|
45
|
+
list: allowPublic(),
|
|
46
|
+
get: allowPublic(),
|
|
47
|
+
create: requireRoles(roles),
|
|
48
|
+
update: requireRoles(roles),
|
|
49
|
+
delete: requireRoles(roles)
|
|
50
|
+
}, overrides);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* All operations require authentication.
|
|
54
|
+
*/
|
|
55
|
+
function authenticated(overrides) {
|
|
56
|
+
return withOverrides({
|
|
57
|
+
list: requireAuth(),
|
|
58
|
+
get: requireAuth(),
|
|
59
|
+
create: requireAuth(),
|
|
60
|
+
update: requireAuth(),
|
|
61
|
+
delete: requireAuth()
|
|
62
|
+
}, overrides);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* All operations require specific roles.
|
|
66
|
+
* @param roles - Required roles (user needs at least one). Default: ['admin']
|
|
67
|
+
*/
|
|
68
|
+
function adminOnly(roles = ["admin"], overrides) {
|
|
69
|
+
return withOverrides({
|
|
70
|
+
list: requireRoles(roles),
|
|
71
|
+
get: requireRoles(roles),
|
|
72
|
+
create: requireRoles(roles),
|
|
73
|
+
update: requireRoles(roles),
|
|
74
|
+
delete: requireRoles(roles)
|
|
75
|
+
}, overrides);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Owner-scoped with admin bypass.
|
|
79
|
+
* list = auth (scoped to owner), get = auth, create = auth,
|
|
80
|
+
* update + delete = ownership check with admin bypass.
|
|
81
|
+
*
|
|
82
|
+
* @param ownerField - Field containing owner ID (default: 'userId')
|
|
83
|
+
* @param bypassRoles - Roles that bypass ownership check (default: ['admin'])
|
|
84
|
+
*/
|
|
85
|
+
function ownerWithAdminBypass(ownerField = "userId", bypassRoles = ["admin"], overrides) {
|
|
86
|
+
return withOverrides({
|
|
87
|
+
list: requireAuth(),
|
|
88
|
+
get: requireAuth(),
|
|
89
|
+
create: requireAuth(),
|
|
90
|
+
update: anyOf(requireRoles(bypassRoles), requireOwnership(ownerField)),
|
|
91
|
+
delete: anyOf(requireRoles(bypassRoles), requireOwnership(ownerField))
|
|
92
|
+
}, overrides);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Full public access — no auth required for any operation.
|
|
96
|
+
* Use sparingly (dev/testing, truly public APIs).
|
|
97
|
+
*/
|
|
98
|
+
function fullPublic(overrides) {
|
|
99
|
+
return withOverrides({
|
|
100
|
+
list: allowPublic(),
|
|
101
|
+
get: allowPublic(),
|
|
102
|
+
create: allowPublic(),
|
|
103
|
+
update: allowPublic(),
|
|
104
|
+
delete: allowPublic()
|
|
105
|
+
}, overrides);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Read-only: list + get authenticated, write operations denied.
|
|
109
|
+
* Useful for computed/derived resources.
|
|
110
|
+
*/
|
|
111
|
+
function readOnly(overrides) {
|
|
112
|
+
return withOverrides({
|
|
113
|
+
list: requireAuth(),
|
|
114
|
+
get: requireAuth()
|
|
115
|
+
}, overrides);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
export { presets_exports as a, readOnly as c, ownerWithAdminBypass as i, authenticated as n, publicRead as o, fullPublic as r, publicReadAdminWrite as s, adminOnly as t };
|
|
120
|
+
//# sourceMappingURL=presets-BITljm96.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets-BITljm96.mjs","names":[],"sources":["../src/permissions/presets.ts"],"sourcesContent":["/**\n * Permission Presets — Common permission patterns in one call.\n *\n * Reduces 5 lines of permission declarations to 1.\n * Each preset returns a ResourcePermissions object that can be\n * spread or overridden per-operation.\n *\n * @example\n * ```typescript\n * import { permissions } from '@classytic/arc';\n *\n * // Public read, authenticated write\n * defineResource({ name: 'product', permissions: permissions.publicRead() });\n *\n * // Override specific operations\n * defineResource({\n * name: 'product',\n * permissions: permissions.publicRead({ delete: requireRoles(['superadmin']) }),\n * });\n * ```\n */\n\nimport type { PermissionCheck } from \"./types.js\";\nimport {\n allowPublic,\n requireAuth,\n requireRoles,\n requireOwnership,\n anyOf,\n} from \"./index.js\";\n\n/**\n * ResourcePermissions shape — matches the type in types/index.ts\n */\ninterface ResourcePermissions<TDoc = any> {\n list?: PermissionCheck<TDoc>;\n get?: PermissionCheck<TDoc>;\n create?: PermissionCheck<TDoc>;\n update?: PermissionCheck<TDoc>;\n delete?: PermissionCheck<TDoc>;\n}\n\ntype PermissionOverrides<TDoc = any> = Partial<ResourcePermissions<TDoc>>;\n\n/**\n * Merge a base preset with user overrides.\n * Overrides replace individual operations — undefined values don't clear them.\n */\nfunction withOverrides<TDoc = any>(\n base: ResourcePermissions<TDoc>,\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n if (!overrides) return base;\n const filtered = Object.fromEntries(\n Object.entries(overrides).filter(([, v]) => v !== undefined),\n );\n return { ...base, ...filtered };\n}\n\n/**\n * Public read, authenticated write.\n * list + get = allowPublic(), create + update + delete = requireAuth()\n */\nexport function publicRead<TDoc = any>(\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n return withOverrides(\n {\n list: allowPublic(),\n get: allowPublic(),\n create: requireAuth(),\n update: requireAuth(),\n delete: requireAuth(),\n },\n overrides,\n );\n}\n\n/**\n * Public read, admin write.\n * list + get = allowPublic(), create + update + delete = requireRoles(['admin'])\n */\nexport function publicReadAdminWrite<TDoc = any>(\n roles: readonly string[] = [\"admin\"],\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n return withOverrides(\n {\n list: allowPublic(),\n get: allowPublic(),\n create: requireRoles(roles),\n update: requireRoles(roles),\n delete: requireRoles(roles),\n },\n overrides,\n );\n}\n\n/**\n * All operations require authentication.\n */\nexport function authenticated<TDoc = any>(\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n return withOverrides(\n {\n list: requireAuth(),\n get: requireAuth(),\n create: requireAuth(),\n update: requireAuth(),\n delete: requireAuth(),\n },\n overrides,\n );\n}\n\n/**\n * All operations require specific roles.\n * @param roles - Required roles (user needs at least one). Default: ['admin']\n */\nexport function adminOnly<TDoc = any>(\n roles: readonly string[] = [\"admin\"],\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n return withOverrides(\n {\n list: requireRoles(roles),\n get: requireRoles(roles),\n create: requireRoles(roles),\n update: requireRoles(roles),\n delete: requireRoles(roles),\n },\n overrides,\n );\n}\n\n/**\n * Owner-scoped with admin bypass.\n * list = auth (scoped to owner), get = auth, create = auth,\n * update + delete = ownership check with admin bypass.\n *\n * @param ownerField - Field containing owner ID (default: 'userId')\n * @param bypassRoles - Roles that bypass ownership check (default: ['admin'])\n */\nexport function ownerWithAdminBypass<TDoc = any>(\n ownerField: Extract<keyof TDoc, string> | string = \"userId\",\n bypassRoles: readonly string[] = [\"admin\"],\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n return withOverrides(\n {\n list: requireAuth(),\n get: requireAuth(),\n create: requireAuth(),\n update: anyOf(requireRoles(bypassRoles), requireOwnership(ownerField)),\n delete: anyOf(requireRoles(bypassRoles), requireOwnership(ownerField)),\n },\n overrides,\n );\n}\n\n/**\n * Full public access — no auth required for any operation.\n * Use sparingly (dev/testing, truly public APIs).\n */\nexport function fullPublic<TDoc = any>(\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n return withOverrides(\n {\n list: allowPublic(),\n get: allowPublic(),\n create: allowPublic(),\n update: allowPublic(),\n delete: allowPublic(),\n },\n overrides,\n );\n}\n\n/**\n * Read-only: list + get authenticated, write operations denied.\n * Useful for computed/derived resources.\n */\nexport function readOnly<TDoc = any>(\n overrides?: PermissionOverrides<TDoc>,\n): ResourcePermissions<TDoc> {\n return withOverrides(\n {\n list: requireAuth(),\n get: requireAuth(),\n },\n overrides,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgDA,SAAS,cACP,MACA,WAC2B;AAC3B,KAAI,CAAC,UAAW,QAAO;CACvB,MAAM,WAAW,OAAO,YACtB,OAAO,QAAQ,UAAU,CAAC,QAAQ,GAAG,OAAO,MAAM,OAAU,CAC7D;AACD,QAAO;EAAE,GAAG;EAAM,GAAG;EAAU;;;;;;AAOjC,SAAgB,WACd,WAC2B;AAC3B,QAAO,cACL;EACE,MAAM,aAAa;EACnB,KAAK,aAAa;EAClB,QAAQ,aAAa;EACrB,QAAQ,aAAa;EACrB,QAAQ,aAAa;EACtB,EACD,UACD;;;;;;AAOH,SAAgB,qBACd,QAA2B,CAAC,QAAQ,EACpC,WAC2B;AAC3B,QAAO,cACL;EACE,MAAM,aAAa;EACnB,KAAK,aAAa;EAClB,QAAQ,aAAa,MAAM;EAC3B,QAAQ,aAAa,MAAM;EAC3B,QAAQ,aAAa,MAAM;EAC5B,EACD,UACD;;;;;AAMH,SAAgB,cACd,WAC2B;AAC3B,QAAO,cACL;EACE,MAAM,aAAa;EACnB,KAAK,aAAa;EAClB,QAAQ,aAAa;EACrB,QAAQ,aAAa;EACrB,QAAQ,aAAa;EACtB,EACD,UACD;;;;;;AAOH,SAAgB,UACd,QAA2B,CAAC,QAAQ,EACpC,WAC2B;AAC3B,QAAO,cACL;EACE,MAAM,aAAa,MAAM;EACzB,KAAK,aAAa,MAAM;EACxB,QAAQ,aAAa,MAAM;EAC3B,QAAQ,aAAa,MAAM;EAC3B,QAAQ,aAAa,MAAM;EAC5B,EACD,UACD;;;;;;;;;;AAWH,SAAgB,qBACd,aAAmD,UACnD,cAAiC,CAAC,QAAQ,EAC1C,WAC2B;AAC3B,QAAO,cACL;EACE,MAAM,aAAa;EACnB,KAAK,aAAa;EAClB,QAAQ,aAAa;EACrB,QAAQ,MAAM,aAAa,YAAY,EAAE,iBAAiB,WAAW,CAAC;EACtE,QAAQ,MAAM,aAAa,YAAY,EAAE,iBAAiB,WAAW,CAAC;EACvE,EACD,UACD;;;;;;AAOH,SAAgB,WACd,WAC2B;AAC3B,QAAO,cACL;EACE,MAAM,aAAa;EACnB,KAAK,aAAa;EAClB,QAAQ,aAAa;EACrB,QAAQ,aAAa;EACrB,QAAQ,aAAa;EACtB,EACD,UACD;;;;;;AAOH,SAAgB,SACd,WAC2B;AAC3B,QAAO,cACL;EACE,MAAM,aAAa;EACnB,KAAK,aAAa;EACnB,EACD,UACD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { t as PermissionCheck } from "./types-aYB4V7uN.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/permissions/presets.d.ts
|
|
4
|
+
declare namespace presets_d_exports {
|
|
5
|
+
export { adminOnly, authenticated, fullPublic, ownerWithAdminBypass, publicRead, publicReadAdminWrite, readOnly };
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* ResourcePermissions shape — matches the type in types/index.ts
|
|
9
|
+
*/
|
|
10
|
+
interface ResourcePermissions<TDoc = any> {
|
|
11
|
+
list?: PermissionCheck<TDoc>;
|
|
12
|
+
get?: PermissionCheck<TDoc>;
|
|
13
|
+
create?: PermissionCheck<TDoc>;
|
|
14
|
+
update?: PermissionCheck<TDoc>;
|
|
15
|
+
delete?: PermissionCheck<TDoc>;
|
|
16
|
+
}
|
|
17
|
+
type PermissionOverrides<TDoc = any> = Partial<ResourcePermissions<TDoc>>;
|
|
18
|
+
/**
|
|
19
|
+
* Public read, authenticated write.
|
|
20
|
+
* list + get = allowPublic(), create + update + delete = requireAuth()
|
|
21
|
+
*/
|
|
22
|
+
declare function publicRead<TDoc = any>(overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
|
|
23
|
+
/**
|
|
24
|
+
* Public read, admin write.
|
|
25
|
+
* list + get = allowPublic(), create + update + delete = requireRoles(['admin'])
|
|
26
|
+
*/
|
|
27
|
+
declare function publicReadAdminWrite<TDoc = any>(roles?: readonly string[], overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
|
|
28
|
+
/**
|
|
29
|
+
* All operations require authentication.
|
|
30
|
+
*/
|
|
31
|
+
declare function authenticated<TDoc = any>(overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
|
|
32
|
+
/**
|
|
33
|
+
* All operations require specific roles.
|
|
34
|
+
* @param roles - Required roles (user needs at least one). Default: ['admin']
|
|
35
|
+
*/
|
|
36
|
+
declare function adminOnly<TDoc = any>(roles?: readonly string[], overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
|
|
37
|
+
/**
|
|
38
|
+
* Owner-scoped with admin bypass.
|
|
39
|
+
* list = auth (scoped to owner), get = auth, create = auth,
|
|
40
|
+
* update + delete = ownership check with admin bypass.
|
|
41
|
+
*
|
|
42
|
+
* @param ownerField - Field containing owner ID (default: 'userId')
|
|
43
|
+
* @param bypassRoles - Roles that bypass ownership check (default: ['admin'])
|
|
44
|
+
*/
|
|
45
|
+
declare function ownerWithAdminBypass<TDoc = any>(ownerField?: Extract<keyof TDoc, string> | string, bypassRoles?: readonly string[], overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
|
|
46
|
+
/**
|
|
47
|
+
* Full public access — no auth required for any operation.
|
|
48
|
+
* Use sparingly (dev/testing, truly public APIs).
|
|
49
|
+
*/
|
|
50
|
+
declare function fullPublic<TDoc = any>(overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
|
|
51
|
+
/**
|
|
52
|
+
* Read-only: list + get authenticated, write operations denied.
|
|
53
|
+
* Useful for computed/derived resources.
|
|
54
|
+
*/
|
|
55
|
+
declare function readOnly<TDoc = any>(overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { presets_d_exports as a, readOnly as c, ownerWithAdminBypass as i, authenticated as n, publicRead as o, fullPublic as r, publicReadAdminWrite as s, adminOnly as t };
|
|
58
|
+
//# sourceMappingURL=presets-DzSMwlKj.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets-DzSMwlKj.d.mts","names":[],"sources":["../src/permissions/presets.ts"],"mappings":";;;;;;;;;UAkCU,mBAAA;EACR,IAAA,GAAO,eAAA,CAAgB,IAAA;EACvB,GAAA,GAAM,eAAA,CAAgB,IAAA;EACtB,MAAA,GAAS,eAAA,CAAgB,IAAA;EACzB,MAAA,GAAS,eAAA,CAAgB,IAAA;EACzB,MAAA,GAAS,eAAA,CAAgB,IAAA;AAAA;AAAA,KAGtB,mBAAA,eAAkC,OAAA,CAAQ,mBAAA,CAAoB,IAAA;;;;;iBAqBnD,UAAA,YAAA,CACd,SAAA,GAAY,mBAAA,CAAoB,IAAA,IAC/B,mBAAA,CAAoB,IAAA;;;;;iBAiBP,oBAAA,YAAA,CACd,KAAA,sBACA,SAAA,GAAY,mBAAA,CAAoB,IAAA,IAC/B,mBAAA,CAAoB,IAAA;;;;iBAgBP,aAAA,YAAA,CACd,SAAA,GAAY,mBAAA,CAAoB,IAAA,IAC/B,mBAAA,CAAoB,IAAA;;;;;iBAiBP,SAAA,YAAA,CACd,KAAA,sBACA,SAAA,GAAY,mBAAA,CAAoB,IAAA,IAC/B,mBAAA,CAAoB,IAAA;;;;;;;;;iBAqBP,oBAAA,YAAA,CACd,UAAA,GAAY,OAAA,OAAc,IAAA,oBAC1B,WAAA,sBACA,SAAA,GAAY,mBAAA,CAAoB,IAAA,IAC/B,mBAAA,CAAoB,IAAA;;;;;iBAiBP,UAAA,YAAA,CACd,SAAA,GAAY,mBAAA,CAAoB,IAAA,IAC/B,mBAAA,CAAoB,IAAA;;;;;iBAiBP,QAAA,YAAA,CACd,SAAA,GAAY,mBAAA,CAAoB,IAAA,IAC/B,mBAAA,CAAoB,IAAA"}
|