@classytic/arc 2.20.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BaseController-D-MHZ27_.mjs → BaseController-C6FlRWxF.mjs} +2 -2
- package/dist/{EventTransport-IEn7Tn-Q.d.mts → EventTransport-QmY7A258.d.mts} +0 -1
- package/dist/{QueryCache-TRfXau3W.d.mts → QueryCache-Z1B38koh.d.mts} +0 -1
- package/dist/{actionPermissions-C9RMiIpc.mjs → actionPermissions-ze70xXJi.mjs} +1 -1
- package/dist/audit/index.d.mts +9 -5
- package/dist/auth/audit.d.mts +7 -4
- package/dist/auth/audit.mjs +5 -0
- package/dist/auth/index.d.mts +35 -14
- package/dist/auth/index.mjs +6 -3
- package/dist/auth/redis-session.d.mts +1 -2
- package/dist/cache/index.d.mts +2 -3
- package/dist/cli/commands/describe.d.mts +1 -2
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/context/index.d.mts +2 -2
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +4 -4
- package/dist/{core-CWhyGkiM.mjs → core-b6fgFgnd.mjs} +129 -17
- package/dist/{createActionRouter-HlQso0jO.mjs → createActionRouter-h1cEEaEX.mjs} +10 -6
- package/dist/{createAggregationRouter-5LzPzgw1.mjs → createAggregationRouter-B1C0kvTL.mjs} +3 -3
- package/dist/{createApp-D6uwzCKu.mjs → createApp-Ci7yG5gv.mjs} +390 -192
- package/dist/discovery/index.d.mts +0 -1
- package/dist/docs/index.d.mts +1 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/{elevation-xGUBq6p_.d.mts → elevation-C9s5hFP_.d.mts} +0 -1
- package/dist/encryption/index.d.mts +2 -144
- package/dist/encryption/index.mjs +9 -5
- package/dist/{errorHandler-Dxf6P_kV.mjs → errorHandler-BTzXSwIg.mjs} +24 -3
- package/dist/{errorHandler-BtUUDPEa.d.mts → errorHandler-C6yv4ZTW.d.mts} +13 -1
- package/dist/{errors-DOHkjQlD.d.mts → errors-DHcHVAgk.d.mts} +0 -1
- package/dist/{eventPlugin-DbBFd3x_.d.mts → eventPlugin-A6EbthXR.d.mts} +10 -6
- package/dist/events/index.d.mts +9 -6
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -2
- package/dist/factory/index.d.mts +30 -9
- package/dist/factory/index.mjs +35 -4
- package/dist/{fastify-augmentation-CCQJEDgh.d.mts → fastify-augmentation-9_fuetB4.d.mts} +2 -3
- package/dist/fields-D3lhgzhD.d.mts +121 -0
- package/dist/health-ScyjIVo5.mjs +202 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +4 -3
- package/dist/idempotency/index.mjs +2 -0
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{types-BHMKlMBU.d.mts → index-BEu4wjbL.d.mts} +241 -80
- package/dist/{index-DUa8WHz3.d.mts → index-D8MKYGDa.d.mts} +88 -7
- package/dist/{index-_--GyzcO.d.mts → index-DbtgCOCj.d.mts} +1324 -1228
- package/dist/{index-CdFkIMEd.d.mts → index-ogIHvOjo.d.mts} +36 -7
- package/dist/index.d.mts +8 -7
- package/dist/index.mjs +6 -6
- package/dist/{init-YH_nslQR.mjs → init-DleK3p9f.mjs} +272 -139
- package/dist/integrations/event-gateway.d.mts +2 -3
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/jobs.d.mts +0 -1
- package/dist/integrations/mcp/index.d.mts +6 -7
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -2
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/streamline.d.mts +2 -2
- package/dist/integrations/webhooks.d.mts +8 -5
- package/dist/integrations/webhooks.mjs +9 -5
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -2
- package/dist/integrations/websocket-redis.d.mts +1 -2
- package/dist/integrations/websocket.d.mts +2 -2
- package/dist/jsonBody-BlzAoGCe.mjs +43 -0
- package/dist/{loadResources-B8UmpITt.mjs → loadResources-DpU8pfjV.mjs} +4 -3
- package/dist/logger/index.d.mts +26 -1
- package/dist/logger/index.mjs +39 -1
- package/dist/{mcpPlugin-CuW6L_uD.mjs → mcpPlugin-ByA2tf6D.mjs} +8 -4
- package/dist/middleware/index.d.mts +1 -2
- package/dist/{module-JYH9V0I8.d.mts → module-B4u10iOJ.d.mts} +71 -1
- package/dist/{openapi-Dq6RbEGK.mjs → openapi-BRpMZD7C.mjs} +3 -3
- package/dist/permissions/index.d.mts +4 -3
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-CgbOLJZG.mjs → permissions-BJMuKMl7.mjs} +33 -1
- package/dist/pipeline/index.d.mts +1 -2
- package/dist/plugins/index.d.mts +96 -7
- package/dist/plugins/index.mjs +86 -268
- package/dist/plugins/response-cache.d.mts +5 -3
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +7 -4
- package/dist/presets/filesUpload.d.mts +4 -5
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +1 -2
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -2
- package/dist/presets/search.d.mts +2 -3
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-DWsYQdbV.mjs → presets-gw0ciIUD.mjs} +1 -1
- package/dist/{pushref-registry-DnCqL3aa.d.mts → pushref-registry-BbcnpNTm.d.mts} +4 -2
- package/dist/{queryCachePlugin-xzFyKAjT.d.mts → queryCachePlugin-CfYWq-Ak.d.mts} +1 -2
- package/dist/rateLimitKey-BoWRH3e7.mjs +14 -0
- package/dist/{redis-C3W0qbz8.d.mts → redis-UDtLaNj6.d.mts} +0 -1
- package/dist/{redis-stream-DsP1vqZB.d.mts → redis-stream-iVR2l7aS.d.mts} +1 -2
- package/dist/{registerRoute-DGSREwPw.mjs → registerRoute-xHc42fwB.mjs} +38 -18
- package/dist/registry/index.d.mts +2 -2
- package/dist/requestId-5BhTVzBv.mjs +113 -0
- package/dist/schemaConverter-Ds5reSGs.mjs +168 -0
- package/dist/{schemaIR-ajetw3Fv.mjs → schemaIR-B-6SPe7g.mjs} +2 -2
- package/dist/schemas/index.d.mts +0 -1
- package/dist/scim/index.d.mts +0 -1
- package/dist/scim/index.mjs +6 -7
- package/dist/scope/index.d.mts +2 -3
- package/dist/scope/index.mjs +2 -76
- package/dist/scope-k15RXfcq.mjs +64 -0
- package/dist/{sessionManager-BXG-HGwG.d.mts → sessionManager-BHOM3N_0.d.mts} +10 -6
- package/dist/{storage-Co_PQpeQ.d.mts → storage-DLUZ04kK.d.mts} +8 -4
- package/dist/testing/index.d.mts +83 -4
- package/dist/testing/index.mjs +74 -4
- package/dist/testing/storageContract.d.mts +1 -2
- package/dist/{tracing-D8jlGXP7.d.mts → tracing-CsGKQJGg.d.mts} +0 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-C9b2HR5W.d.mts → types-Bc42CUyk.d.mts} +1 -2
- package/dist/types-Cc91nXuA.d.mts +132 -0
- package/dist/{fields-C7ahdGYf.d.mts → types-Csxga7d3.d.mts} +2 -123
- package/dist/{types-LNebCoJx.d.mts → types-DH0GupI7.d.mts} +8 -4
- package/dist/usage/index.d.mts +142 -0
- package/dist/usage/index.mjs +0 -0
- package/dist/utils/index.d.mts +220 -144
- package/dist/utils/index.mjs +3 -3
- package/dist/{utils-ChjYQpjn.mjs → utils-FhJ88TJE.mjs} +129 -39
- package/dist/{versioning-D_eBCfNr.d.mts → versioning-DLwklPlG.d.mts} +1 -7
- package/dist/{websocket-MlYlrYq0.d.mts → websocket-uuWqM6UY.d.mts} +5 -4
- package/llms.txt +39 -0
- package/package.json +453 -447
- package/skills/arc/SKILL.md +14 -8
- package/skills/arc/references/api-reference.md +34 -0
- package/skills/arc/references/auth.md +21 -4
- package/skills/arc/references/mcp.md +1 -1
- package/skills/arc/references/production.md +44 -2
- package/skills/arc-code-review/SKILL.md +1 -1
- package/skills/arc-code-review/references/anti-patterns.md +71 -18
- package/skills/arc-code-review/references/arc-cheatsheet.md +15 -2
- package/skills/arc-code-review/references/migration-recipes.md +7 -7
- package/skills/arc-code-review/references/mongokit-migration.md +4 -3
- package/skills/arc-module-publishing/SKILL.md +243 -0
- package/skills/arc-module-publishing/references/authoring-modules.md +327 -0
- package/skills/arc-module-publishing/references/package-manifest.md +157 -0
- package/skills/arc-module-publishing/references/publishing-workflow.md +182 -0
- package/dist/preloadResources-CHw3_oA_.mjs +0 -45
- package/dist/schemaConverter-U0MnO9vd.mjs +0 -110
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
-
|
|
3
2
|
//#region src/auth/sessionManager.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Session data stored in the session store.
|
|
@@ -81,13 +80,18 @@ declare module "fastify" {
|
|
|
81
80
|
authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
82
81
|
/** Session management helpers */
|
|
83
82
|
sessionManager: {
|
|
84
|
-
/** Create a new session for a user */
|
|
83
|
+
/** Create a new session for a user */
|
|
84
|
+
createSession: (userId: string, metadata?: Record<string, unknown>) => Promise<{
|
|
85
85
|
sessionId: string;
|
|
86
86
|
cookie: string;
|
|
87
|
-
}>;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
}>;
|
|
88
|
+
/** Revoke a specific session */
|
|
89
|
+
revokeSession: (sessionId: string) => Promise<void>;
|
|
90
|
+
/** Revoke all sessions for a user */
|
|
91
|
+
revokeAllSessions: (userId: string) => Promise<void>;
|
|
92
|
+
/** Revoke all sessions except the current one */
|
|
93
|
+
revokeOtherSessions: (userId: string, currentSessionId: string) => Promise<void>;
|
|
94
|
+
/** Refresh a session (reset updatedAt, extend expiry if needed) */
|
|
91
95
|
refreshSession: (sessionId: string) => Promise<SessionData | null>;
|
|
92
96
|
};
|
|
93
97
|
}
|
|
@@ -98,14 +98,18 @@ interface StorageReadRange {
|
|
|
98
98
|
type StorageReadResult = {
|
|
99
99
|
kind: "stream";
|
|
100
100
|
stream: NodeJS.ReadableStream;
|
|
101
|
-
contentType: string;
|
|
102
|
-
|
|
101
|
+
contentType: string;
|
|
102
|
+
/** Total size of the full object (NOT the ranged slice). Required for `Content-Range`. */
|
|
103
|
+
bytes?: number;
|
|
104
|
+
/** Actual byte range returned when the caller passed `range`. */
|
|
103
105
|
range?: StorageReadRange;
|
|
104
106
|
} | {
|
|
105
107
|
kind: "buffer";
|
|
106
108
|
buffer: Buffer;
|
|
107
|
-
contentType: string;
|
|
108
|
-
|
|
109
|
+
contentType: string;
|
|
110
|
+
/** Total size of the full object. Used for `Content-Range` on partial reads. */
|
|
111
|
+
totalBytes?: number;
|
|
112
|
+
/** Actual byte range returned when the caller passed `range`. */
|
|
109
113
|
range?: StorageReadRange;
|
|
110
114
|
};
|
|
111
115
|
/**
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { V as ResourceDefinition, qt as AnyRecord } from "../index-
|
|
2
|
-
import {
|
|
1
|
+
import { V as ResourceDefinition, qt as AnyRecord } from "../index-DbtgCOCj.mjs";
|
|
2
|
+
import { n as ArcModuleInput } from "../module-B4u10iOJ.mjs";
|
|
3
|
+
import { d as preloadResourcesAsync, t as CreateAppOptions, u as preloadResources } from "../index-BEu4wjbL.mjs";
|
|
3
4
|
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
4
5
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
5
6
|
import { Mock } from "vitest";
|
|
6
7
|
import { StandardRepo } from "@classytic/repo-core/repository";
|
|
7
|
-
|
|
8
8
|
//#region src/testing/assertions.d.ts
|
|
9
9
|
/**
|
|
10
10
|
* expectArc — Arc-specific response assertions
|
|
@@ -297,6 +297,85 @@ declare function createBetterAuthTestHelpers(options?: BetterAuthTestHelpersOpti
|
|
|
297
297
|
*/
|
|
298
298
|
declare function setupBetterAuthTestApp(input: SetupBetterAuthTestAppInput): Promise<SetupBetterAuthTestAppResult>;
|
|
299
299
|
//#endregion
|
|
300
|
+
//#region src/testing/bootModuleApp.d.ts
|
|
301
|
+
/** Handed to a modules factory — the in-memory DB is live at call time. */
|
|
302
|
+
/**
|
|
303
|
+
* A provisioned test database — what the `database` seam yields. Arc
|
|
304
|
+
* core never names a driver: kits ship factories (`mongoMemoryDatabase`
|
|
305
|
+
* is the built-in default; a sqlitekit/pgkit testkit ships its own) and
|
|
306
|
+
* the connection flows through as an opaque generic.
|
|
307
|
+
*/
|
|
308
|
+
interface TestDatabase<TConn = unknown> {
|
|
309
|
+
/** Connection string, when the backend has one (informational). */
|
|
310
|
+
uri: string;
|
|
311
|
+
/** Driver connection handle passed to the modules factory. */
|
|
312
|
+
connection: TConn;
|
|
313
|
+
/** Stop/disconnect everything this factory started. MUST be idempotent-safe to call once. */
|
|
314
|
+
teardown(): Promise<void>;
|
|
315
|
+
}
|
|
316
|
+
/** The DB-provisioning seam — `bootModuleApp({ database })`. */
|
|
317
|
+
type TestDatabaseFactory<TConn = unknown> = (opts: {
|
|
318
|
+
replset?: boolean;
|
|
319
|
+
}) => Promise<TestDatabase<TConn>>;
|
|
320
|
+
interface TestkitContext<TConn = unknown> {
|
|
321
|
+
/** The provisioned database's connection string. */
|
|
322
|
+
uri: string;
|
|
323
|
+
/** @deprecated Alias of `uri` from the Mongo-default era — prefer `uri`. */
|
|
324
|
+
mongoUri: string;
|
|
325
|
+
/**
|
|
326
|
+
* Driver connection handle (the default factory yields mongoose's
|
|
327
|
+
* DEFAULT connection). Generic so DB-typed wrappers assert the driver
|
|
328
|
+
* type ONCE: `bootModuleApp<Connection>(...)`. Arc core stays DB-agnostic.
|
|
329
|
+
*/
|
|
330
|
+
connection: TConn;
|
|
331
|
+
}
|
|
332
|
+
type BootModulesInput<TConn = unknown> = ArcModuleInput | readonly ArcModuleInput[] | ((ctx: TestkitContext<TConn>) => ArcModuleInput | readonly ArcModuleInput[] | Promise<ArcModuleInput | readonly ArcModuleInput[]>);
|
|
333
|
+
interface BootModuleAppOptions<TConn = unknown> {
|
|
334
|
+
/**
|
|
335
|
+
* DB provisioner. Default: `mongoMemoryDatabase` (in-memory MongoDB —
|
|
336
|
+
* lazy devDep imports, nothing Mongo touches arc's runtime). Kits for
|
|
337
|
+
* other stores inject their own factory here; the rest of the harness
|
|
338
|
+
* (module resolution, app boot, teardown ordering) is DB-agnostic.
|
|
339
|
+
*/
|
|
340
|
+
database?: TestDatabaseFactory<TConn>;
|
|
341
|
+
/** Single-node replica set (transactions). Default: standalone. Passed to the database factory. */
|
|
342
|
+
replset?: boolean;
|
|
343
|
+
/**
|
|
344
|
+
* Extra `createApp` options merged over the testing defaults
|
|
345
|
+
* (`preset: 'testing'`, `auth: false`, `sensible: false` — utility
|
|
346
|
+
* plugins stay off so the testkit is dependency-light; re-enable per
|
|
347
|
+
* boot when a module genuinely exercises one).
|
|
348
|
+
*/
|
|
349
|
+
app?: Partial<CreateAppOptions>;
|
|
350
|
+
}
|
|
351
|
+
interface ModuleTestApp<TConn = unknown> {
|
|
352
|
+
app: FastifyInstance;
|
|
353
|
+
/** The provisioned database's connection string. */
|
|
354
|
+
uri: string;
|
|
355
|
+
/** @deprecated Alias of `uri` from the Mongo-default era — prefer `uri`. */
|
|
356
|
+
mongoUri: string;
|
|
357
|
+
/** The mongoose `Connection` handed to the modules factory. */
|
|
358
|
+
connection: TConn;
|
|
359
|
+
/**
|
|
360
|
+
* Typed module-export accessor — `t.exports<CatalogEngine>('catalog')`
|
|
361
|
+
* instead of reaching through `(t.app as ...).arc.modules.catalog`.
|
|
362
|
+
* Delegates to `getModuleExports` (throws with the registered-module
|
|
363
|
+
* list when the name is unknown — same contract hosts get).
|
|
364
|
+
*/
|
|
365
|
+
exports<TExports = unknown>(name: string): TExports;
|
|
366
|
+
/** Close app → disconnect mongoose → stop the in-memory server. Idempotent. */
|
|
367
|
+
close(): Promise<void>;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* The built-in DB factory: in-memory MongoDB via lazy devDep imports.
|
|
371
|
+
* Everything Mongo-specific about the harness lives HERE — the harness
|
|
372
|
+
* itself is driver-blind and any kit can replace this via `database`.
|
|
373
|
+
*/
|
|
374
|
+
declare function mongoMemoryDatabase(opts: {
|
|
375
|
+
replset?: boolean;
|
|
376
|
+
}): Promise<TestDatabase<unknown>>;
|
|
377
|
+
declare function bootModuleApp<TConn = unknown>(modules: BootModulesInput<TConn>, options?: BootModuleAppOptions<TConn>): Promise<ModuleTestApp<TConn>>;
|
|
378
|
+
//#endregion
|
|
300
379
|
//#region src/testing/fixtures.d.ts
|
|
301
380
|
type FixtureFactory<T extends AnyRecord = AnyRecord> = (data: Partial<T>) => Promise<T>;
|
|
302
381
|
/** Delete hook invoked by `clear()` for records that were created through a factory. */
|
|
@@ -541,4 +620,4 @@ declare function createTestApp(options?: CreateTestAppOptions): Promise<TestAppC
|
|
|
541
620
|
*/
|
|
542
621
|
declare function createMinimalTestApp(options?: FastifyServerOptions): FastifyInstance;
|
|
543
622
|
//#endregion
|
|
544
|
-
export { type ArcAssertion, type ArcResponseLike, type AuthMode, type AuthResponse, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type BetterAuthTestUser, type CreateOrgInput, type CreateTestAppOptions, type DbMode, type FixtureDestroyer, type FixtureFactory, type FixtureRegistration, HttpTestHarness, type HttpTestHarnessOptions, type MockRepository, type OrgResponse, type RoleConfig, type SetupBetterAuthTestAppInput, type SetupBetterAuthTestAppResult, type SignInInput, type SignUpInput, type StorageContractSetup, type StorageContractSetupResult, type TestAppContext, type TestAuthProvider, type TestAuthSession, type TestFixtures, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, preloadResources, preloadResourcesAsync, runStorageContract, safeParseBody, setupBetterAuthTestApp, waitFor };
|
|
623
|
+
export { type ArcAssertion, type ArcResponseLike, type AuthMode, type AuthResponse, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type BetterAuthTestUser, type BootModuleAppOptions, type BootModulesInput, type CreateOrgInput, type CreateTestAppOptions, type DbMode, type FixtureDestroyer, type FixtureFactory, type FixtureRegistration, HttpTestHarness, type HttpTestHarnessOptions, type MockRepository, type ModuleTestApp, type OrgResponse, type RoleConfig, type SetupBetterAuthTestAppInput, type SetupBetterAuthTestAppResult, type SignInInput, type SignUpInput, type StorageContractSetup, type StorageContractSetupResult, type TestAppContext, type TestAuthProvider, type TestAuthSession, type TestDatabase, type TestDatabaseFactory, type TestFixtures, type TestkitContext, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runStorageContract, safeParseBody, setupBetterAuthTestApp, waitFor };
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-DNre0wRg.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { d as getModuleExports, h as preloadResourcesAsync, m as preloadResources, n as createApp } from "../createApp-Ci7yG5gv.mjs";
|
|
3
3
|
import { runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import Fastify from "fastify";
|
|
5
5
|
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
@@ -210,7 +210,10 @@ function createJwtAuthProvider(app, opts = {}) {
|
|
|
210
210
|
if (!config.user) throw new Error(`[jwt] role '${role}' has neither 'user' nor 'token'`);
|
|
211
211
|
const jwt = app.jwt;
|
|
212
212
|
if (!jwt?.sign) throw new Error(`[jwt] app.jwt.sign() is unavailable — register @fastify/jwt before calling createJwtAuthProvider.`);
|
|
213
|
-
return jwt.sign(
|
|
213
|
+
return jwt.sign({
|
|
214
|
+
type: "access",
|
|
215
|
+
...config.user
|
|
216
|
+
});
|
|
214
217
|
}
|
|
215
218
|
});
|
|
216
219
|
}
|
|
@@ -459,6 +462,73 @@ async function setupBetterAuthTestApp(input) {
|
|
|
459
462
|
};
|
|
460
463
|
}
|
|
461
464
|
//#endregion
|
|
465
|
+
//#region src/testing/bootModuleApp.ts
|
|
466
|
+
/**
|
|
467
|
+
* The built-in DB factory: in-memory MongoDB via lazy devDep imports.
|
|
468
|
+
* Everything Mongo-specific about the harness lives HERE — the harness
|
|
469
|
+
* itself is driver-blind and any kit can replace this via `database`.
|
|
470
|
+
*/
|
|
471
|
+
async function mongoMemoryDatabase(opts) {
|
|
472
|
+
let mms;
|
|
473
|
+
try {
|
|
474
|
+
mms = await import("mongodb-memory-server");
|
|
475
|
+
} catch {
|
|
476
|
+
throw new Error("[arc-testing] bootModuleApp's default database is in-memory MongoDB — install 'mongodb-memory-server' + 'mongoose' as devDependencies, or inject your own `database` factory for a different store");
|
|
477
|
+
}
|
|
478
|
+
const server = opts.replset ? await mms.MongoMemoryReplSet.create({ replSet: { count: 1 } }) : await mms.MongoMemoryServer.create();
|
|
479
|
+
const uri = server.getUri();
|
|
480
|
+
const mongoose = (await import("mongoose")).default;
|
|
481
|
+
if (mongoose.connection.readyState !== 0) await mongoose.disconnect();
|
|
482
|
+
await mongoose.connect(uri);
|
|
483
|
+
mongoose.connection.deleteModel(/.*/);
|
|
484
|
+
return {
|
|
485
|
+
uri,
|
|
486
|
+
connection: mongoose.connection,
|
|
487
|
+
teardown: async () => {
|
|
488
|
+
if (mongoose.connection.readyState !== 0) await mongoose.disconnect().catch(() => void 0);
|
|
489
|
+
await server.stop().catch(() => void 0);
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
async function bootModuleApp(modules, options = {}) {
|
|
494
|
+
const db = await (options.database ?? mongoMemoryDatabase)({ replset: options.replset ?? false });
|
|
495
|
+
const ctx = {
|
|
496
|
+
uri: db.uri,
|
|
497
|
+
mongoUri: db.uri,
|
|
498
|
+
connection: db.connection
|
|
499
|
+
};
|
|
500
|
+
const resolved = typeof modules === "function" ? await modules(ctx) : modules;
|
|
501
|
+
const list = Array.isArray(resolved) ? resolved : [resolved];
|
|
502
|
+
let app;
|
|
503
|
+
try {
|
|
504
|
+
app = await createApp({
|
|
505
|
+
preset: "testing",
|
|
506
|
+
auth: false,
|
|
507
|
+
sensible: false,
|
|
508
|
+
...options.app,
|
|
509
|
+
modules: list
|
|
510
|
+
});
|
|
511
|
+
await app.ready();
|
|
512
|
+
} catch (err) {
|
|
513
|
+
await db.teardown().catch(() => void 0);
|
|
514
|
+
throw err;
|
|
515
|
+
}
|
|
516
|
+
let closed = false;
|
|
517
|
+
return {
|
|
518
|
+
app,
|
|
519
|
+
uri: db.uri,
|
|
520
|
+
mongoUri: db.uri,
|
|
521
|
+
connection: db.connection,
|
|
522
|
+
exports: (name) => getModuleExports(app, name),
|
|
523
|
+
close: async () => {
|
|
524
|
+
if (closed) return;
|
|
525
|
+
closed = true;
|
|
526
|
+
await app.close().catch(() => void 0);
|
|
527
|
+
await db.teardown().catch(() => void 0);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
//#endregion
|
|
462
532
|
//#region src/testing/fixtures.ts
|
|
463
533
|
function createTestFixtures() {
|
|
464
534
|
const registry = /* @__PURE__ */ new Map();
|
|
@@ -1046,7 +1116,7 @@ function pickDefaultAuth(authMode, callerAuth) {
|
|
|
1046
1116
|
};
|
|
1047
1117
|
}
|
|
1048
1118
|
async function createTestApp(options = {}) {
|
|
1049
|
-
const { createApp } = await import("../createApp-
|
|
1119
|
+
const { createApp } = await import("../createApp-Ci7yG5gv.mjs").then((n) => n.r);
|
|
1050
1120
|
const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
|
|
1051
1121
|
let dbHandle;
|
|
1052
1122
|
let dbUri;
|
|
@@ -1116,4 +1186,4 @@ function createMinimalTestApp(options = {}) {
|
|
|
1116
1186
|
});
|
|
1117
1187
|
}
|
|
1118
1188
|
//#endregion
|
|
1119
|
-
export { HttpTestHarness, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, preloadResources, preloadResourcesAsync, runStorageContract, safeParseBody, setupBetterAuthTestApp, waitFor };
|
|
1189
|
+
export { HttpTestHarness, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runStorageContract, safeParseBody, setupBetterAuthTestApp, waitFor };
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { i as RequestScope } from "../types-
|
|
3
|
-
import {
|
|
4
|
-
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { $ as OpenApiSchemas, $t as UserOrganization, A as RequestIdOptions, At as RouteDefinition, Bt as RouteHandler, C as RequestContext, D as HealthCheck, Dt as ActionEntry, E as GracefulShutdownOptions, Et as ActionDefinition, F as FastifyWithDecorators, Fn as BaseControllerOptions, Ft as ControllerLike, G as OnTenantDeleteConfig, Gt as JwtContext, H as CrudController, Ht as AuthPluginOptions, I as MiddlewareHandler, It as FastifyHandler, J as PresetHook, Jt as ApiResponse, K as ResolvedTenantPurge, Kt as TokenPair, L as RequestWithExtras, Lt as IController, M as EventsDecorator, Mt as RouteMethod, N as FastifyRequestExtras, Nt as RateLimitConfig, O as HealthOptions, Ot as ActionHandlerFn, P as FastifyWithAuth, Pt as ControllerHandler, Q as MiddlewareConfig, Qt as UserLike, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, Ut as Authenticator, Vt as AuthHelpers, W as ResourcePermissions, Wt as AuthenticatorContext, X as CrudRouteKey, Xt as JWTPayload, Y as PresetResult, Yt as ArcRequest, Z as CrudSchemas, Zt as ObjectId, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, et as RouteSchemaOptions, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, i as ValidationResult, it as ResourceHooks, j as ArcDecorator, jt as RouteMcpConfig, k as IntrospectionPluginOptions, kt as ActionsMap, l as TypedRepository, m as RegistryStats, n as ConfigError, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, q as PresetFunction, qt as AnyRecord, r as ValidateOptions, rt as ResourceHookContext, s as InferResourceDoc, t as RouteHandlerMethod, tt as ArcFieldRule, u as TypedResourceConfig, v as LookupOption, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, zt as IRequestContext } from "../index-DbtgCOCj.mjs";
|
|
2
|
+
import { i as RequestScope } from "../types-DH0GupI7.mjs";
|
|
3
|
+
import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-Csxga7d3.mjs";
|
|
4
|
+
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-C9s5hFP_.mjs";
|
|
5
5
|
export type { 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, OnTenantDeleteConfig, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResolvedTenantPurge, ResourceCacheConfig, ResourceConfig, ResourceExtensions, 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-DLUZ04kK.mjs";
|
|
2
2
|
export { Storage, StorageContext, StorageFile, StorageReadRange, StorageReadResult, StorageUploadInput };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { V as ResourceDefinition } from "./index-
|
|
1
|
+
import { V as ResourceDefinition } from "./index-DbtgCOCj.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
|
|
4
3
|
//#region src/integrations/mcp/types.d.ts
|
|
5
4
|
/** Behavioral hints for MCP clients (tool annotations per MCP spec) */
|
|
6
5
|
interface ToolAnnotations {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { FastifyRequest } from "fastify";
|
|
2
|
+
//#region src/encryption/types.d.ts
|
|
3
|
+
/** JWE key-management algorithm — the protected-header `alg`. */
|
|
4
|
+
type JweAlg = "RSA-OAEP-256" | "RSA-OAEP-384" | "RSA-OAEP-512" | "ECDH-ES" | "ECDH-ES+A256KW" | "A256GCMKW" | "dir";
|
|
5
|
+
/** JWE content-encryption algorithm — the protected-header `enc`. AEAD only. */
|
|
6
|
+
type JweEnc = "A128GCM" | "A192GCM" | "A256GCM";
|
|
7
|
+
/** Encryption strategy for a route's response. */
|
|
8
|
+
type EncryptionMode = "jwe" | "fields";
|
|
9
|
+
/**
|
|
10
|
+
* Opaque key material. For `jwe` mode this is a `jose` `KeyLike` /
|
|
11
|
+
* `CryptoKey` / `Uint8Array`; for `fields` mode a 32-byte secret
|
|
12
|
+
* (`Buffer` / `Uint8Array`). Kept `unknown` at the contract layer so the
|
|
13
|
+
* `KeyProvider` interface carries no hard `jose` type dependency — hosts
|
|
14
|
+
* without `jose` installed can still implement field-mode providers.
|
|
15
|
+
*/
|
|
16
|
+
type KeyMaterial = unknown;
|
|
17
|
+
/** A key paired with its identifier — what encrypt operations consume. */
|
|
18
|
+
interface ActiveKey {
|
|
19
|
+
/** Key id, stamped into the JWE `kid` header / field envelope for rotation. */
|
|
20
|
+
readonly kid: string;
|
|
21
|
+
/** The key material (public key for JWE, 32-byte secret for fields). */
|
|
22
|
+
readonly key: KeyMaterial;
|
|
23
|
+
/** Optional per-key `alg` override (else the plugin default applies). */
|
|
24
|
+
readonly alg?: JweAlg;
|
|
25
|
+
}
|
|
26
|
+
/** Context handed to the key provider so resolution can be request-scoped. */
|
|
27
|
+
interface KeyContext {
|
|
28
|
+
readonly request: FastifyRequest;
|
|
29
|
+
}
|
|
30
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Pluggable key resolution. Arc owns the request-time plumbing — `kid` →
|
|
33
|
+
* key, algorithm allowlist enforcement; the host owns the trust store —
|
|
34
|
+
* which keys are valid and where they live (file / env / AWS KMS / GCP KMS
|
|
35
|
+
* / Vault). Encryption and decryption are deliberately separate operations
|
|
36
|
+
* because they often use DIFFERENT keys (JWE: a public key encrypts the
|
|
37
|
+
* outbound response, a private key by `kid` decrypts the inbound request —
|
|
38
|
+
* exactly Visa's two-key-pair model).
|
|
39
|
+
*
|
|
40
|
+
* Rotation mirrors `@fastify/secure-session`: the active key encrypts; any
|
|
41
|
+
* key resolvable by `kid` can decrypt, so you can roll keys without dropping
|
|
42
|
+
* in-flight traffic.
|
|
43
|
+
*/
|
|
44
|
+
interface KeyProvider {
|
|
45
|
+
/** Active key for encrypting OUTBOUND responses. */
|
|
46
|
+
encryptionKey(ctx: KeyContext): MaybePromise<ActiveKey>;
|
|
47
|
+
/** Resolve a DECRYPTION key by the inbound message's `kid`. */
|
|
48
|
+
decryptionKey(kid: string, ctx: KeyContext): MaybePromise<KeyMaterial | undefined>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Per-resource encryption directive — the typed slice plugins register on
|
|
52
|
+
* `ResourceExtensions`. Declared on a resource via
|
|
53
|
+
* `defineResource({ extensions: { encryption: { … } } })`; arc threads it
|
|
54
|
+
* to request time on `request.routeOptions.config.arcExtensions.encryption`.
|
|
55
|
+
*/
|
|
56
|
+
interface EncryptionDirective {
|
|
57
|
+
/** Strategy. Defaults to the plugin-level `mode` (default `'jwe'`). */
|
|
58
|
+
readonly mode?: EncryptionMode;
|
|
59
|
+
/**
|
|
60
|
+
* For `mode: 'fields'` — dot-paths of fields to encrypt, resolved against
|
|
61
|
+
* the serialized response body (e.g. `'cardNumber'`, `'account.iban'`).
|
|
62
|
+
* Ignored for `mode: 'jwe'` (the whole body is encrypted).
|
|
63
|
+
*/
|
|
64
|
+
readonly fields?: readonly string[];
|
|
65
|
+
/**
|
|
66
|
+
* Explicit opt-out — set `false` to disable encryption for this resource
|
|
67
|
+
* even when a plugin-level default (`routes`) would otherwise apply.
|
|
68
|
+
*/
|
|
69
|
+
readonly enabled?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Plugin-level options. `keyProvider` is the only required field; everything
|
|
73
|
+
* else has a secure default.
|
|
74
|
+
*/
|
|
75
|
+
interface EncryptionOptions {
|
|
76
|
+
/**
|
|
77
|
+
* Master on/off. Default `true`. Set `false` to register the plugin as a
|
|
78
|
+
* typed no-op (config-gated environments) — same pattern as
|
|
79
|
+
* `idempotencyPlugin`.
|
|
80
|
+
*/
|
|
81
|
+
readonly enabled?: boolean;
|
|
82
|
+
/** Key provider — resolves `kid` → key for encrypt/decrypt. Required. */
|
|
83
|
+
readonly keyProvider: KeyProvider;
|
|
84
|
+
/** Default mode when a route opts in without naming one. Default `'jwe'`. */
|
|
85
|
+
readonly mode?: EncryptionMode;
|
|
86
|
+
/** JWE key-management algorithm. Default `'RSA-OAEP-256'`. */
|
|
87
|
+
readonly alg?: JweAlg;
|
|
88
|
+
/** JWE content-encryption algorithm. Default `'A256GCM'`. */
|
|
89
|
+
readonly enc?: JweEnc;
|
|
90
|
+
/**
|
|
91
|
+
* Inbound-decryption algorithm allowlists — block algorithm-substitution
|
|
92
|
+
* downgrade attacks. Default `[alg]` / `[enc]`. Set explicitly only when a
|
|
93
|
+
* client legitimately uses more than one algorithm.
|
|
94
|
+
*/
|
|
95
|
+
readonly allowedAlgs?: readonly JweAlg[];
|
|
96
|
+
readonly allowedEncs?: readonly JweEnc[];
|
|
97
|
+
/**
|
|
98
|
+
* Fallback opt-in for routes that don't declare
|
|
99
|
+
* `extensions.encryption`. Return a directive (or `true` for the default
|
|
100
|
+
* mode) to encrypt, `false`/`undefined` to skip. The per-resource
|
|
101
|
+
* `extensions.encryption` ALWAYS wins when present. Use this to scope ALE
|
|
102
|
+
* to sensitive prefixes without touching every resource.
|
|
103
|
+
*
|
|
104
|
+
* @example confine ALE to `/payments` and `/accounts`
|
|
105
|
+
* ```ts
|
|
106
|
+
* routes: (req) => /^\/(payments|accounts)\b/.test(req.url)
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
readonly routes?: (request: FastifyRequest) => EncryptionDirective | boolean | undefined;
|
|
110
|
+
/** Decrypt inbound `application/jose` request bodies. Default `true`. */
|
|
111
|
+
readonly decryptRequests?: boolean;
|
|
112
|
+
/** Media type that signals an encrypted REQUEST body. Default `'application/jose'`. */
|
|
113
|
+
readonly contentType?: string;
|
|
114
|
+
/** Content-Type set on full-body JWE RESPONSES. Default `'application/jose'`. */
|
|
115
|
+
readonly responseContentType?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Response header advertising that the body is encrypted, so generic
|
|
118
|
+
* client SDKs know to decrypt. Default `'x-encrypted'`; set `false` to omit.
|
|
119
|
+
*/
|
|
120
|
+
readonly flagHeader?: string | false;
|
|
121
|
+
}
|
|
122
|
+
/** Request decorations added by the plugin. */
|
|
123
|
+
declare module "fastify" {
|
|
124
|
+
interface FastifyRequest {
|
|
125
|
+
/** `true` when the inbound body was decrypted from a JWE by this plugin. */
|
|
126
|
+
requestDecrypted?: boolean;
|
|
127
|
+
/** `true` when the outbound body was encrypted by this plugin. */
|
|
128
|
+
responseEncrypted?: boolean;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { JweAlg as a, KeyMaterial as c, EncryptionOptions as i, KeyProvider as l, EncryptionDirective as n, JweEnc as o, EncryptionMode as r, KeyContext as s, ActiveKey as t };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { i as RequestScope } from "./types-
|
|
1
|
+
import { i as RequestScope } from "./types-DH0GupI7.mjs";
|
|
2
2
|
import { FastifyRequest } from "fastify";
|
|
3
|
-
|
|
4
3
|
//#region src/permissions/types.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* User base interface - minimal shape Arc expects
|
|
@@ -196,124 +195,4 @@ interface PermissionCheckMeta {
|
|
|
196
195
|
};
|
|
197
196
|
}
|
|
198
197
|
//#endregion
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Field-Level Permissions
|
|
202
|
-
*
|
|
203
|
-
* Control field visibility and writability per role.
|
|
204
|
-
* Integrated into the response path (read) and sanitization path (write).
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* ```typescript
|
|
208
|
-
* import { fields, defineResource } from '@classytic/arc';
|
|
209
|
-
*
|
|
210
|
-
* const userResource = defineResource({
|
|
211
|
-
* name: 'user',
|
|
212
|
-
* adapter: userAdapter,
|
|
213
|
-
* fields: {
|
|
214
|
-
* salary: fields.visibleTo(['admin', 'hr']),
|
|
215
|
-
* internalNotes: fields.writableBy(['admin']),
|
|
216
|
-
* email: fields.redactFor(['viewer']),
|
|
217
|
-
* password: fields.hidden(),
|
|
218
|
-
* },
|
|
219
|
-
* });
|
|
220
|
-
* ```
|
|
221
|
-
*/
|
|
222
|
-
type FieldPermissionType = "hidden" | "visibleTo" | "writableBy" | "redactFor";
|
|
223
|
-
interface FieldPermission {
|
|
224
|
-
readonly _type: FieldPermissionType;
|
|
225
|
-
readonly roles?: readonly string[];
|
|
226
|
-
readonly redactValue?: unknown;
|
|
227
|
-
}
|
|
228
|
-
type FieldPermissionMap = Record<string, FieldPermission>;
|
|
229
|
-
declare const fields: {
|
|
230
|
-
/**
|
|
231
|
-
* Field is never included in responses. Not writable via API.
|
|
232
|
-
*
|
|
233
|
-
* @example
|
|
234
|
-
* ```typescript
|
|
235
|
-
* fields: { password: fields.hidden() }
|
|
236
|
-
* ```
|
|
237
|
-
*/
|
|
238
|
-
hidden(): FieldPermission;
|
|
239
|
-
/**
|
|
240
|
-
* Field is only visible to users with specified roles.
|
|
241
|
-
* Other users don't see the field at all.
|
|
242
|
-
*
|
|
243
|
-
* @example
|
|
244
|
-
* ```typescript
|
|
245
|
-
* fields: { salary: fields.visibleTo(['admin', 'hr']) }
|
|
246
|
-
* ```
|
|
247
|
-
*/
|
|
248
|
-
visibleTo(roles: readonly string[]): FieldPermission;
|
|
249
|
-
/**
|
|
250
|
-
* Field is only writable by users with specified roles.
|
|
251
|
-
* All users can still read the field. Users without the role
|
|
252
|
-
* have the field silently stripped from write operations.
|
|
253
|
-
*
|
|
254
|
-
* @example
|
|
255
|
-
* ```typescript
|
|
256
|
-
* fields: { role: fields.writableBy(['admin']) }
|
|
257
|
-
* ```
|
|
258
|
-
*/
|
|
259
|
-
writableBy(roles: readonly string[]): FieldPermission;
|
|
260
|
-
/**
|
|
261
|
-
* Field is redacted (replaced with a placeholder) for specified roles.
|
|
262
|
-
* Other users see the real value.
|
|
263
|
-
*
|
|
264
|
-
* @param roles - Roles that see the redacted value
|
|
265
|
-
* @param redactValue - Replacement value (default: '***')
|
|
266
|
-
*
|
|
267
|
-
* @example
|
|
268
|
-
* ```typescript
|
|
269
|
-
* fields: {
|
|
270
|
-
* email: fields.redactFor(['viewer']),
|
|
271
|
-
* ssn: fields.redactFor(['basic'], '***-**-****'),
|
|
272
|
-
* }
|
|
273
|
-
* ```
|
|
274
|
-
*/
|
|
275
|
-
redactFor(roles: readonly string[], redactValue?: unknown): FieldPermission;
|
|
276
|
-
};
|
|
277
|
-
/**
|
|
278
|
-
* Apply field-level READ permissions to a response object.
|
|
279
|
-
* Strips hidden fields, enforces visibility, and applies redaction.
|
|
280
|
-
*
|
|
281
|
-
* @param data - The response object (mutated in place for performance)
|
|
282
|
-
* @param fieldPermissions - Field permission map from resource config
|
|
283
|
-
* @param userRoles - Current user's roles (empty array for unauthenticated)
|
|
284
|
-
* @returns The filtered object
|
|
285
|
-
*/
|
|
286
|
-
declare function applyFieldReadPermissions<T extends Record<string, unknown>>(data: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): T;
|
|
287
|
-
/**
|
|
288
|
-
* Result of applying write permissions — includes both the filtered body
|
|
289
|
-
* and the list of fields that were stripped so callers can decide whether
|
|
290
|
-
* to reject the request (secure default) or silently strip (legacy).
|
|
291
|
-
*/
|
|
292
|
-
interface FieldWritePermissionResult<T extends Record<string, unknown>> {
|
|
293
|
-
readonly body: T;
|
|
294
|
-
readonly deniedFields: readonly string[];
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Apply field-level WRITE permissions to request body.
|
|
298
|
-
*
|
|
299
|
-
* Returns both the filtered body and the list of denied fields. Callers are
|
|
300
|
-
* expected to reject the request when `deniedFields.length > 0` — silently
|
|
301
|
-
* stripping fields hides misconfigurations and real attacks. See
|
|
302
|
-
* `BodySanitizer` for the default policy.
|
|
303
|
-
*
|
|
304
|
-
* @param body - The request body (returns a new filtered copy)
|
|
305
|
-
* @param fieldPermissions - Field permission map from resource config
|
|
306
|
-
* @param userRoles - Current user's roles
|
|
307
|
-
*/
|
|
308
|
-
declare function applyFieldWritePermissions<T extends Record<string, unknown>>(body: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): FieldWritePermissionResult<T>;
|
|
309
|
-
/**
|
|
310
|
-
* Resolve effective roles by merging global user roles with org-level roles.
|
|
311
|
-
*
|
|
312
|
-
* Global roles come from `req.user.role` (normalized via getUserRoles()).
|
|
313
|
-
* Org roles come from `req.context.orgRoles` (set by BA adapter's org bridge).
|
|
314
|
-
*
|
|
315
|
-
* When no org context exists, returns global roles only — backward compatible.
|
|
316
|
-
*/
|
|
317
|
-
declare function resolveEffectiveRoles(userRoles: readonly string[], orgRoles: readonly string[]): string[];
|
|
318
|
-
//#endregion
|
|
319
|
-
export { applyFieldWritePermissions as a, PermissionCheck as c, UserBase as d, getUserRoles as f, applyFieldReadPermissions as i, PermissionContext as l, FieldPermissionMap as n, fields as o, normalizeRoles as p, FieldPermissionType as r, resolveEffectiveRoles as s, FieldPermission as t, PermissionResult as u };
|
|
198
|
+
export { getUserRoles as a, UserBase as i, PermissionContext as n, normalizeRoles as o, PermissionResult as r, PermissionCheck as t };
|
|
@@ -168,8 +168,10 @@ type RequestScope = {
|
|
|
168
168
|
kind: "service";
|
|
169
169
|
clientId: string;
|
|
170
170
|
organizationId: string;
|
|
171
|
-
scopes?: readonly string[];
|
|
172
|
-
|
|
171
|
+
scopes?: readonly string[];
|
|
172
|
+
/** App-defined scope dimensions — see `member.context` for details. */
|
|
173
|
+
context?: Readonly<Record<string, string>>;
|
|
174
|
+
/** Parent organizations — see `member.ancestorOrgIds` for details. */
|
|
173
175
|
ancestorOrgIds?: readonly string[];
|
|
174
176
|
/**
|
|
175
177
|
* Capability mandate — narrows what the caller may do beyond OAuth `scopes`.
|
|
@@ -207,8 +209,10 @@ type RequestScope = {
|
|
|
207
209
|
kind: "elevated";
|
|
208
210
|
userId?: string;
|
|
209
211
|
organizationId?: string;
|
|
210
|
-
elevatedBy: string;
|
|
211
|
-
|
|
212
|
+
elevatedBy: string;
|
|
213
|
+
/** App-defined scope dimensions — see `member.context` for details. */
|
|
214
|
+
context?: Readonly<Record<string, string>>;
|
|
215
|
+
/** Parent organizations — see `member.ancestorOrgIds` for details. */
|
|
212
216
|
ancestorOrgIds?: readonly string[];
|
|
213
217
|
};
|
|
214
218
|
/** Check if scope is `member` kind */
|