@classytic/arc 2.21.0 → 2.23.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-BnLYgE9A.mjs → BaseController-BQ8KNvWC.mjs} +3 -2
- package/dist/{ResourceRegistry-7RnrEWNP.mjs → ResourceRegistry-y9Hnypus.mjs} +1 -0
- package/dist/{actionPermissions-ze70xXJi.mjs → actionPermissions-lszoJle1.mjs} +1 -1
- package/dist/applyPermissionResult-Tw4iXtlf.mjs +264 -0
- package/dist/audit/index.d.mts +2 -2
- package/dist/auth/index.d.mts +30 -2
- package/dist/auth/index.mjs +6 -3
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-DZ6Xe1jR.mjs → core-DQ6rDTyH.mjs} +126 -68
- package/dist/{createActionRouter-BWMDW-dL.mjs → createActionRouter-BuOv-m4Y.mjs} +10 -6
- package/dist/{createAggregationRouter-l5mNdYrZ.mjs → createAggregationRouter-CgJv_BU_.mjs} +3 -3
- package/dist/{createApp-Bi61XxqU.mjs → createApp-CjOeWJ9_.mjs} +252 -57
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/encryption/index.mjs +9 -5
- package/dist/{errorHandler-Dxf6P_kV.mjs → errorHandler-Bq2OeEQL.mjs} +34 -10
- package/dist/{errorHandler-B0e-bq-O.d.mts → errorHandler-C6yv4ZTW.d.mts} +13 -0
- package/dist/events/index.mjs +5 -2
- package/dist/factory/index.d.mts +46 -9
- package/dist/factory/index.mjs +64 -13
- package/dist/{fastify-augmentation-DafigGQp.d.mts → fastify-augmentation-g_f9omSc.d.mts} +2 -2
- 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 +7 -0
- package/dist/idempotency/index.mjs +7 -17
- package/dist/{index-Dtrj6XZk.d.mts → index-BBsvQt7U.d.mts} +1310 -1226
- package/dist/{types-DpxoNX5v.d.mts → index-Ba10fdKp.d.mts} +196 -66
- package/dist/{index-B5N-NaHs.d.mts → index-CC5ruDGh.d.mts} +3 -3
- package/dist/{index-B5PoNbfu.d.mts → index-wMoWBLkn.d.mts} +68 -2
- package/dist/index.d.mts +6 -5
- package/dist/index.mjs +6 -5
- package/dist/{init-0YhHkXGk.mjs → init-CjO4p_q1.mjs} +123 -4
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +67 -4
- 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/webhooks.d.mts +131 -7
- package/dist/integrations/webhooks.mjs +169 -10
- package/dist/interface-n5hpKg1d.mjs +16 -0
- package/dist/jsonBody-BlzAoGCe.mjs +43 -0
- package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
- package/dist/logger/index.d.mts +26 -1
- package/dist/logger/index.mjs +39 -1
- package/dist/{mcpPlugin-B9XjjNVG.mjs → mcpPlugin-BtzIL_kM.mjs} +312 -39
- package/dist/middleware/index.d.mts +1 -1
- package/dist/{module-WeuU5jfd.d.mts → module-B4u10iOJ.d.mts} +26 -1
- package/dist/{openapi-Kyod_di6.mjs → openapi-jSDx-VDv.mjs} +3 -3
- package/dist/permissions/index.d.mts +4 -3
- package/dist/permissions/index.mjs +3 -2
- package/dist/{permissions-BJMuKMl7.mjs → permissions-tbxFHxjZ.mjs} +77 -264
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +24 -7
- package/dist/plugins/index.mjs +14 -282
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.mjs +7 -4
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +1 -1
- 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/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-gw0ciIUD.mjs → presets-BOe4Ugkl.mjs} +1 -1
- package/dist/rateLimitKey-BoWRH3e7.mjs +14 -0
- package/dist/realtime-I8OrfnMR.mjs +375 -0
- package/dist/{registerRoute-oGAl0oA8.mjs → registerRoute-CWO6mdYe.mjs} +38 -18
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/requestId-5BhTVzBv.mjs +113 -0
- package/dist/schemaConverter-DJvodiHj.mjs +171 -0
- package/dist/{schemaIR-ajetw3Fv.mjs → schemaIR-Ca7ySNPx.mjs} +2 -2
- package/dist/scim/index.mjs +6 -7
- package/dist/scope/index.mjs +2 -76
- package/dist/scope-LONQ3mfZ.mjs +37 -0
- package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
- package/dist/{sse-CfUjwjDk.mjs → sse-0u4P4-OI.mjs} +7 -30
- package/dist/sseStream-BQ7Qaynp.mjs +57 -0
- package/dist/testing/index.d.mts +83 -3
- package/dist/testing/index.mjs +70 -3
- package/dist/types/index.d.mts +2 -2
- package/dist/{types-DRj-Ut4n.d.mts → types-5OuqeTDX.d.mts} +37 -2
- package/dist/{fields-BGqrAa_u.d.mts → types-Csxga7d3.d.mts} +1 -121
- package/dist/usage/index.d.mts +142 -0
- package/dist/usage/index.mjs +0 -0
- package/dist/utils/index.d.mts +101 -19
- package/dist/utils/index.mjs +5 -3
- package/dist/{utils-CO8a3B_1.mjs → utils-CZyeRXTh.mjs} +80 -82
- package/dist/{versioning-DQ4mCvg0.d.mts → versioning-D6PvCwpq.d.mts} +43 -9
- package/llms.txt +39 -0
- package/package.json +513 -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 +734 -654
- 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 +2 -1
- package/skills/arc-module-publishing/references/authoring-modules.md +55 -0
- package/skills/arc-module-publishing/references/package-manifest.md +4 -2
- package/dist/preloadResources-CHw3_oA_.mjs +0 -45
- package/dist/schemaConverter-U0MnO9vd.mjs +0 -110
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-CjOeWJ9_.mjs";
|
|
3
3
|
import { runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import Fastify from "fastify";
|
|
5
5
|
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
@@ -462,6 +462,73 @@ async function setupBetterAuthTestApp(input) {
|
|
|
462
462
|
};
|
|
463
463
|
}
|
|
464
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
|
|
465
532
|
//#region src/testing/fixtures.ts
|
|
466
533
|
function createTestFixtures() {
|
|
467
534
|
const registry = /* @__PURE__ */ new Map();
|
|
@@ -1049,7 +1116,7 @@ function pickDefaultAuth(authMode, callerAuth) {
|
|
|
1049
1116
|
};
|
|
1050
1117
|
}
|
|
1051
1118
|
async function createTestApp(options = {}) {
|
|
1052
|
-
const { createApp } = await import("../createApp-
|
|
1119
|
+
const { createApp } = await import("../createApp-CjOeWJ9_.mjs").then((n) => n.r);
|
|
1053
1120
|
const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
|
|
1054
1121
|
let dbHandle;
|
|
1055
1122
|
let dbUri;
|
|
@@ -1119,4 +1186,4 @@ function createMinimalTestApp(options = {}) {
|
|
|
1119
1186
|
});
|
|
1120
1187
|
}
|
|
1121
1188
|
//#endregion
|
|
1122
|
-
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
|
|
1
|
+
import { $ as OpenApiSchemas, $t as UserLike, 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, Ft as ControllerLike, G as OnTenantDeleteConfig, Gt as AuthenticatorContext, H as CrudController, Ht as AuthHelpers, I as MiddlewareHandler, In as BaseControllerOptions, It as FastifyHandler, J as PresetHook, Jt as AnyRecord, K as ResolvedTenantPurge, Kt as JwtContext, 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 ObjectId, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, Ut as AuthPluginOptions, W as ResourcePermissions, Wt as Authenticator, X as CrudRouteKey, Xt as ArcRequest, Y as PresetResult, Yt as ApiResponse, Z as CrudSchemas, Zt as JWTPayload, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, en as UserOrganization, 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 TokenPair, 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-BBsvQt7U.mjs";
|
|
2
2
|
import { i as RequestScope } from "../types-DH0GupI7.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-Csxga7d3.mjs";
|
|
4
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 };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as ResourceDefinition } from "./index-
|
|
1
|
+
import { V as ResourceDefinition } from "./index-BBsvQt7U.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/integrations/mcp/types.d.ts
|
|
4
4
|
/** Behavioral hints for MCP clients (tool annotations per MCP spec) */
|
|
@@ -157,6 +157,41 @@ interface CrudDescriptionMeta {
|
|
|
157
157
|
* Arc's auto-derived filterable-fields blurb.
|
|
158
158
|
*/
|
|
159
159
|
type CrudDescriptionOverride = string | ((meta: CrudDescriptionMeta) => string);
|
|
160
|
+
/**
|
|
161
|
+
* Execution wiring for MCP tools — the app-level services HTTP requests
|
|
162
|
+
* receive via Fastify decorators (`fastify.arc.hooks`, `fastify.events`,
|
|
163
|
+
* `fastify.audit`, `fastify.log`, `fastify.idempotency`) and MCP's
|
|
164
|
+
* synthetic-context path historically dropped.
|
|
165
|
+
*
|
|
166
|
+
* When present, tool handlers thread it into the request context so the
|
|
167
|
+
* controller pipeline achieves FULL HTTP parity: resource hooks run
|
|
168
|
+
* (before/around/after), the arcCorePlugin after-hook publishes
|
|
169
|
+
* `<resource>.<op>d` events (→ realtime feeds, webhooks, event-driven
|
|
170
|
+
* cache invalidation all see MCP-originated mutations), `ctx.server.*`
|
|
171
|
+
* works inside handlers, and mutating CRUD tools accept an optional
|
|
172
|
+
* `_idempotencyKey` input for retry-safe execution.
|
|
173
|
+
*
|
|
174
|
+
* `mcpPlugin` builds this automatically from its Fastify instance (lazy
|
|
175
|
+
* getters — decorator registration order doesn't matter). Standalone
|
|
176
|
+
* `resourceToTools()` / `createMcpServer()` callers pass their own, or
|
|
177
|
+
* omit it to keep the legacy dispatch-only behavior.
|
|
178
|
+
*/
|
|
179
|
+
interface McpExecutionWiring {
|
|
180
|
+
/** Shared `HookSystem` — `fastify.arc.hooks`. */
|
|
181
|
+
readonly hooks?: unknown;
|
|
182
|
+
/** Event bus decorator — `fastify.events`. */
|
|
183
|
+
readonly events?: unknown;
|
|
184
|
+
/** Audit decorator — `fastify.audit`. */
|
|
185
|
+
readonly audit?: unknown;
|
|
186
|
+
/** Structured logger — `fastify.log`. */
|
|
187
|
+
readonly log?: unknown;
|
|
188
|
+
/**
|
|
189
|
+
* Idempotency store (the `IdempotencyStore` contract from
|
|
190
|
+
* `@classytic/arc/idempotency`). Presence advertises `_idempotencyKey`
|
|
191
|
+
* on mutating CRUD tools and enables executor-level check/lock/replay.
|
|
192
|
+
*/
|
|
193
|
+
readonly idempotencyStore?: unknown;
|
|
194
|
+
}
|
|
160
195
|
/** Per-resource MCP configuration overrides */
|
|
161
196
|
interface McpResourceConfig {
|
|
162
197
|
/** Which CRUD operations to expose (default: all enabled on the resource) */
|
|
@@ -392,4 +427,4 @@ interface CreateMcpServerConfig {
|
|
|
392
427
|
/** CRUD operation type */
|
|
393
428
|
type CrudOperation = "list" | "get" | "create" | "update" | "delete";
|
|
394
429
|
//#endregion
|
|
395
|
-
export { CrudDescriptionOverride as a, McpAuthResult as c,
|
|
430
|
+
export { ToolDefinition as _, CrudDescriptionOverride as a, McpAuthResult as c, McpResourceConfig as d, PromptDefinition as f, ToolContext as g, ToolAnnotations as h, CrudDescriptionMeta as i, McpExecutionWiring as l, SessionEntry as m, CallToolResult as n, CrudOperation as o, PromptResult as p, CreateMcpServerConfig as r, McpAuthResolver as s, BetterAuthHandler as t, McpPluginOptions as u };
|
|
@@ -195,124 +195,4 @@ interface PermissionCheckMeta {
|
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
197
|
//#endregion
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Field-Level Permissions
|
|
201
|
-
*
|
|
202
|
-
* Control field visibility and writability per role.
|
|
203
|
-
* Integrated into the response path (read) and sanitization path (write).
|
|
204
|
-
*
|
|
205
|
-
* @example
|
|
206
|
-
* ```typescript
|
|
207
|
-
* import { fields, defineResource } from '@classytic/arc';
|
|
208
|
-
*
|
|
209
|
-
* const userResource = defineResource({
|
|
210
|
-
* name: 'user',
|
|
211
|
-
* adapter: userAdapter,
|
|
212
|
-
* fields: {
|
|
213
|
-
* salary: fields.visibleTo(['admin', 'hr']),
|
|
214
|
-
* internalNotes: fields.writableBy(['admin']),
|
|
215
|
-
* email: fields.redactFor(['viewer']),
|
|
216
|
-
* password: fields.hidden(),
|
|
217
|
-
* },
|
|
218
|
-
* });
|
|
219
|
-
* ```
|
|
220
|
-
*/
|
|
221
|
-
type FieldPermissionType = "hidden" | "visibleTo" | "writableBy" | "redactFor";
|
|
222
|
-
interface FieldPermission {
|
|
223
|
-
readonly _type: FieldPermissionType;
|
|
224
|
-
readonly roles?: readonly string[];
|
|
225
|
-
readonly redactValue?: unknown;
|
|
226
|
-
}
|
|
227
|
-
type FieldPermissionMap = Record<string, FieldPermission>;
|
|
228
|
-
declare const fields: {
|
|
229
|
-
/**
|
|
230
|
-
* Field is never included in responses. Not writable via API.
|
|
231
|
-
*
|
|
232
|
-
* @example
|
|
233
|
-
* ```typescript
|
|
234
|
-
* fields: { password: fields.hidden() }
|
|
235
|
-
* ```
|
|
236
|
-
*/
|
|
237
|
-
hidden(): FieldPermission;
|
|
238
|
-
/**
|
|
239
|
-
* Field is only visible to users with specified roles.
|
|
240
|
-
* Other users don't see the field at all.
|
|
241
|
-
*
|
|
242
|
-
* @example
|
|
243
|
-
* ```typescript
|
|
244
|
-
* fields: { salary: fields.visibleTo(['admin', 'hr']) }
|
|
245
|
-
* ```
|
|
246
|
-
*/
|
|
247
|
-
visibleTo(roles: readonly string[]): FieldPermission;
|
|
248
|
-
/**
|
|
249
|
-
* Field is only writable by users with specified roles.
|
|
250
|
-
* All users can still read the field. Users without the role
|
|
251
|
-
* have the field silently stripped from write operations.
|
|
252
|
-
*
|
|
253
|
-
* @example
|
|
254
|
-
* ```typescript
|
|
255
|
-
* fields: { role: fields.writableBy(['admin']) }
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
writableBy(roles: readonly string[]): FieldPermission;
|
|
259
|
-
/**
|
|
260
|
-
* Field is redacted (replaced with a placeholder) for specified roles.
|
|
261
|
-
* Other users see the real value.
|
|
262
|
-
*
|
|
263
|
-
* @param roles - Roles that see the redacted value
|
|
264
|
-
* @param redactValue - Replacement value (default: '***')
|
|
265
|
-
*
|
|
266
|
-
* @example
|
|
267
|
-
* ```typescript
|
|
268
|
-
* fields: {
|
|
269
|
-
* email: fields.redactFor(['viewer']),
|
|
270
|
-
* ssn: fields.redactFor(['basic'], '***-**-****'),
|
|
271
|
-
* }
|
|
272
|
-
* ```
|
|
273
|
-
*/
|
|
274
|
-
redactFor(roles: readonly string[], redactValue?: unknown): FieldPermission;
|
|
275
|
-
};
|
|
276
|
-
/**
|
|
277
|
-
* Apply field-level READ permissions to a response object.
|
|
278
|
-
* Strips hidden fields, enforces visibility, and applies redaction.
|
|
279
|
-
*
|
|
280
|
-
* @param data - The response object (mutated in place for performance)
|
|
281
|
-
* @param fieldPermissions - Field permission map from resource config
|
|
282
|
-
* @param userRoles - Current user's roles (empty array for unauthenticated)
|
|
283
|
-
* @returns The filtered object
|
|
284
|
-
*/
|
|
285
|
-
declare function applyFieldReadPermissions<T extends Record<string, unknown>>(data: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): T;
|
|
286
|
-
/**
|
|
287
|
-
* Result of applying write permissions — includes both the filtered body
|
|
288
|
-
* and the list of fields that were stripped so callers can decide whether
|
|
289
|
-
* to reject the request (secure default) or silently strip (legacy).
|
|
290
|
-
*/
|
|
291
|
-
interface FieldWritePermissionResult<T extends Record<string, unknown>> {
|
|
292
|
-
readonly body: T;
|
|
293
|
-
readonly deniedFields: readonly string[];
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Apply field-level WRITE permissions to request body.
|
|
297
|
-
*
|
|
298
|
-
* Returns both the filtered body and the list of denied fields. Callers are
|
|
299
|
-
* expected to reject the request when `deniedFields.length > 0` — silently
|
|
300
|
-
* stripping fields hides misconfigurations and real attacks. See
|
|
301
|
-
* `BodySanitizer` for the default policy.
|
|
302
|
-
*
|
|
303
|
-
* @param body - The request body (returns a new filtered copy)
|
|
304
|
-
* @param fieldPermissions - Field permission map from resource config
|
|
305
|
-
* @param userRoles - Current user's roles
|
|
306
|
-
*/
|
|
307
|
-
declare function applyFieldWritePermissions<T extends Record<string, unknown>>(body: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): FieldWritePermissionResult<T>;
|
|
308
|
-
/**
|
|
309
|
-
* Resolve effective roles by merging global user roles with org-level roles.
|
|
310
|
-
*
|
|
311
|
-
* Global roles come from `req.user.role` (normalized via getUserRoles()).
|
|
312
|
-
* Org roles come from `req.context.orgRoles` (set by BA adapter's org bridge).
|
|
313
|
-
*
|
|
314
|
-
* When no org context exists, returns global roles only — backward compatible.
|
|
315
|
-
*/
|
|
316
|
-
declare function resolveEffectiveRoles(userRoles: readonly string[], orgRoles: readonly string[]): string[];
|
|
317
|
-
//#endregion
|
|
318
|
-
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 };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { i as RequestScope } from "../types-DH0GupI7.mjs";
|
|
2
|
+
import { n as PermissionContext, t as PermissionCheck } from "../types-Csxga7d3.mjs";
|
|
3
|
+
import { FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
4
|
+
//#region src/usage/requireQuota.d.ts
|
|
5
|
+
interface QuotaOptions {
|
|
6
|
+
/** Counter to enforce — `ai.tokens`, `export.runs`, `api.requests`, ... */
|
|
7
|
+
kind: string;
|
|
8
|
+
/**
|
|
9
|
+
* Ceiling for the current period. A number, `false` (unlimited — lets
|
|
10
|
+
* plan tables express "enterprise: false" uniformly), or a sync/async
|
|
11
|
+
* resolver receiving the permission context (plan-aware quotas).
|
|
12
|
+
*/
|
|
13
|
+
limit: number | false | ((ctx: PermissionContext) => number | false | Promise<number | false>);
|
|
14
|
+
/**
|
|
15
|
+
* Actor key override. Default: the meter's own chain
|
|
16
|
+
* (org → user → client → `ip:<addr>`), so quota buckets are the same
|
|
17
|
+
* buckets usage writes and rate limits read.
|
|
18
|
+
*/
|
|
19
|
+
actorOf?: (ctx: PermissionContext) => string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Counter-store failure policy. Default `'allow'` (fail-open — see
|
|
22
|
+
* module header). `'deny'` throws the same 429 shape with
|
|
23
|
+
* `code: 'quota.unavailable'` and status 503.
|
|
24
|
+
*/
|
|
25
|
+
onStoreError?: "allow" | "deny";
|
|
26
|
+
/** Memoize summary reads per (actor, kind) for this many ms. Default 0. */
|
|
27
|
+
cacheTtlMs?: number;
|
|
28
|
+
}
|
|
29
|
+
declare function requireQuota(options: QuotaOptions): PermissionCheck;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/usage/stores/interface.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* Usage store contract — period-bucketed counters per actor.
|
|
34
|
+
*
|
|
35
|
+
* CANONICAL HOME: `@classytic/repo-core/usage` (>=0.10) owns this
|
|
36
|
+
* contract for the ecosystem — kits implement it there without ever
|
|
37
|
+
* depending on arc (`@classytic/mongokit/usage`, ...). This file is a
|
|
38
|
+
* deliberate STRUCTURAL MIRROR of that shape — same trick as
|
|
39
|
+
* `ScheduleLockLike` vs repo-core's `LockAdapter` — so arc's emitted
|
|
40
|
+
* declarations carry no repo-core@>=0.10 subpath reference and the
|
|
41
|
+
* peer floor stays honest. Any conforming adapter assigns directly.
|
|
42
|
+
*
|
|
43
|
+
* The deliberately tiny surface (one increment, one read) is what makes
|
|
44
|
+
* every backend trivial: memory (dev/tests), Redis (`HINCRBY`), or any kit
|
|
45
|
+
* (Mongo `$inc` upsert) implements it in a handful of lines. Aggregation
|
|
46
|
+
* happens IN the store — arc never buffers counters in process memory
|
|
47
|
+
* beyond the built-in memory store, so multi-replica deployments stay
|
|
48
|
+
* correct as long as the store is shared.
|
|
49
|
+
*
|
|
50
|
+
* This is the HORIZONTAL usage layer (per-actor, per-period platform
|
|
51
|
+
* accounting for quotas/plans/billing). Itemized vertical records — e.g.
|
|
52
|
+
* `@classytic/arc-ai/usage`'s per-run AI token/cost tracking — keep their
|
|
53
|
+
* own richer stores and can sink into this one (`kind: 'ai.tokens'`).
|
|
54
|
+
*/
|
|
55
|
+
/** One counter cell: (actor, period, kind). */
|
|
56
|
+
interface UsageBucket {
|
|
57
|
+
/** Who consumed — org / user / client id, or an IP-derived fallback. */
|
|
58
|
+
actor: string;
|
|
59
|
+
/** Aggregation period, `YYYY-MM` (see `usagePeriod()`). */
|
|
60
|
+
period: string;
|
|
61
|
+
/** Namespaced counter, dot-separated: `api.requests`, `ai.tokens`, `storage.bytes`. */
|
|
62
|
+
kind: string;
|
|
63
|
+
}
|
|
64
|
+
interface UsageStore {
|
|
65
|
+
/** Store name for diagnostics (e.g. 'memory', 'redis', 'mongo'). */
|
|
66
|
+
readonly name: string;
|
|
67
|
+
/** Add `amount` to the bucket's counter, creating it at 0 first. MUST be atomic per bucket. */
|
|
68
|
+
increment(bucket: UsageBucket, amount: number): Promise<void> | void;
|
|
69
|
+
/** Every counter for an actor in a period: `{ 'api.requests': 40231, ... }`. */
|
|
70
|
+
summary(actor: string, period: string): Promise<Record<string, number>>;
|
|
71
|
+
/** Optional cleanup hook (connections, timers). */
|
|
72
|
+
close?(): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Canonical period key for a date — calendar month, UTC: `2026-07`.
|
|
76
|
+
* Monthly is the billing-native granularity; stores that want finer
|
|
77
|
+
* windows can shard internally without changing the contract.
|
|
78
|
+
*/
|
|
79
|
+
declare function usagePeriod(date?: Date): string;
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/usage/stores/memory.d.ts
|
|
82
|
+
declare class MemoryUsageStore implements UsageStore {
|
|
83
|
+
#private;
|
|
84
|
+
readonly name = "memory";
|
|
85
|
+
increment(bucket: UsageBucket, amount: number): void;
|
|
86
|
+
summary(actor: string, period: string): Promise<Record<string, number>>;
|
|
87
|
+
/** Test convenience — drop everything. */
|
|
88
|
+
clear(): void;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/usage/usagePlugin.d.ts
|
|
92
|
+
interface UsageTrackOptions {
|
|
93
|
+
/** Count every completed request as `api.requests` (default: true). */
|
|
94
|
+
requests?: boolean;
|
|
95
|
+
/** Sum `content-length` response bytes as `api.egress.bytes` (default: false). */
|
|
96
|
+
egress?: boolean;
|
|
97
|
+
}
|
|
98
|
+
interface UsagePluginOptions {
|
|
99
|
+
/** Master switch — `false` registers a typed no-op (config-gated environments). */
|
|
100
|
+
enabled?: boolean;
|
|
101
|
+
/** Counter backend. Default: in-memory (per-process — use a shared store in multi-replica). */
|
|
102
|
+
store?: UsageStore;
|
|
103
|
+
/** Automatic per-request accounting. Default: `{ requests: true, egress: false }`. */
|
|
104
|
+
track?: UsageTrackOptions;
|
|
105
|
+
/**
|
|
106
|
+
* Derive the actor key from a request. Default: org → user → client →
|
|
107
|
+
* `ip:<addr>` (same chain as `createTenantKeyGenerator`).
|
|
108
|
+
*/
|
|
109
|
+
actorOf?: (request: FastifyRequest) => string | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Paths exempt from automatic tracking. Exact or trailing-`*` prefix.
|
|
112
|
+
* Default: `['/_health*', '/_metrics*']`.
|
|
113
|
+
*/
|
|
114
|
+
ignorePaths?: string[];
|
|
115
|
+
}
|
|
116
|
+
/** The `fastify.usage` surface. */
|
|
117
|
+
interface UsageMeter {
|
|
118
|
+
/**
|
|
119
|
+
* Add to a counter. `actor` may be a raw key or a `RequestScope`
|
|
120
|
+
* (derived via the org → user → client chain). Fire-and-forget safe:
|
|
121
|
+
* awaiting is optional, errors never throw into request flow.
|
|
122
|
+
*/
|
|
123
|
+
record(actor: string | RequestScope | undefined, kind: string, amount?: number): Promise<void>;
|
|
124
|
+
/** All counters for an actor. Period defaults to the current month. */
|
|
125
|
+
summary(actor: string, period?: string): Promise<Record<string, number>>;
|
|
126
|
+
/** Canonical period key (`YYYY-MM`) for a date. */
|
|
127
|
+
period(date?: Date): string;
|
|
128
|
+
/** The actor key automatic tracking would use for this request. */
|
|
129
|
+
actorOf(request: FastifyRequest): string;
|
|
130
|
+
/** The active store (diagnostics). */
|
|
131
|
+
readonly store: UsageStore;
|
|
132
|
+
}
|
|
133
|
+
declare module "fastify" {
|
|
134
|
+
interface FastifyInstance {
|
|
135
|
+
/** Per-actor usage counters — registered by `usagePlugin`. */
|
|
136
|
+
usage?: UsageMeter;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
declare const usagePluginFn: FastifyPluginAsync<UsagePluginOptions>;
|
|
140
|
+
declare const _default: FastifyPluginAsync<UsagePluginOptions>;
|
|
141
|
+
//#endregion
|
|
142
|
+
export { MemoryUsageStore, type QuotaOptions, type UsageBucket, type UsageMeter, type UsagePluginOptions, type UsageStore, type UsageTrackOptions, _default as default, requireQuota, usagePeriod, usagePluginFn as usagePlugin };
|
|
Binary file
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ErrorMapper } from "../errorHandler-
|
|
1
|
+
import { $ as OpenApiSchemas, $t as UserLike, Jt as AnyRecord, S as QueryParserInterface, U as ResourceConfig, b as ParsedQuery } from "../index-BBsvQt7U.mjs";
|
|
2
|
+
import { n as ErrorMapper } from "../errorHandler-C6yv4ZTW.mjs";
|
|
3
3
|
import { a as NotFoundError, c as RateLimitError, d as ValidationError, f as createDomainError, i as ForbiddenError, l as ServiceUnavailableError, m as isArcError, n as ConflictError, o as OrgAccessDeniedError, p as createError, r as ErrorOptions, s as OrgRequiredError, t as ArcError, u as UnauthorizedError } from "../errors-DHcHVAgk.mjs";
|
|
4
4
|
import { errorContractSchema, errorDetailSchema } from "@classytic/repo-core/errors";
|
|
5
5
|
import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
|
|
@@ -437,6 +437,34 @@ declare const NEUTRAL_DIALECT: FilterDialect;
|
|
|
437
437
|
*/
|
|
438
438
|
declare function handleRaw<T>(handler: (request: FastifyRequest, reply: FastifyReply) => Promise<T>, statusCode?: number): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
439
439
|
//#endregion
|
|
440
|
+
//#region src/utils/headers.d.ts
|
|
441
|
+
/**
|
|
442
|
+
* Canonical header typing + accessor.
|
|
443
|
+
*
|
|
444
|
+
* Node/Fastify header values are `string | string[] | undefined` — repeated
|
|
445
|
+
* headers arrive as arrays. `IRequestContext.headers` is DECLARED as
|
|
446
|
+
* `Record<string, string | undefined>` for ergonomic host code, but the HTTP
|
|
447
|
+
* adapter passes Fastify's headers object through unchanged, so `string[]`
|
|
448
|
+
* CAN appear at runtime behind the narrower type (the declared type is
|
|
449
|
+
* scheduled to widen to {@link ArcHeaders} in v3). Until then, read headers
|
|
450
|
+
* through {@link getHeader} instead of indexing — it's honest about arrays
|
|
451
|
+
* and deterministic about duplicates.
|
|
452
|
+
*/
|
|
453
|
+
/** What a headers bag actually contains at runtime — HTTP or synthetic MCP. */
|
|
454
|
+
type ArcHeaders = Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
455
|
+
/**
|
|
456
|
+
* Read a single header value. Lowercases the name (Node normalizes incoming
|
|
457
|
+
* header names to lowercase) and returns the FIRST value when the header
|
|
458
|
+
* was repeated.
|
|
459
|
+
*
|
|
460
|
+
* Duplicate policy: first-value is deterministic, but for security-sensitive
|
|
461
|
+
* headers (authorization material, forwarded-for chains, content-length) a
|
|
462
|
+
* duplicate usually signals smuggling or a broken proxy — consumers that
|
|
463
|
+
* care should check `Array.isArray(headers[name])` and REJECT instead of
|
|
464
|
+
* picking a winner.
|
|
465
|
+
*/
|
|
466
|
+
declare function getHeader(headers: ArcHeaders | Record<string, string | undefined> | undefined, name: string): string | undefined;
|
|
467
|
+
//#endregion
|
|
440
468
|
//#region src/utils/queryParser.d.ts
|
|
441
469
|
interface ArcQueryParserOptions {
|
|
442
470
|
/** Maximum allowed limit value (default: 1000) */
|
|
@@ -599,9 +627,8 @@ declare function createQueryParser(options?: ArcQueryParserOptions): ArcQueryPar
|
|
|
599
627
|
interface JsonSchema {
|
|
600
628
|
/**
|
|
601
629
|
* Optional because JSON Schema allows top-level combinator-only schemas
|
|
602
|
-
* (`{ oneOf: [...] }`, `{ anyOf: [...] }`, `{ allOf: [...] }`) —
|
|
603
|
-
*
|
|
604
|
-
* shapes with no top-level `type`.
|
|
630
|
+
* (`{ oneOf: [...] }`, `{ anyOf: [...] }`, `{ allOf: [...] }`) — hosts
|
|
631
|
+
* may pass such schemas anywhere a `JsonSchema` is accepted.
|
|
605
632
|
*/
|
|
606
633
|
type?: string | string[];
|
|
607
634
|
properties?: Record<string, JsonSchema | AnyRecord>;
|
|
@@ -623,18 +650,25 @@ interface JsonSchema {
|
|
|
623
650
|
*/
|
|
624
651
|
declare const paginationSchema: JsonSchema;
|
|
625
652
|
/**
|
|
626
|
-
* List response schema —
|
|
627
|
-
*
|
|
628
|
-
*
|
|
653
|
+
* List response schema — accepts every wire shape `toCanonicalList` can
|
|
654
|
+
* emit. Hosts who know their endpoint only ever produces one variant can
|
|
655
|
+
* pin to a narrower helper (sharper OpenAPI docs, same serializer speed):
|
|
629
656
|
* - `offsetListResponse(item)` — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`
|
|
630
657
|
* - `keysetListResponse(item)` — `{ method: 'keyset', data, limit, hasMore, next: string|null }`
|
|
631
658
|
* - `aggregateListResponse(item)` — `{ method: 'aggregate', ...offset fields }`
|
|
632
659
|
* - `bareListResponse(item)` — `{ data }`
|
|
633
660
|
*
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
*
|
|
661
|
+
* SHAPE: one merged permissive object, NOT a `oneOf` union (2.22). The
|
|
662
|
+
* pre-2.22 union was semantically identical on the wire — every variant is
|
|
663
|
+
* `additionalProperties: true` and the property definitions never conflict
|
|
664
|
+
* (`next` only exists on keyset, the offset counters only on offset/
|
|
665
|
+
* aggregate; fast-json-stringify skips undefined optionals) — but
|
|
666
|
+
* fast-json-stringify implements top-level `oneOf` by running full AJV
|
|
667
|
+
* validation PER RESPONSE to pick the branch, on the hottest route of
|
|
668
|
+
* every arc app. The merge keeps the exact bytes (pinned by
|
|
669
|
+
* `tests/utils/list-response-serialization.test.ts`) and drops the
|
|
670
|
+
* per-response branch validation. Only `data` is required — the one field
|
|
671
|
+
* all four variants share.
|
|
638
672
|
*/
|
|
639
673
|
declare function listResponse(itemSchema: JsonSchema): JsonSchema;
|
|
640
674
|
/** Offset variant — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`. */
|
|
@@ -663,9 +697,8 @@ declare const responses: {
|
|
|
663
697
|
schema: {
|
|
664
698
|
/**
|
|
665
699
|
* Optional because JSON Schema allows top-level combinator-only schemas
|
|
666
|
-
* (`{ oneOf: [...] }`, `{ anyOf: [...] }`, `{ allOf: [...] }`) —
|
|
667
|
-
*
|
|
668
|
-
* shapes with no top-level `type`.
|
|
700
|
+
* (`{ oneOf: [...] }`, `{ anyOf: [...] }`, `{ allOf: [...] }`) — hosts
|
|
701
|
+
* may pass such schemas anywhere a `JsonSchema` is accepted.
|
|
669
702
|
*/
|
|
670
703
|
type?: string | string[];
|
|
671
704
|
properties?: Record<string, JsonSchema | AnyRecord>;
|
|
@@ -1103,6 +1136,20 @@ declare function isJsonSchema(input: unknown): input is Record<string, unknown>;
|
|
|
1103
1136
|
* Check if an object is a Zod schema (has `_zod` marker from Zod v4).
|
|
1104
1137
|
*/
|
|
1105
1138
|
declare function isZodSchema(input: unknown): boolean;
|
|
1139
|
+
/**
|
|
1140
|
+
* How `toJsonSchema` reacts when a Zod schema CANNOT be converted (zod not
|
|
1141
|
+
* installed/loaded, zod v3, or `z.toJSONSchema()` threw):
|
|
1142
|
+
*
|
|
1143
|
+
* - `'throw'` — fail fast with an actionable error. Used by
|
|
1144
|
+
* `convertRouteSchema` (VALIDATION path): a schema that silently degrades
|
|
1145
|
+
* to `{ type: 'object' }` is validation switched off — the worst outcome
|
|
1146
|
+
* for a wire contract, worse than a boot failure.
|
|
1147
|
+
* - `'warn'` (default) — log loudly, degrade to a permissive `{ type:
|
|
1148
|
+
* 'object' }`. Used by doc-generation paths (OpenAPI extraction of
|
|
1149
|
+
* Better Auth endpoints etc.), where the schema is third-party and a doc
|
|
1150
|
+
* gap must not take the app down.
|
|
1151
|
+
*/
|
|
1152
|
+
type UnconvertibleMode = "warn" | "throw";
|
|
1106
1153
|
/**
|
|
1107
1154
|
* Convert any schema input to JSON Schema.
|
|
1108
1155
|
*
|
|
@@ -1110,13 +1157,16 @@ declare function isZodSchema(input: unknown): boolean;
|
|
|
1110
1157
|
* 1. `null`/`undefined` → `undefined`
|
|
1111
1158
|
* 2. Already JSON Schema → pass through as-is (zero overhead)
|
|
1112
1159
|
* 3. Zod v4 schema → `z.toJSONSchema(schema, { target })`
|
|
1113
|
-
* 4.
|
|
1160
|
+
* 4. Zod v3 schema → unconvertible (actionable "upgrade to v4" error/warn)
|
|
1161
|
+
* 5. Unrecognized object → return as-is (treat as opaque schema)
|
|
1114
1162
|
*
|
|
1115
|
-
* @param input
|
|
1163
|
+
* @param input Schema (Zod, plain JSON Schema, or opaque object)
|
|
1116
1164
|
* @param target Output target — defaults to `draft-7` for Fastify compatibility.
|
|
1117
1165
|
* Pass `openapi-3.0`/`openapi-3.1` for OpenAPI document generation.
|
|
1166
|
+
* @param mode Failure policy for unconvertible Zod schemas — see
|
|
1167
|
+
* {@link UnconvertibleMode}. Defaults to `'warn'`.
|
|
1118
1168
|
*/
|
|
1119
|
-
declare function toJsonSchema(input: unknown, target?: JsonSchemaTarget): Record<string, unknown> | undefined;
|
|
1169
|
+
declare function toJsonSchema(input: unknown, target?: JsonSchemaTarget, mode?: UnconvertibleMode): Record<string, unknown> | undefined;
|
|
1120
1170
|
/**
|
|
1121
1171
|
* Convert all schema fields in an OpenApiSchemas object.
|
|
1122
1172
|
* JSON Schema values pass through unchanged. Only Zod schemas are converted.
|
|
@@ -1193,6 +1243,38 @@ declare function convertRouteSchema(schema: Record<string, unknown>, target?: Js
|
|
|
1193
1243
|
*/
|
|
1194
1244
|
declare function simpleEqualityMatcher(item: unknown, filters: Record<string, unknown>): boolean;
|
|
1195
1245
|
//#endregion
|
|
1246
|
+
//#region src/utils/sseStream.d.ts
|
|
1247
|
+
interface SseStreamOptions {
|
|
1248
|
+
/** Heartbeat comment interval in ms (default: 30000). */
|
|
1249
|
+
heartbeatMs?: number;
|
|
1250
|
+
/**
|
|
1251
|
+
* Reconnection-delay hint written once at connect as the standard SSE
|
|
1252
|
+
* `retry:` field (WHATWG EventSource honors it natively). Omit to send
|
|
1253
|
+
* no hint (browser default ~3s).
|
|
1254
|
+
*/
|
|
1255
|
+
retryMs?: number;
|
|
1256
|
+
}
|
|
1257
|
+
interface SseStream {
|
|
1258
|
+
/**
|
|
1259
|
+
* Write one SSE frame. `id` (optional) becomes the standard SSE `id:`
|
|
1260
|
+
* field — clients receive it as `lastEventId` (WHATWG), enabling
|
|
1261
|
+
* client-side dedup/resume bookkeeping even when the server keeps no
|
|
1262
|
+
* replay log. Returns `false` when the connection was destroyed
|
|
1263
|
+
* (backpressure) — the stream is already cleaned up; stop writing.
|
|
1264
|
+
*/
|
|
1265
|
+
write(eventName: string, data: string, id?: string): boolean;
|
|
1266
|
+
/** Idempotent teardown: heartbeat cleared, cleanups run, socket ended. */
|
|
1267
|
+
close(): void;
|
|
1268
|
+
/** Register teardown work (unsubscribes etc.) — runs exactly once. */
|
|
1269
|
+
onCleanup(fn: () => void): void;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Take over the reply socket and open an SSE stream. Call ONLY after every
|
|
1273
|
+
* gate (auth, permissions, validation) has passed — after `hijack()` no
|
|
1274
|
+
* JSON error can be sent.
|
|
1275
|
+
*/
|
|
1276
|
+
declare function openSseStream(request: FastifyRequest, reply: FastifyReply, options?: SseStreamOptions): SseStream;
|
|
1277
|
+
//#endregion
|
|
1196
1278
|
//#region src/utils/stateMachine.d.ts
|
|
1197
1279
|
/**
|
|
1198
1280
|
* State Machine Utility
|
|
@@ -1453,4 +1535,4 @@ declare function hasEvents(instance: FastifyInstance): instance is FastifyInstan
|
|
|
1453
1535
|
*/
|
|
1454
1536
|
declare function getUserId(user: UserLike | null | undefined): string | undefined;
|
|
1455
1537
|
//#endregion
|
|
1456
|
-
export { ArcError, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, type ConfigError, ConflictError, type ErrorOptions, type EventsDecorator, type FilterDialect, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, type JsonSchemaTarget, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, type PipeUIMessageStreamOptions, RateLimitError, ServiceUnavailableError, type StateMachine, type TransitionConfig, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, type ValidateOptions, ValidationError, type ValidationResult, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|
|
1538
|
+
export { ArcError, type ArcHeaders, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, type ConfigError, ConflictError, type ErrorOptions, type EventsDecorator, type FilterDialect, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, type JsonSchemaTarget, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, type PipeUIMessageStreamOptions, RateLimitError, ServiceUnavailableError, type SseStream, type SseStreamOptions, type StateMachine, type TransitionConfig, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, type ValidateOptions, ValidationError, type ValidationResult, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getHeader, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, openSseStream, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { a as OrgAccessDeniedError, c as ServiceUnavailableError, d as createDomainError, f as createError, i as NotFoundError, l as UnauthorizedError, n as ConflictError, o as OrgRequiredError, p as isArcError, r as ForbiddenError, s as RateLimitError, t as ArcError, u as ValidationError } from "../errors-DkO8Nk6-.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
2
|
+
import { t as simpleEqualityMatcher } from "../simpleEqualityMatcher-Cg05HPsn.mjs";
|
|
3
|
+
import { A as CircuitState, C as defineGuard, D as CircuitBreaker, E as withCompensation, F as validateResourceConfig, M as createCircuitBreakerRegistry, N as assertValidConfig, O as CircuitBreakerError, P as formatValidationErrors, S as NEUTRAL_DIALECT, T as defineCompensation, _ as ArcQueryParser, a as bareListResponse, b as handleRaw, c as errorDetailSchema, d as keysetListResponse, f as listResponse, g as responses, h as queryParams, i as aggregateListResponse, j as createCircuitBreaker, k as CircuitBreakerRegistry, l as getDefaultCrudSchemas, m as paginationSchema, n as createStateMachine, o as deleteResponse, p as offsetListResponse, r as scheduleBackground, s as errorContractSchema, t as getUserId, u as getListQueryParams, v as createQueryParser, w as defineErrorMapper, x as MONGO_DIALECT, y as getHeader } from "../utils-CZyeRXTh.mjs";
|
|
4
|
+
import { a as isZodSchema, i as isJsonSchema, n as convertRouteSchema, o as toJsonSchema, t as convertOpenApiSchemas } from "../schemaConverter-DJvodiHj.mjs";
|
|
4
5
|
import { a as promoteStreamTokenToHeader, i as pipeUIMessageStreamToReply, n as forwardedStreamHeaders, r as isReadableStream, t as UI_MESSAGE_STREAM_HEADERS } from "../streaming-BQcW6_Kq.mjs";
|
|
6
|
+
import { t as openSseStream } from "../sseStream-BQ7Qaynp.mjs";
|
|
5
7
|
import { t as hasEvents } from "../typeGuards-C9KXUly6.mjs";
|
|
6
|
-
export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, ValidationError, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|
|
8
|
+
export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, ValidationError, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getHeader, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, openSseStream, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|