@classytic/arc 2.11.3 → 2.13.1
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 +27 -18
- package/dist/{BaseController-swXruJ2_.mjs → BaseController-DX_T-bDB.mjs} +388 -423
- package/dist/EventTransport-CT_52aWU.d.mts +34 -0
- package/dist/EventTransport-DLWoUMHy.mjs +103 -0
- package/dist/{QueryCache-DOBNHBE0.d.mts → QueryCache-D41bfdBB.d.mts} +1 -1
- package/dist/{ResourceRegistry-DkAeAuTX.mjs → ResourceRegistry-CTERg_2x.mjs} +139 -66
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +1 -1
- package/dist/auth/audit.d.mts +199 -0
- package/dist/auth/audit.mjs +288 -0
- package/dist/auth/index.d.mts +5 -5
- package/dist/auth/index.mjs +117 -191
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-DwxtK3uG.mjs → betterAuthOpenApi--M_i87dQ.mjs} +1 -1
- package/dist/buildHandler-olo-gt94.mjs +610 -0
- package/dist/cache/index.d.mts +3 -3
- package/dist/cache/index.mjs +3 -3
- package/dist/cli/commands/describe.d.mts +89 -13
- package/dist/cli/commands/describe.mjs +56 -2
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +147 -48
- package/dist/cli/commands/init.d.mts +13 -0
- package/dist/cli/commands/init.mjs +237 -112
- package/dist/cli/commands/introspect.mjs +8 -1
- package/dist/context/index.mjs +1 -1
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -5
- package/dist/core-D72ia0EH.mjs +1399 -0
- package/dist/{createActionRouter-u3ql2EDo.mjs → createActionRouter-CEvzKcy8.mjs} +7 -20
- package/dist/createAggregationRouter-CyecOxnO.mjs +114 -0
- package/dist/{createApp-BFxtdKy6.mjs → createApp-XX2-N0Yd.mjs} +31 -27
- package/dist/defineEvent-D5h7EvAx.mjs +188 -0
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DOFoxoDs.mjs → elevation-DgoeTyfX.mjs} +1 -1
- package/dist/errorHandler-Bk-AGhkU.mjs +174 -0
- package/dist/errorHandler-DFr45ZG4.d.mts +45 -0
- package/dist/errors-j4aJm1Wg.mjs +184 -0
- package/dist/{eventPlugin-KrFIQ097.mjs → eventPlugin-CaKTYkYM.mjs} +35 -137
- package/dist/{eventPlugin-CUNjYYRY.d.mts → eventPlugin-qXpqTebY.d.mts} +57 -7
- package/dist/events/index.d.mts +164 -5
- package/dist/events/index.mjs +133 -209
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +204 -31
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +2 -2
- package/dist/factory/index.mjs +2 -2
- package/dist/{fields-C8Y0XLAu.d.mts → fields-COhcH3fk.d.mts} +23 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/index.mjs +1 -20
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +1 -1
- package/dist/{index-BYCqHCVu.d.mts → index-BTqLEvhu.d.mts} +164 -4
- package/dist/{index-6u4_Gg6G.d.mts → index-BtW7qYwa.d.mts} +661 -281
- package/dist/{index-BdXnTPRj.d.mts → index-Ds61mrJE.d.mts} +50 -4
- package/dist/{index-DdQ3O9Pg.d.mts → index-Dz5IKsrE.d.mts} +360 -219
- package/dist/index.d.mts +6 -7
- package/dist/index.mjs +9 -10
- package/dist/integrations/event-gateway.d.mts +2 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/mcp/index.d.mts +2 -2
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/streamline.d.mts +60 -11
- package/dist/integrations/streamline.mjs +75 -85
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +2 -8
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +2 -2
- package/dist/migrations/index.d.mts +23 -3
- package/dist/migrations/index.mjs +0 -7
- package/dist/{multipartBody-CvTR1Un6.mjs → multipartBody-BOvVSVCD.mjs} +11 -8
- package/dist/{openapi-BGUn7Ki1.mjs → openapi-CiOMVW1p.mjs} +143 -13
- package/dist/org/index.d.mts +2 -2
- package/dist/org/index.mjs +1 -1
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-gd_aUWrR.mjs → permissions-ohQyv50e.mjs} +404 -176
- package/dist/{pipe-DVoIheVC.mjs → pipe-Zr0KXjQe.mjs} +1 -1
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/pipeline/index.mjs +1 -1
- package/dist/plugins/index.d.mts +18 -33
- package/dist/plugins/index.mjs +33 -13
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +5 -5
- package/dist/presets/filesUpload.mjs +6 -9
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +2 -2
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +6 -8
- package/dist/{presets-Z7P5w4gF.mjs → presets-BbkjdPeH.mjs} +6 -28
- package/dist/{queryCachePlugin-BUXBSm4F.d.mts → queryCachePlugin-CqMdLI2-.d.mts} +2 -2
- package/dist/{queryCachePlugin-Bq6bO6vc.mjs → queryCachePlugin-m1XsgAIJ.mjs} +3 -3
- package/dist/{redis-Cm1gnRDf.d.mts → redis-DiMkdHEl.d.mts} +1 -1
- package/dist/redis-stream-D6HzR1Z_.d.mts +232 -0
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{replyHelpers-ByllIXXV.mjs → replyHelpers-CK-FNO8E.mjs} +3 -21
- package/dist/{resourceToTools-ByZpgjeH.mjs → resourceToTools-C5coh64w.mjs} +224 -71
- package/dist/{routerShared-BqLRb5l7.mjs → routerShared-D6_fEGHh.mjs} +40 -36
- package/dist/{schemaIR-BlG9bY7v.mjs → schemaIR-7Vl611Qs.mjs} +1 -1
- package/dist/schemas/index.d.mts +100 -30
- package/dist/schemas/index.mjs +86 -29
- package/dist/scim/index.d.mts +264 -0
- package/dist/scim/index.mjs +963 -0
- package/dist/scope/index.d.mts +3 -3
- package/dist/scope/index.mjs +4 -4
- package/dist/{sse-V7aXc3bW.mjs → sse-Bz-5ZeTt.mjs} +1 -1
- package/dist/{store-helpers-BhrzxvyQ.mjs → store-helpers-BkIN9-vu.mjs} +1 -1
- package/dist/testing/index.d.mts +2 -8
- package/dist/testing/index.mjs +16 -24
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-BH7dEGvU.d.mts → types-BvqwCCSx.d.mts} +77 -29
- package/dist/{types-tgR4Pt8F.d.mts → types-CTYvcwHe.d.mts} +195 -1
- package/dist/{types-AOD8fxIw.mjs → types-C_s5moIu.mjs} +117 -1
- package/dist/{types-9beEMe25.d.mts → types-DQHFc8PM.d.mts} +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +5 -5
- package/dist/{utils-CcYTj09l.mjs → utils-_h9B3c57.mjs} +1269 -1334
- package/dist/{versioning-M9lNLhO8.d.mts → versioning-DTTvc80y.d.mts} +1 -1
- package/package.json +24 -34
- package/skills/arc/SKILL.md +521 -785
- package/skills/arc/references/agent-auth.md +238 -0
- package/skills/arc/references/api-reference.md +187 -0
- package/skills/arc/references/auth.md +354 -7
- package/skills/arc/references/enterprise-auth.md +94 -0
- package/skills/arc/references/events.md +8 -6
- package/skills/arc/references/mcp.md +2 -2
- package/skills/arc/references/multi-tenancy.md +11 -2
- package/skills/arc/references/production.md +10 -9
- package/skills/arc/references/scim.md +247 -0
- package/skills/arc/references/testing.md +1 -1
- package/skills/arc-code-review/SKILL.md +141 -0
- package/skills/arc-code-review/references/anti-patterns.md +911 -0
- package/skills/arc-code-review/references/arc-cheatsheet.md +380 -0
- package/skills/arc-code-review/references/migration-recipes.md +700 -0
- package/skills/arc-code-review/references/mongokit-migration.md +386 -0
- package/skills/arc-code-review/references/scaffolding.md +230 -0
- package/skills/arc-code-review/references/severity.md +127 -0
- package/dist/EventTransport-CfVEGaEl.d.mts +0 -293
- package/dist/adapters/index.d.mts +0 -3
- package/dist/adapters/index.mjs +0 -2
- package/dist/adapters-D0tT2Tyo.mjs +0 -949
- package/dist/auth/mongoose.d.mts +0 -191
- package/dist/auth/mongoose.mjs +0 -73
- package/dist/core-DnUsRpuX.mjs +0 -1049
- package/dist/errorHandler-BQm8ZxTK.mjs +0 -173
- package/dist/errorHandler-Co3lnVmJ.d.mts +0 -114
- package/dist/errors-D5c-5BJL.mjs +0 -232
- package/dist/index-BbMrcvGp.d.mts +0 -362
- package/dist/redis-stream-CM8TXTix.d.mts +0 -110
- /package/dist/{HookSystem-CGsMd6oK.mjs → HookSystem-Iiebom92.mjs} +0 -0
- /package/dist/{actionPermissions-sUUKDhtP.mjs → actionPermissions-CyUkQu6O.mjs} +0 -0
- /package/dist/{caching-CheW3m-S.mjs → caching-SM8gghN6.mjs} +0 -0
- /package/dist/{constants-BhY1OHoH.mjs → constants-Cxde4rpC.mjs} +0 -0
- /package/dist/{elevation-s5ykdNHr.d.mts → elevation-BXOWoGCF.d.mts} +0 -0
- /package/dist/{externalPaths-Bapitwvd.d.mts → externalPaths-BD5nw6St.d.mts} +0 -0
- /package/dist/{interface-CkkWm5uR.d.mts → interface-DfLGcus7.d.mts} +0 -0
- /package/dist/{interface-Da0r7Lna.d.mts → interface-beEtJyWM.d.mts} +0 -0
- /package/dist/{keys-CARyUjiR.mjs → keys-CGcCbNyu.mjs} +0 -0
- /package/dist/{loadResources-CPpkyKfM.mjs → loadResources-DBMQg_Aj.mjs} +0 -0
- /package/dist/{memory-DikHSvWa.mjs → memory-UBydS5ku.mjs} +0 -0
- /package/dist/{metrics-Csh4nsvv.mjs → metrics-Qnvwc-LQ.mjs} +0 -0
- /package/dist/{pluralize-BneOJkpi.mjs → pluralize-DQgqgifU.mjs} +0 -0
- /package/dist/{registry-D63ee7fl.mjs → registry-I-ogLgL9.mjs} +0 -0
- /package/dist/{requestContext-C5XeK3VA.mjs → requestContext-SSaaTgW8.mjs} +0 -0
- /package/dist/{schemaConverter-B0oKLuqI.mjs → schemaConverter-De34B1ZG.mjs} +0 -0
- /package/dist/{sessionManager-D-oNWHz3.d.mts → sessionManager-C4Le_UB3.d.mts} +0 -0
- /package/dist/{storage-BwGQXUpd.d.mts → storage-Dfzt4VTl.d.mts} +0 -0
- /package/dist/{tracing-DokiEsuz.d.mts → tracing-QJVprktp.d.mts} +0 -0
- /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-BzkXkvVv.mjs} +0 -0
- /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{versioning-CGPjkqAg.mjs → versioning-BUrT5aP4.mjs} +0 -0
- /package/dist/{websocket-CyJ1VIFI.d.mts → websocket-ChC2rqe1.d.mts} +0 -0
package/dist/scope/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { C as isOrgInScope, D as requireTeamId, E as requireOrgId, O as requireUserId, S as isMember, T as requireClientId, _ as getUserId, a as getAncestorOrgIds, b as isAuthenticated, c as getMandate, d as getOrgRoles, f as getRequestScope, g as getTeamId, h as getServiceScopes, i as RequestScope, l as getOrgContext, m as getScopeContextMap, n as Mandate, o as getClientId, p as getScopeContext, r as PUBLIC_SCOPE, s as getDPoPJkt, t as AUTHENTICATED_SCOPE, u as getOrgId, v as getUserRoles, w as isService, x as isElevated, y as hasOrgAccess } from "../types-CTYvcwHe.mjs";
|
|
2
|
+
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-BXOWoGCF.mjs";
|
|
3
3
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/scope/rateLimitKey.d.ts
|
|
@@ -30,4 +30,4 @@ interface ResolveOrgFromHeaderOptions {
|
|
|
30
30
|
*/
|
|
31
31
|
declare function resolveOrgFromHeader(options: ResolveOrgFromHeaderOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
32
32
|
//#endregion
|
|
33
|
-
export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, resolveOrgFromHeader };
|
|
33
|
+
export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, type Mandate, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
|
package/dist/scope/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import { n as normalizeRoles } from "../types-
|
|
3
|
-
import { n as elevation_default, t as elevationPlugin } from "../elevation-
|
|
1
|
+
import { C as requireClientId, E as requireUserId, S as isService, T as requireTeamId, _ as hasOrgAccess, a as getDPoPJkt, b as isMember, c as getOrgId, d as getScopeContext, f as getScopeContextMap, g as getUserRoles, h as getUserId, i as getClientId, l as getOrgRoles, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, r as getAncestorOrgIds, s as getOrgContext, t as AUTHENTICATED_SCOPE, u as getRequestScope, v as isAuthenticated, w as requireOrgId, x as isOrgInScope, y as isElevated } from "../types-C_s5moIu.mjs";
|
|
2
|
+
import { n as normalizeRoles } from "../types-D57iXYb8.mjs";
|
|
3
|
+
import { n as elevation_default, t as elevationPlugin } from "../elevation-DgoeTyfX.mjs";
|
|
4
4
|
//#region src/scope/rateLimitKey.ts
|
|
5
5
|
function createTenantKeyGenerator(opts) {
|
|
6
6
|
if (opts?.strategy) return opts.strategy;
|
|
@@ -76,4 +76,4 @@ function resolveOrgFromHeader(options) {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
//#endregion
|
|
79
|
-
export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, resolveOrgFromHeader };
|
|
79
|
+
export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-C_s5moIu.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/plugins/sse.ts
|
|
6
6
|
var sse_exports = /* @__PURE__ */ __exportAll({
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { d as ResourceLike, r as CreateAppOptions } from "../types-
|
|
1
|
+
import { V as ResourceDefinition, Wt as AnyRecord } from "../index-BtW7qYwa.mjs";
|
|
2
|
+
import { d as ResourceLike, r as CreateAppOptions } from "../types-BvqwCCSx.mjs";
|
|
3
3
|
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
5
5
|
import { Mock } from "vitest";
|
|
@@ -349,12 +349,6 @@ declare class HttpTestHarness<T = unknown> {
|
|
|
349
349
|
private resource;
|
|
350
350
|
private optionsOrGetter;
|
|
351
351
|
private enabledRoutes;
|
|
352
|
-
/**
|
|
353
|
-
* Update verbs exercised by this harness instance. One entry for single-method
|
|
354
|
-
* resources (`"PATCH"` or `"PUT"`), two for `updateMethod: "both"` so both
|
|
355
|
-
* verbs are covered — the framework mounts both, and the harness should
|
|
356
|
-
* probe both.
|
|
357
|
-
*/
|
|
358
352
|
private updateMethods;
|
|
359
353
|
constructor(resource: ResourceDefinition<unknown>, optionsOrGetter: OptionsOrGetter<T>);
|
|
360
354
|
private getOptions;
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as CRUD_OPERATIONS } from "../constants-
|
|
1
|
+
import { t as CRUD_OPERATIONS } from "../constants-Cxde4rpC.mjs";
|
|
2
2
|
import { runStorageContract } from "./storageContract.mjs";
|
|
3
3
|
import Fastify from "fastify";
|
|
4
4
|
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
@@ -49,13 +49,13 @@ function expectArc(response) {
|
|
|
49
49
|
},
|
|
50
50
|
ok(status = 200) {
|
|
51
51
|
expect(response.statusCode, `expected 2xx (${status}) but got ${response.statusCode}. Body: ${response.body.slice(0, 200)}`).toBe(status);
|
|
52
|
-
|
|
52
|
+
getBody();
|
|
53
53
|
return assertion;
|
|
54
54
|
},
|
|
55
55
|
failed(status) {
|
|
56
56
|
if (status !== void 0) expect(response.statusCode).toBe(status);
|
|
57
57
|
else expect(response.statusCode).toBeGreaterThanOrEqual(400);
|
|
58
|
-
expect(getBody().
|
|
58
|
+
expect(getBody().code, "expected ErrorContract.code on failed response").toBeDefined();
|
|
59
59
|
return assertion;
|
|
60
60
|
},
|
|
61
61
|
unauthorized() {
|
|
@@ -74,7 +74,8 @@ function expectArc(response) {
|
|
|
74
74
|
return assertion.failed(409);
|
|
75
75
|
},
|
|
76
76
|
hasData() {
|
|
77
|
-
|
|
77
|
+
const body = getBody();
|
|
78
|
+
expect(Object.keys(body).length > 0, "expected response body to be defined").toBe(true);
|
|
78
79
|
return assertion;
|
|
79
80
|
},
|
|
80
81
|
hasStatus(status) {
|
|
@@ -82,22 +83,17 @@ function expectArc(response) {
|
|
|
82
83
|
return assertion;
|
|
83
84
|
},
|
|
84
85
|
hidesField(field) {
|
|
85
|
-
|
|
86
|
-
expect(data, "expected body.data to be defined before field check").toBeDefined();
|
|
87
|
-
expect(data, `expected field '${field}' to be hidden from response.data`).not.toHaveProperty(field);
|
|
86
|
+
expect(getBody(), `expected field '${field}' to be hidden from response`).not.toHaveProperty(field);
|
|
88
87
|
return assertion;
|
|
89
88
|
},
|
|
90
89
|
showsField(field) {
|
|
91
|
-
|
|
92
|
-
expect(data, "expected body.data to be defined before field check").toBeDefined();
|
|
93
|
-
expect(data, `expected field '${field}' on response.data`).toHaveProperty(field);
|
|
90
|
+
expect(getBody(), `expected field '${field}' on response`).toHaveProperty(field);
|
|
94
91
|
return assertion;
|
|
95
92
|
},
|
|
96
93
|
paginated(expected) {
|
|
97
94
|
const body = getBody();
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
expect(Array.isArray(docs), "expected `docs[]` (or `data[]`) on paginated response").toBe(true);
|
|
95
|
+
const data = body.data;
|
|
96
|
+
expect(Array.isArray(data), "expected `data[]` array on paginated response").toBe(true);
|
|
101
97
|
if (expected?.page !== void 0) expect(body.page).toBe(expected.page);
|
|
102
98
|
if (expected?.limit !== void 0) expect(body.limit).toBe(expected.limit);
|
|
103
99
|
if (expected?.total !== void 0) expect(body.total).toBe(expected.total);
|
|
@@ -107,8 +103,8 @@ function expectArc(response) {
|
|
|
107
103
|
},
|
|
108
104
|
hasError(matcher) {
|
|
109
105
|
const body = getBody();
|
|
110
|
-
const errorField = body.
|
|
111
|
-
expect(errorField, "expected body.
|
|
106
|
+
const errorField = body.message ?? body.error;
|
|
107
|
+
expect(errorField, "expected body.message (or body.error) to be set").toBeDefined();
|
|
112
108
|
if (typeof matcher === "string") expect(errorField).toBe(matcher);
|
|
113
109
|
else expect(errorField).toMatch(matcher);
|
|
114
110
|
return assertion;
|
|
@@ -520,12 +516,6 @@ var HttpTestHarness = class {
|
|
|
520
516
|
resource;
|
|
521
517
|
optionsOrGetter;
|
|
522
518
|
enabledRoutes;
|
|
523
|
-
/**
|
|
524
|
-
* Update verbs exercised by this harness instance. One entry for single-method
|
|
525
|
-
* resources (`"PATCH"` or `"PUT"`), two for `updateMethod: "both"` so both
|
|
526
|
-
* verbs are covered — the framework mounts both, and the harness should
|
|
527
|
-
* probe both.
|
|
528
|
-
*/
|
|
529
519
|
updateMethods;
|
|
530
520
|
constructor(resource, optionsOrGetter) {
|
|
531
521
|
this.resource = resource;
|
|
@@ -588,7 +578,7 @@ var HttpTestHarness = class {
|
|
|
588
578
|
expect(res.statusCode).toBe(200);
|
|
589
579
|
const body = JSON.parse(res.body);
|
|
590
580
|
expect(body.success).toBe(true);
|
|
591
|
-
const list = body.data ?? body.
|
|
581
|
+
const list = body.data ?? body.data;
|
|
592
582
|
expect(Array.isArray(list)).toBe(true);
|
|
593
583
|
});
|
|
594
584
|
if (enabledRoutes.has("get")) {
|
|
@@ -792,7 +782,7 @@ function createMockRepository(overrides = {}) {
|
|
|
792
782
|
return {
|
|
793
783
|
getAll: vi.fn().mockResolvedValue({
|
|
794
784
|
method: "offset",
|
|
795
|
-
|
|
785
|
+
data: [],
|
|
796
786
|
total: 0,
|
|
797
787
|
page: 1,
|
|
798
788
|
limit: 20,
|
|
@@ -822,6 +812,8 @@ function createMockRepository(overrides = {}) {
|
|
|
822
812
|
acknowledged: true,
|
|
823
813
|
deletedCount: 0
|
|
824
814
|
}),
|
|
815
|
+
claim: vi.fn().mockResolvedValue(null),
|
|
816
|
+
claimVersion: vi.fn().mockResolvedValue(null),
|
|
825
817
|
getBySlug: vi.fn().mockResolvedValue(null),
|
|
826
818
|
getDeleted: vi.fn().mockResolvedValue([]),
|
|
827
819
|
restore: vi.fn().mockResolvedValue(null),
|
|
@@ -1081,7 +1073,7 @@ function pickDefaultAuth(authMode, callerAuth) {
|
|
|
1081
1073
|
};
|
|
1082
1074
|
}
|
|
1083
1075
|
async function createTestApp(options = {}) {
|
|
1084
|
-
const { createApp } = await import("../createApp-
|
|
1076
|
+
const { createApp } = await import("../createApp-XX2-N0Yd.mjs").then((n) => n.r);
|
|
1085
1077
|
const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
|
|
1086
1078
|
let dbHandle;
|
|
1087
1079
|
let dbUri;
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as OpenApiSchemas, A as RequestIdOptions,
|
|
2
|
-
import {
|
|
3
|
-
import { c as PermissionCheck, d as UserBase, l as PermissionContext, u as PermissionResult } from "../fields-
|
|
4
|
-
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { $ as OpenApiSchemas, A as RequestIdOptions, Bt as Authenticator, C as RequestContext, D as HealthCheck, E as GracefulShutdownOptions, F as FastifyWithDecorators, G as ActionsMap, Gt as ApiResponse, H as ActionDefinition, Ht as JwtContext, I as MiddlewareHandler, J as CrudRouteKey, Jt as ObjectId, K as ArcFieldRule, Kt as ArcRequest, L as RequestWithExtras, M as EventsDecorator, N as FastifyRequestExtras, O as HealthOptions, P as FastifyWithAuth, Q as MiddlewareConfig, Rt as AuthHelpers, S as QueryParserInterface, T as CrudRouterOptions, U as ActionEntry, Ut as TokenPair, Vt as AuthenticatorContext, W as ActionHandlerFn, Wt as AnyRecord, X as EventDefinition, Xt as UserOrganization, Y as CrudSchemas, Yt as UserLike, Z as FieldRule, _ as ControllerQueryOptions, _t as IControllerResponse, a as InferAdapterDoc, at as ResourceConfig, b as ParsedQuery, c as TypedController, ct as ResourcePermissions, d as PaginationResult, dt as RouteMethod, et as PresetFunction, f as IntrospectionData, ft as RouteSchemaOptions, g as ArcInternalMetadata, gt as IController, h as ResourceMetadata, ht as FastifyHandler, i as ValidationResult, it as ResourceCacheConfig, j as ArcDecorator, k as IntrospectionPluginOptions, l as TypedRepository, lt as RouteDefinition, m as RegistryStats, mt as ControllerLike, n as ConfigError, nt as PresetResult, o as InferDocType, on as BaseControllerOptions, ot as ResourceHookContext, p as RegistryEntry, pt as ControllerHandler, q as CrudController, qt as JWTPayload, r as ValidateOptions, rt as RateLimitConfig, s as InferResourceDoc, st as ResourceHooks, t as RouteHandlerMethod, tt as PresetHook, u as TypedResourceConfig, ut as RouteMcpConfig, v as LookupOption, vt as IRequestContext, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, yt as RouteHandler, zt as AuthPluginOptions } from "../index-BtW7qYwa.mjs";
|
|
2
|
+
import { i as RequestScope } from "../types-CTYvcwHe.mjs";
|
|
3
|
+
import { c as PermissionCheck, d as UserBase, l as PermissionContext, u as PermissionResult } from "../fields-COhcH3fk.mjs";
|
|
4
|
+
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-BXOWoGCF.mjs";
|
|
5
5
|
export { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord, ApiResponse, ArcDecorator, ArcFieldRule, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResourceCacheConfig, ResourceConfig, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult };
|
package/dist/types/storage.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-
|
|
1
|
+
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-Dfzt4VTl.mjs";
|
|
2
2
|
export { Storage, StorageContext, StorageFile, StorageReadRange, StorageReadResult, StorageUploadInput };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as ElevationOptions } from "./elevation-
|
|
4
|
-
import {
|
|
5
|
-
import { t as ExternalOpenApiPaths } from "./externalPaths-
|
|
6
|
-
import { r as QueryCachePluginOptions } from "./queryCachePlugin-
|
|
7
|
-
import { t as EventPluginOptions } from "./eventPlugin-
|
|
8
|
-
import { f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-
|
|
9
|
-
import { t as ErrorHandlerOptions } from "./errorHandler-
|
|
10
|
-
import { r as IdempotencyStore } from "./interface-
|
|
1
|
+
import { r as CacheStore } from "./interface-beEtJyWM.mjs";
|
|
2
|
+
import { Bt as Authenticator } from "./index-BtW7qYwa.mjs";
|
|
3
|
+
import { n as ElevationOptions } from "./elevation-BXOWoGCF.mjs";
|
|
4
|
+
import { a as EventTransport } from "./EventTransport-CT_52aWU.mjs";
|
|
5
|
+
import { t as ExternalOpenApiPaths } from "./externalPaths-BD5nw6St.mjs";
|
|
6
|
+
import { r as QueryCachePluginOptions } from "./queryCachePlugin-CqMdLI2-.mjs";
|
|
7
|
+
import { t as EventPluginOptions } from "./eventPlugin-qXpqTebY.mjs";
|
|
8
|
+
import { f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-DTTvc80y.mjs";
|
|
9
|
+
import { t as ErrorHandlerOptions } from "./errorHandler-DFr45ZG4.mjs";
|
|
10
|
+
import { r as IdempotencyStore } from "./interface-DfLGcus7.mjs";
|
|
11
11
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
|
|
12
12
|
|
|
13
13
|
//#region src/factory/loadResources.d.ts
|
|
@@ -682,7 +682,20 @@ interface CreateAppOptions {
|
|
|
682
682
|
keywords?: string[];
|
|
683
683
|
};
|
|
684
684
|
/**
|
|
685
|
-
* Enable `reply.
|
|
685
|
+
* Enable `reply.sendList()` + `reply.stream()` response decorators.
|
|
686
|
+
*
|
|
687
|
+
* Arc emits raw data on success — HTTP status discriminates, no
|
|
688
|
+
* `{ success, data }` envelope — so single-doc handlers just
|
|
689
|
+
* `return doc` or `reply.send(doc)` and errors throw `ArcError`
|
|
690
|
+
* (the global handler serializes to `ErrorContract`). The two
|
|
691
|
+
* decorators below cover the cases that DO need framework support:
|
|
692
|
+
*
|
|
693
|
+
* - `sendList(input)` normalises any kit-shaped paginated/array
|
|
694
|
+
* result to the canonical wire shape via repo-core's
|
|
695
|
+
* `toCanonicalList` so the server and `@classytic/arc-next`
|
|
696
|
+
* typed client share one declaration.
|
|
697
|
+
* - `stream(source, options)` sets `Content-Type` /
|
|
698
|
+
* `Content-Disposition` headers for file downloads in one call.
|
|
686
699
|
*
|
|
687
700
|
* Default: `false` (opt-in).
|
|
688
701
|
*
|
|
@@ -690,32 +703,64 @@ interface CreateAppOptions {
|
|
|
690
703
|
* ```typescript
|
|
691
704
|
* const app = await createApp({ replyHelpers: true });
|
|
692
705
|
*
|
|
693
|
-
* //
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
*
|
|
706
|
+
* // List endpoint — kit-shaped pagination → canonical wire shape
|
|
707
|
+
* app.get('/orders', async (req, reply) => {
|
|
708
|
+
* const result = await orderRepo.getAll(req.query);
|
|
709
|
+
* return reply.sendList(result);
|
|
710
|
+
* });
|
|
711
|
+
*
|
|
712
|
+
* // File download
|
|
713
|
+
* app.get('/orders/export.csv', async (req, reply) => {
|
|
714
|
+
* return reply.stream(csvReadable, {
|
|
715
|
+
* contentType: 'text/csv',
|
|
716
|
+
* filename: 'orders.csv',
|
|
717
|
+
* });
|
|
718
|
+
* });
|
|
699
719
|
* ```
|
|
700
720
|
*/
|
|
701
721
|
replyHelpers?: boolean;
|
|
702
722
|
/**
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
723
|
+
* Serialize `bigint` values in JSON responses.
|
|
724
|
+
*
|
|
725
|
+
* `JSON.stringify` throws on `bigint` by default. This option installs a
|
|
726
|
+
* `preSerialization` hook that converts them on the way out:
|
|
727
|
+
*
|
|
728
|
+
* - `false` (default) — no conversion. JSON serialization throws if a
|
|
729
|
+
* `bigint` reaches the wire. Safest when no codepath produces bigints.
|
|
730
|
+
* - `'string'` — **recommended for IDs / money / counters / ledgers.**
|
|
731
|
+
* Converts every `bigint` to a decimal string. Lossless: every digit
|
|
732
|
+
* survives the wire. Clients parse with `BigInt(value)` to reconstitute.
|
|
733
|
+
* - `'number'` — converts every `bigint` to `Number(value)`.
|
|
734
|
+
* **Lossy above 2^53 - 1 (`Number.MAX_SAFE_INTEGER` = 9007199254740991).**
|
|
735
|
+
* Use ONLY when you've audited the value range — e.g. small enums,
|
|
736
|
+
* bounded counters that physically can't exceed the safe range. For
|
|
737
|
+
* anything that could be an ID, monetary amount, or unbounded counter,
|
|
738
|
+
* `Number()` corruption silently rounds digits and there is no
|
|
739
|
+
* recovery. arc emits a one-shot startup warning when you opt into
|
|
740
|
+
* this mode.
|
|
741
|
+
* - `true` — back-compat alias for `'number'`. Will be removed in a
|
|
742
|
+
* future major. Migrate to `'string'` (lossless) or explicit
|
|
743
|
+
* `'number'` (acknowledges the precision risk).
|
|
744
|
+
*
|
|
745
|
+
* Default: `false` (opt-in — most apps don't use bigint).
|
|
710
746
|
*
|
|
711
747
|
* @example
|
|
712
748
|
* ```typescript
|
|
749
|
+
* // Lossless — recommended path
|
|
750
|
+
* const app = await createApp({
|
|
751
|
+
* serializeBigInt: 'string',
|
|
752
|
+
* });
|
|
753
|
+
* // { totalSatoshis: "9007199254740999" }
|
|
754
|
+
*
|
|
755
|
+
* // Lossy — only when value range is bounded
|
|
713
756
|
* const app = await createApp({
|
|
714
|
-
* serializeBigInt:
|
|
757
|
+
* serializeBigInt: 'number',
|
|
758
|
+
* });
|
|
759
|
+
* // { totalSatoshis: 9007199254740999 } // precision lost above MAX_SAFE_INTEGER
|
|
715
760
|
* });
|
|
716
761
|
* ```
|
|
717
762
|
*/
|
|
718
|
-
serializeBigInt?: boolean;
|
|
763
|
+
serializeBigInt?: boolean | "number" | "string";
|
|
719
764
|
/**
|
|
720
765
|
* Resources to register automatically. Accepts two shapes:
|
|
721
766
|
*
|
|
@@ -733,7 +778,7 @@ interface CreateAppOptions {
|
|
|
733
778
|
* Arc's lifecycle contract:
|
|
734
779
|
* ```
|
|
735
780
|
* 1. Arc core (security, auth, events)
|
|
736
|
-
* 2. plugins() ← infra (DB, SSE,
|
|
781
|
+
* 2. plugins() ← infra (DB, SSE, data)
|
|
737
782
|
* 3. bootstrap[] ← domain init (engines, singletons)
|
|
738
783
|
* 4. resources resolution ← (factory form: call it here)
|
|
739
784
|
* 5. resources registered ← plugins mounted on Fastify
|
|
@@ -757,6 +802,9 @@ interface CreateAppOptions {
|
|
|
757
802
|
*
|
|
758
803
|
* @example Factory with async-booted engine
|
|
759
804
|
* ```ts
|
|
805
|
+
* import { createApp, defineResource } from '@classytic/arc';
|
|
806
|
+
* import { createMongooseAdapter } from '@classytic/mongokit/adapter';
|
|
807
|
+
*
|
|
760
808
|
* const app = await createApp({
|
|
761
809
|
* bootstrap: [async () => { await ensureCatalogEngine(); }],
|
|
762
810
|
* resources: async () => {
|
|
@@ -867,7 +915,7 @@ interface CreateAppOptions {
|
|
|
867
915
|
* Custom plugin registration — runs after Arc core (security, auth, events)
|
|
868
916
|
* but before `bootstrap` and `resources`.
|
|
869
917
|
*
|
|
870
|
-
* Use this for infrastructure setup: database connections, OpenAPI
|
|
918
|
+
* Use this for infrastructure setup: database connections, OpenAPI data,
|
|
871
919
|
* webhook plugins, SSE wiring, etc.
|
|
872
920
|
*/
|
|
873
921
|
plugins?: (fastify: FastifyInstance) => Promise<void>;
|
|
@@ -882,7 +930,7 @@ interface CreateAppOptions {
|
|
|
882
930
|
* Boot order:
|
|
883
931
|
* ```
|
|
884
932
|
* 1. Arc core (security, auth, events)
|
|
885
|
-
* 2. plugins() ← infra (DB, SSE,
|
|
933
|
+
* 2. plugins() ← infra (DB, SSE, data)
|
|
886
934
|
* 3. bootstrap[] ← domain init (singletons, event handlers)
|
|
887
935
|
* 4. resources[] ← auto-discovered routes
|
|
888
936
|
* ```
|
|
@@ -19,6 +19,93 @@
|
|
|
19
19
|
* const globalRoles = getUserRoles(scope);
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
+
/**
|
|
23
|
+
* A capability mandate — declarative, time-boxed, optionally cap-bounded
|
|
24
|
+
* authorization for a single high-value action by an AI agent or service.
|
|
25
|
+
*
|
|
26
|
+
* Models the **2025 agent-payments / agent-authorization conventions**:
|
|
27
|
+
* - Google + Anthropic + Stripe **AP2** (Agent Payments Protocol)
|
|
28
|
+
* - Stripe **x402 / Agentic Commerce**
|
|
29
|
+
* - **MCP authorization** (RFC 9728 + RFC 9700 + RFC 9449)
|
|
30
|
+
*
|
|
31
|
+
* Where OAuth `scopes` answer "what can this client EVER do?", a mandate
|
|
32
|
+
* answers "what is THIS REQUEST authorized to do RIGHT NOW?" — narrower in
|
|
33
|
+
* capability, time, and value.
|
|
34
|
+
*
|
|
35
|
+
* Arc does **not** parse mandate JWTs / Verifiable Credentials — your
|
|
36
|
+
* authenticate callback does (one `jose.jwtVerify()` or `did-jwt-vc.verify()`
|
|
37
|
+
* call) and populates this object. Arc's `requireMandate(capability, opts)`
|
|
38
|
+
* permission helper reads it and validates the action against the mandate's
|
|
39
|
+
* declared bounds.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // Inbound: Authorization: Mandate eyJhbGc...
|
|
44
|
+
* authenticate: async (request) => {
|
|
45
|
+
* const proof = request.headers['authorization']?.replace(/^Mandate /, '');
|
|
46
|
+
* const claims = await verifyMandate(proof); // host's verifier
|
|
47
|
+
* request.scope = {
|
|
48
|
+
* kind: 'service',
|
|
49
|
+
* clientId: claims.iss,
|
|
50
|
+
* organizationId: claims.org,
|
|
51
|
+
* scopes: claims.scope?.split(' '),
|
|
52
|
+
* mandate: {
|
|
53
|
+
* id: claims.jti,
|
|
54
|
+
* capability: claims.cap, // 'payment.charge' / 'inbox.send' / ...
|
|
55
|
+
* cap: claims.amount, // numeric ceiling
|
|
56
|
+
* expiresAt: claims.exp * 1000,
|
|
57
|
+
* parent: claims.parent, // delegated mandate chain
|
|
58
|
+
* audience: claims.aud, // resource id mandate is bound to
|
|
59
|
+
* },
|
|
60
|
+
* dpopJkt: claims.cnf?.jkt, // RFC 7638 — sender-constrained
|
|
61
|
+
* };
|
|
62
|
+
* return { id: claims.iss };
|
|
63
|
+
* };
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
interface Mandate {
|
|
67
|
+
/** Mandate identifier — typically the JWT `jti` claim. */
|
|
68
|
+
id: string;
|
|
69
|
+
/**
|
|
70
|
+
* Capability string the mandate authorizes. Hierarchical-dotted convention:
|
|
71
|
+
* `payment.charge`, `payment.refund`, `inbox.send`, `data.export`, etc.
|
|
72
|
+
* Match in `requireMandate(capability)`; treated as opaque otherwise.
|
|
73
|
+
*/
|
|
74
|
+
capability: string;
|
|
75
|
+
/**
|
|
76
|
+
* Optional numeric ceiling for the action — interpretation is per-capability:
|
|
77
|
+
* for `payment.*` the upper bound on amount (in the mandate's currency),
|
|
78
|
+
* for `data.export` the row count, for `inbox.send` the message count.
|
|
79
|
+
* Validated by the host via `validateAmount` in `requireMandate`'s opts.
|
|
80
|
+
*/
|
|
81
|
+
cap?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Currency code (ISO 4217) when `cap` represents a monetary amount.
|
|
84
|
+
* Required for AP2 / x402 payment mandates; ignored for non-monetary caps.
|
|
85
|
+
*/
|
|
86
|
+
currency?: string;
|
|
87
|
+
/** Expiry as epoch ms. `requireMandate` enforces with optional grace window. */
|
|
88
|
+
expiresAt?: number;
|
|
89
|
+
/**
|
|
90
|
+
* Parent mandate id when this mandate was delegated from another. Lets the
|
|
91
|
+
* audit chain reconstruct the full delegation graph (root user authorization
|
|
92
|
+
* → agent platform mandate → per-action sub-mandate).
|
|
93
|
+
*/
|
|
94
|
+
parent?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Resource the mandate is bound to (e.g., `invoice:INV-7`, `order:ORD-42`).
|
|
97
|
+
* When set, `requireMandate` can verify the inbound request targets the
|
|
98
|
+
* same resource — prevents a payment-mandate for one invoice being replayed
|
|
99
|
+
* against another.
|
|
100
|
+
*/
|
|
101
|
+
audience?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Free-form claims the host's mandate verifier extracted (intent text,
|
|
104
|
+
* user-presented confirmation, risk score, etc.). Surfaced in audit rows;
|
|
105
|
+
* arc takes no position on the shape.
|
|
106
|
+
*/
|
|
107
|
+
meta?: Readonly<Record<string, unknown>>;
|
|
108
|
+
}
|
|
22
109
|
/**
|
|
23
110
|
* Request scope — 5 kinds, no ambiguity.
|
|
24
111
|
*
|
|
@@ -84,6 +171,38 @@ type RequestScope = {
|
|
|
84
171
|
scopes?: readonly string[]; /** App-defined scope dimensions — see `member.context` for details. */
|
|
85
172
|
context?: Readonly<Record<string, string>>; /** Parent organizations — see `member.ancestorOrgIds` for details. */
|
|
86
173
|
ancestorOrgIds?: readonly string[];
|
|
174
|
+
/**
|
|
175
|
+
* Capability mandate — narrows what the caller may do beyond OAuth `scopes`.
|
|
176
|
+
*
|
|
177
|
+
* Where `scopes` says "this client can write orders", a mandate says
|
|
178
|
+
* "this specific request can charge up to $50 on behalf of user U for
|
|
179
|
+
* invoice INV-7, expires at T". Models the AP2 / Stripe x402 / Google
|
|
180
|
+
* Agent Payments Protocol pattern for AI-agent-led actions on protected
|
|
181
|
+
* resources.
|
|
182
|
+
*
|
|
183
|
+
* Arc does **not** parse or verify mandate JWTs / VCs — your authenticate
|
|
184
|
+
* function does (using `jose` / `did-jwt-vc` / your provider's SDK) and
|
|
185
|
+
* populates this field. Arc's `requireMandate(capability, opts)`
|
|
186
|
+
* permission helper reads it.
|
|
187
|
+
*
|
|
188
|
+
* Optional — omit when the request isn't mandate-bound (most M2M auth
|
|
189
|
+
* still works fine via OAuth `scopes`).
|
|
190
|
+
*/
|
|
191
|
+
mandate?: Readonly<Mandate>;
|
|
192
|
+
/**
|
|
193
|
+
* DPoP key thumbprint (RFC 7638 JWK SHA-256 thumbprint) bound to this
|
|
194
|
+
* service identity. When present, the inbound token is sender-constrained
|
|
195
|
+
* — replaying it from a different client (different key) MUST fail.
|
|
196
|
+
*
|
|
197
|
+
* Arc does **not** verify the DPoP proof header — your authenticate
|
|
198
|
+
* function does (one `jose.dpop.verify()` call) and populates this
|
|
199
|
+
* field on success. Arc's `requireDPoP()` permission helper checks
|
|
200
|
+
* presence + binding.
|
|
201
|
+
*
|
|
202
|
+
* Pairs with RFC 9449 (OAuth DPoP) and is mandatory for high-value
|
|
203
|
+
* agent flows in MCP authorization (RFC 9728 + RFC 9700) and AP2.
|
|
204
|
+
*/
|
|
205
|
+
dpopJkt?: string;
|
|
87
206
|
} | {
|
|
88
207
|
kind: "elevated";
|
|
89
208
|
userId?: string;
|
|
@@ -131,6 +250,37 @@ declare function getClientId(scope: RequestScope): string | undefined;
|
|
|
131
250
|
* Returns an empty array for any non-service kind.
|
|
132
251
|
*/
|
|
133
252
|
declare function getServiceScopes(scope: RequestScope): readonly string[];
|
|
253
|
+
/**
|
|
254
|
+
* Get the capability mandate from a service scope (when present).
|
|
255
|
+
*
|
|
256
|
+
* Returns the `Mandate` populated by your authenticate function on
|
|
257
|
+
* mandate-bound requests (AP2 / x402 / MCP authorization), or `undefined`
|
|
258
|
+
* for any non-service scope or non-mandate-bound service request.
|
|
259
|
+
*
|
|
260
|
+
* Use directly when you need the full mandate for custom logic; use
|
|
261
|
+
* `requireMandate(capability)` for the canonical permission-check path.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* import { getMandate } from '@classytic/arc/scope';
|
|
266
|
+
*
|
|
267
|
+
* const mandate = getMandate(request.scope);
|
|
268
|
+
* if (mandate?.audience !== `invoice:${request.params.id}`) {
|
|
269
|
+
* throw new ForbiddenError('Mandate is bound to a different resource');
|
|
270
|
+
* }
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
declare function getMandate(scope: RequestScope): Readonly<Mandate> | undefined;
|
|
274
|
+
/**
|
|
275
|
+
* Get the DPoP key thumbprint (RFC 7638) from a service scope.
|
|
276
|
+
*
|
|
277
|
+
* When non-empty, the inbound credential is sender-constrained — replaying
|
|
278
|
+
* it from a different keypair must fail. Set by your authenticate function
|
|
279
|
+
* after a successful `jose.dpop.verify()` (or equivalent) call. Read via
|
|
280
|
+
* `requireDPoP()` permission helper or directly when implementing custom
|
|
281
|
+
* binding checks.
|
|
282
|
+
*/
|
|
283
|
+
declare function getDPoPJkt(scope: RequestScope): string | undefined;
|
|
134
284
|
/** Get org roles from scope (empty array if not a member) */
|
|
135
285
|
declare function getOrgRoles(scope: RequestScope): string[];
|
|
136
286
|
/** Get team ID from scope (only available on member kind) */
|
|
@@ -214,6 +364,50 @@ declare function isOrgInScope(scope: RequestScope, targetOrgId: string): boolean
|
|
|
214
364
|
* ```
|
|
215
365
|
*/
|
|
216
366
|
declare function getUserId(scope: RequestScope): string | undefined;
|
|
367
|
+
/**
|
|
368
|
+
* Throwing variant of `getOrgId`. Returns the organization id when the
|
|
369
|
+
* scope carries one (`member` / `service` / `elevated`); throws
|
|
370
|
+
* `OrgRequiredError` (HTTP 403, code `ORG_SELECTION_REQUIRED`) otherwise.
|
|
371
|
+
*
|
|
372
|
+
* Use whenever a handler/service path REQUIRES an org id — `requireOrgId`
|
|
373
|
+
* eliminates the `if (!orgId) throw …` boilerplate that drifts across
|
|
374
|
+
* handlers, and ensures every "missing org" produces the same error shape.
|
|
375
|
+
*
|
|
376
|
+
* @param hint Optional context for the error message (e.g. route name) —
|
|
377
|
+
* surfaces in the response body and in logs, so the failing path is
|
|
378
|
+
* visible without grepping for the throw site.
|
|
379
|
+
* @example
|
|
380
|
+
* ```typescript
|
|
381
|
+
* import { requireOrgId } from '@classytic/arc/scope';
|
|
382
|
+
*
|
|
383
|
+
* const orgId = requireOrgId(req.scope, 'POST /orders');
|
|
384
|
+
* await orderRepo.create({ ...req.body, organizationId: orgId });
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
declare function requireOrgId(scope: RequestScope, hint?: string): string;
|
|
388
|
+
/**
|
|
389
|
+
* Throwing variant of `getUserId`. Returns the user id when the scope is
|
|
390
|
+
* `authenticated` / `member` / `elevated` and carries one; throws
|
|
391
|
+
* `UnauthorizedError` (HTTP 401) otherwise.
|
|
392
|
+
*
|
|
393
|
+
* @param hint Optional route/context tag for the error message.
|
|
394
|
+
*/
|
|
395
|
+
declare function requireUserId(scope: RequestScope, hint?: string): string;
|
|
396
|
+
/**
|
|
397
|
+
* Throwing variant of `getClientId`. Returns the service-account `clientId`
|
|
398
|
+
* when the scope kind is `service`; throws `UnauthorizedError` (HTTP 401)
|
|
399
|
+
* otherwise. Use for routes that are service-account-only.
|
|
400
|
+
*/
|
|
401
|
+
declare function requireClientId(scope: RequestScope, hint?: string): string;
|
|
402
|
+
/**
|
|
403
|
+
* Throwing variant of `getTeamId`. Returns the team id when the scope is
|
|
404
|
+
* `member` and carries one; throws `OrgRequiredError` (HTTP 403) otherwise.
|
|
405
|
+
*
|
|
406
|
+
* Uses the org-context error class (not a separate `TeamRequiredError`)
|
|
407
|
+
* because the failure mode is the same shape as missing-org: the request
|
|
408
|
+
* lacks a tenancy dimension the route requires.
|
|
409
|
+
*/
|
|
410
|
+
declare function requireTeamId(scope: RequestScope, hint?: string): string;
|
|
217
411
|
/**
|
|
218
412
|
* Get global user roles from scope (available on authenticated and member).
|
|
219
413
|
* These are user-level roles (e.g. superadmin, finance-admin) distinct from
|
|
@@ -283,4 +477,4 @@ declare const PUBLIC_SCOPE: Readonly<RequestScope>;
|
|
|
283
477
|
/** Default authenticated scope — used when user is logged in but no org */
|
|
284
478
|
declare const AUTHENTICATED_SCOPE: Readonly<RequestScope>;
|
|
285
479
|
//#endregion
|
|
286
|
-
export {
|
|
480
|
+
export { isOrgInScope as C, requireTeamId as D, requireOrgId as E, requireUserId as O, isMember as S, requireClientId as T, getUserId as _, getAncestorOrgIds as a, isAuthenticated as b, getMandate as c, getOrgRoles as d, getRequestScope as f, getTeamId as g, getServiceScopes as h, RequestScope as i, getOrgContext as l, getScopeContextMap as m, Mandate as n, getClientId as o, getScopeContext as p, PUBLIC_SCOPE as r, getDPoPJkt as s, AUTHENTICATED_SCOPE as t, getOrgId as u, getUserRoles as v, isService as w, isElevated as x, hasOrgAccess as y };
|