@classytic/arc 2.8.4 → 2.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/audit/index.d.mts +1 -1
- package/dist/audit/index.mjs +1 -1
- package/dist/audit/mongodb.d.mts +1 -1
- package/dist/audit/mongodb.mjs +1 -1
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +2 -2
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-C5lDyRH2.mjs → betterAuthOpenApi-BuUcUEJq.mjs} +1 -1
- package/dist/cache/index.d.mts +73 -3
- package/dist/cache/index.mjs +95 -2
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-DKSwNSXf.mjs → core-F0QoWBt2.mjs} +1 -1
- package/dist/{createActionRouter-Df1BuawX.mjs → createActionRouter-BORM8f17.mjs} +1 -1
- package/dist/{createApp-BOYjBgdI.mjs → createApp-B1EY8zxa.mjs} +11 -11
- package/dist/{defineResource-Bb_Bdhtw.mjs → defineResource-tcgySDo1.mjs} +2 -2
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/dynamic/index.d.mts +2 -2
- package/dist/dynamic/index.mjs +1 -1
- package/dist/{elevation-BBGFjzIP.mjs → elevation-DtFxrG0s.mjs} +1 -1
- package/dist/{errorHandler-CdZDavNH.d.mts → errorHandler-Bah5JhBd.d.mts} +1 -1
- package/dist/{eventPlugin-CVxlE6De.d.mts → eventPlugin-D9DKB2zM.d.mts} +1 -1
- package/dist/events/index.d.mts +3 -3
- package/dist/events/index.mjs +1 -1
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +2 -2
- package/dist/filesUpload-C7r7HIeA.mjs +319 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/mongodb.d.mts +1 -1
- package/dist/idempotency/redis.d.mts +2 -2
- package/dist/idempotency/redis.mjs +134 -13
- package/dist/{index-CSkeivBx.d.mts → index-BLXBmWud.d.mts} +3 -3
- package/dist/{index-BgmMdpm8.d.mts → index-C1meYuDn.d.mts} +1 -1
- package/dist/{index-CpTSDqmD.d.mts → index-DtDzOBn8.d.mts} +3 -3
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +4 -4
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/jobs.d.mts +25 -3
- package/dist/integrations/jobs.mjs +63 -4
- package/dist/integrations/mcp/index.d.mts +2 -2
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/{interface-BVuMfeVv.d.mts → interface-CMRutPfe.d.mts} +38 -16
- package/dist/{mongodb-B8U2xaLj.d.mts → mongodb-BsP-WbhN.d.mts} +1 -1
- package/dist/{mongodb-X7LbEjTN.d.mts → mongodb-CTcp0hQZ.d.mts} +1 -1
- package/dist/{openapi-CYCuekCn.mjs → openapi-CbKUJY_m.mjs} +3 -3
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +3 -3
- package/dist/plugins/index.d.mts +4 -4
- package/dist/plugins/index.mjs +8 -8
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/policies/index.d.mts +1 -1
- package/dist/presets/filesUpload.d.mts +49 -0
- package/dist/presets/filesUpload.mjs +2 -0
- package/dist/presets/index.d.mts +3 -2
- package/dist/presets/index.mjs +2 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/{queryCachePlugin-CnTZZTC5.d.mts → queryCachePlugin-BJJGBTlu.d.mts} +1 -1
- package/dist/redis-BM00zaPB.d.mts +115 -0
- package/dist/{redis-stream-D54N5oXs.d.mts → redis-stream-CrsfUmPt.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{resourceToTools-O_HwWXFa.mjs → resourceToTools-8s-EsCCe.mjs} +1 -1
- package/dist/rpc/index.d.mts +1 -1
- package/dist/{schemaConverter-OxfCshus.mjs → schemaConverter-Y7nCYaLJ.mjs} +24 -8
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +1 -1
- package/dist/{sse-CJpt7LGI.mjs → sse-Ad7ypl9e.mjs} +1 -1
- package/dist/storage-Dfzt4VTl.d.mts +146 -0
- package/dist/testing/index.d.mts +4 -3
- package/dist/testing/index.mjs +3 -2
- package/dist/testing/storageContract.d.mts +26 -0
- package/dist/testing/storageContract.mjs +216 -0
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +2 -0
- package/dist/types/storage.mjs +1 -0
- package/dist/{types-CcG4avic.d.mts → types-BsbNMEDR.d.mts} +1 -1
- package/dist/{types-Bg2X42_m.d.mts → types-Ch9pTQbf.d.mts} +9 -9
- package/dist/{types-CVC4HOKi.d.mts → types-DZi1aYhm.d.mts} +1 -1
- package/dist/utils/index.d.mts +26 -8
- package/dist/utils/index.mjs +1 -1
- package/package.json +16 -1
- package/skills/arc/references/events.md +29 -0
- package/dist/redis-z3sFr1UP.d.mts +0 -49
- /package/dist/{EventTransport-CinyO7zQ.d.mts → EventTransport-BXja8NOc.d.mts} +0 -0
- /package/dist/{HookSystem-BjFu7zf1.mjs → HookSystem-HprTmvVY.mjs} +0 -0
- /package/dist/{ResourceRegistry-Dq3_zBQP.mjs → ResourceRegistry-C6uXlWe3.mjs} +0 -0
- /package/dist/{caching-CjybdRwx.mjs → caching-IMuYVjTL.mjs} +0 -0
- /package/dist/{circuitBreaker-CvXkjfrW.d.mts → circuitBreaker-dTtG-UyS.d.mts} +0 -0
- /package/dist/{elevation-s5ykdNHr.d.mts → elevation-B6S5csVA.d.mts} +0 -0
- /package/dist/{errorHandler-mzqk4cGl.mjs → errorHandler-f869_8PQ.mjs} +0 -0
- /package/dist/{errors-Bmn3eZT6.d.mts → errors-Ck2h67pm.d.mts} +0 -0
- /package/dist/{eventPlugin-D91S2YF4.mjs → eventPlugin-CDjVTM82.mjs} +0 -0
- /package/dist/{externalPaths-Bapitwvd.d.mts → externalPaths-BnkYrNzp.d.mts} +0 -0
- /package/dist/{fields-DC4So2M2.d.mts → fields-DpZQa_Q3.d.mts} +0 -0
- /package/dist/{interface-DplgQO2e.d.mts → interface-4y979v99.d.mts} +0 -0
- /package/dist/{interface-B-pe8fhj.d.mts → interface-DfLGcus7.d.mts} +0 -0
- /package/dist/{loadResources-Bksk8ydA.mjs → loadResources-PWd0OCpV.mjs} +0 -0
- /package/dist/{logger-CDjpjySd.mjs → logger-D1YrIImS.mjs} +0 -0
- /package/dist/{metrics-TuOmguhi.mjs → metrics-B-PU4-Yu.mjs} +0 -0
- /package/dist/{mongodb-B5O6xaW1.mjs → mongodb-Utc5k_-0.mjs} +0 -0
- /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-CWP6MB39.mjs} +0 -0
- /package/dist/{queryCachePlugin-D0iIVhW_.mjs → queryCachePlugin-BH-fidlv.mjs} +0 -0
- /package/dist/{registry-B0Wl7uVV.mjs → registry-BiTKT1Dg.mjs} +0 -0
- /package/dist/{replyHelpers-BLojtuvR.mjs → replyHelpers-CxkYGT81.mjs} +0 -0
- /package/dist/{sessionManager-D-oNWHz3.d.mts → sessionManager-DDCmiNIo.d.mts} +0 -0
- /package/dist/{tracing-DxjKk7eW.d.mts → tracing-DdN2-wHJ.d.mts} +0 -0
- /package/dist/{types-C72d3NDn.d.mts → types-BD85MlEK.d.mts} +0 -0
- /package/dist/{versioning-Cm8qoFDg.mjs → versioning-CDugduqI.mjs} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as EventTransport, n as EventHandler, r as EventLogger, t as DomainEvent } from "./EventTransport-
|
|
1
|
+
import { i as EventTransport, n as EventHandler, r as EventLogger, t as DomainEvent } from "./EventTransport-BXja8NOc.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/events/transports/redis-stream.d.ts
|
|
4
4
|
interface RedisStreamLike {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Gt as RegisterOptions, H as IntrospectionPluginOptions, Kt as ResourceRegistry } from "../interface-
|
|
1
|
+
import { Gt as RegisterOptions, H as IntrospectionPluginOptions, Kt as ResourceRegistry } from "../interface-CMRutPfe.mjs";
|
|
2
2
|
import { FastifyPluginAsync } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/registry/introspectionPlugin.d.ts
|
package/dist/registry/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as introspectionPlugin_default, t as introspectionPlugin } from "../registry-
|
|
2
|
-
import { t as ResourceRegistry } from "../ResourceRegistry-
|
|
1
|
+
import { n as introspectionPlugin_default, t as introspectionPlugin } from "../registry-BiTKT1Dg.mjs";
|
|
2
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-C6uXlWe3.mjs";
|
|
3
3
|
export { ResourceRegistry, introspectionPlugin_default as introspectionPlugin, introspectionPlugin as introspectionPluginFn };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as BaseController } from "./BaseController-DAGGc5Xn.mjs";
|
|
2
2
|
import { n as normalizePermissionResult } from "./applyPermissionResult-D6GPMsvh.mjs";
|
|
3
|
-
import { t as pluralize } from "./pluralize-
|
|
3
|
+
import { t as pluralize } from "./pluralize-CWP6MB39.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
//#region src/integrations/mcp/createMcpServer.ts
|
|
6
6
|
/**
|
package/dist/rpc/index.d.mts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
//#region src/utils/schemaConverter.ts
|
|
2
|
+
/** Default target for Fastify-consumed schemas (matches Fastify v5's default AJV draft). */
|
|
3
|
+
const DEFAULT_FASTIFY_TARGET = "draft-7";
|
|
4
|
+
/** Default target for OpenAPI document generation (matches arc's emitted OpenAPI version). */
|
|
5
|
+
const DEFAULT_OPENAPI_TARGET = "openapi-3.0";
|
|
2
6
|
let _toJSONSchema = null;
|
|
3
7
|
import("zod").then(({ z }) => {
|
|
4
8
|
if (typeof z?.toJSONSchema === "function") _toJSONSchema = (schema, opts) => z.toJSONSchema(schema, opts);
|
|
@@ -26,17 +30,23 @@ function isZodSchema(input) {
|
|
|
26
30
|
* Detection order:
|
|
27
31
|
* 1. `null`/`undefined` → `undefined`
|
|
28
32
|
* 2. Already JSON Schema → pass through as-is (zero overhead)
|
|
29
|
-
* 3. Zod v4 schema → `z.toJSONSchema(schema, { target
|
|
33
|
+
* 3. Zod v4 schema → `z.toJSONSchema(schema, { target })`
|
|
30
34
|
* 4. Unrecognized object → return as-is (treat as opaque schema)
|
|
35
|
+
*
|
|
36
|
+
* @param input Schema (Zod, plain JSON Schema, or opaque object)
|
|
37
|
+
* @param target Output target — defaults to `draft-7` for Fastify compatibility.
|
|
38
|
+
* Pass `openapi-3.0`/`openapi-3.1` for OpenAPI document generation.
|
|
31
39
|
*/
|
|
32
|
-
function toJsonSchema(input) {
|
|
40
|
+
function toJsonSchema(input, target = DEFAULT_FASTIFY_TARGET) {
|
|
33
41
|
if (input == null) return void 0;
|
|
34
42
|
if (typeof input !== "object") return void 0;
|
|
35
43
|
if (isJsonSchema(input)) return input;
|
|
36
44
|
if (isZodSchema(input)) {
|
|
37
45
|
if (!_toJSONSchema) return input;
|
|
38
46
|
try {
|
|
39
|
-
|
|
47
|
+
const converted = _toJSONSchema(input, { target });
|
|
48
|
+
if ("$schema" in converted) delete converted.$schema;
|
|
49
|
+
return converted;
|
|
40
50
|
} catch {
|
|
41
51
|
return { type: "object" };
|
|
42
52
|
}
|
|
@@ -46,8 +56,11 @@ function toJsonSchema(input) {
|
|
|
46
56
|
/**
|
|
47
57
|
* Convert all schema fields in an OpenApiSchemas object.
|
|
48
58
|
* JSON Schema values pass through unchanged. Only Zod schemas are converted.
|
|
59
|
+
*
|
|
60
|
+
* Defaults to the `openapi-3.0` target since this function feeds OpenAPI doc
|
|
61
|
+
* generation, not Fastify route validation.
|
|
49
62
|
*/
|
|
50
|
-
function convertOpenApiSchemas(schemas) {
|
|
63
|
+
function convertOpenApiSchemas(schemas, target = DEFAULT_OPENAPI_TARGET) {
|
|
51
64
|
const result = {};
|
|
52
65
|
const schemaFields = [
|
|
53
66
|
"entity",
|
|
@@ -59,7 +72,7 @@ function convertOpenApiSchemas(schemas) {
|
|
|
59
72
|
];
|
|
60
73
|
for (const field of schemaFields) {
|
|
61
74
|
const value = schemas[field];
|
|
62
|
-
if (value !== void 0) result[field] = toJsonSchema(value) ?? value;
|
|
75
|
+
if (value !== void 0) result[field] = toJsonSchema(value, target) ?? value;
|
|
63
76
|
}
|
|
64
77
|
for (const [key, value] of Object.entries(schemas)) if (!schemaFields.includes(key)) result[key] = value;
|
|
65
78
|
return result;
|
|
@@ -73,19 +86,22 @@ function convertOpenApiSchemas(schemas) {
|
|
|
73
86
|
* JSON Schema values pass through unchanged. Only Zod schemas are converted.
|
|
74
87
|
*
|
|
75
88
|
* Used for both additionalRoutes and customSchemas (CRUD overrides).
|
|
89
|
+
*
|
|
90
|
+
* Defaults to `draft-7` so Fastify v5's bundled AJV 8 accepts the output.
|
|
91
|
+
* Pass `openapi-3.0` (or `openapi-3.1`) when generating OpenAPI documents.
|
|
76
92
|
*/
|
|
77
|
-
function convertRouteSchema(schema) {
|
|
93
|
+
function convertRouteSchema(schema, target = DEFAULT_FASTIFY_TARGET) {
|
|
78
94
|
const result = { ...schema };
|
|
79
95
|
for (const field of [
|
|
80
96
|
"body",
|
|
81
97
|
"querystring",
|
|
82
98
|
"params",
|
|
83
99
|
"headers"
|
|
84
|
-
]) if (result[field] !== void 0) result[field] = toJsonSchema(result[field]) ?? result[field];
|
|
100
|
+
]) if (result[field] !== void 0) result[field] = toJsonSchema(result[field], target) ?? result[field];
|
|
85
101
|
if (result.response !== void 0 && typeof result.response === "object" && result.response !== null) {
|
|
86
102
|
const responseObj = result.response;
|
|
87
103
|
const convertedResponse = {};
|
|
88
|
-
for (const [statusCode, responseSchema] of Object.entries(responseObj)) convertedResponse[statusCode] = toJsonSchema(responseSchema) ?? responseSchema;
|
|
104
|
+
for (const [statusCode, responseSchema] of Object.entries(responseObj)) convertedResponse[statusCode] = toJsonSchema(responseSchema, target) ?? responseSchema;
|
|
89
105
|
result.response = convertedResponse;
|
|
90
106
|
}
|
|
91
107
|
return result;
|
package/dist/scope/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as isAuthenticated, a as getClientId, b as isOrgInScope, c as getOrgRoles, d as getScopeContextMap, f as getServiceScopes, g as hasOrgAccess, h as getUserRoles, i as getAncestorOrgIds, l as getRequestScope, m as getUserId, n as PUBLIC_SCOPE, o as getOrgContext, p as getTeamId, r as RequestScope, s as getOrgId, t as AUTHENTICATED_SCOPE, u as getScopeContext, v as isElevated, x as isService, y as isMember } from "../types-
|
|
2
|
-
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { _ as isAuthenticated, a as getClientId, b as isOrgInScope, c as getOrgRoles, d as getScopeContextMap, f as getServiceScopes, g as hasOrgAccess, h as getUserRoles, i as getAncestorOrgIds, l as getRequestScope, m as getUserId, n as PUBLIC_SCOPE, o as getOrgContext, p as getTeamId, r as RequestScope, s as getOrgId, t as AUTHENTICATED_SCOPE, u as getScopeContext, v as isElevated, x as isService, y as isMember } from "../types-BD85MlEK.mjs";
|
|
2
|
+
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-B6S5csVA.mjs";
|
|
3
3
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/scope/rateLimitKey.d.ts
|
package/dist/scope/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as isElevated, a as getOrgContext, b as isService, c as getRequestScope, d as getServiceScopes, f as getTeamId, g as isAuthenticated, h as hasOrgAccess, i as getClientId, l as getScopeContext, m as getUserRoles, n as PUBLIC_SCOPE, o as getOrgId, p as getUserId, r as getAncestorOrgIds, s as getOrgRoles, t as AUTHENTICATED_SCOPE, u as getScopeContextMap, v as isMember, y as isOrgInScope } from "../types-AOD8fxIw.mjs";
|
|
2
2
|
import { n as normalizeRoles } from "../types-ZUu_h0jp.mjs";
|
|
3
|
-
import { n as elevation_default, t as elevationPlugin } from "../elevation-
|
|
3
|
+
import { n as elevation_default, t as elevationPlugin } from "../elevation-DtFxrG0s.mjs";
|
|
4
4
|
//#region src/scope/rateLimitKey.ts
|
|
5
5
|
function createTenantKeyGenerator(opts) {
|
|
6
6
|
if (opts?.strategy) return opts.strategy;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import { n as PUBLIC_SCOPE, o as getOrgId } from "./types-AOD8fxIw.mjs";
|
|
3
|
-
import { t as arcLog } from "./logger-
|
|
3
|
+
import { t as arcLog } from "./logger-D1YrIImS.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/plugins/sse.ts
|
|
6
6
|
var sse_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//#region src/types/storage.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Arc's minimal backend-agnostic storage contract.
|
|
4
|
+
*
|
|
5
|
+
* Implementations live OUTSIDE arc core. This interface is deliberately
|
|
6
|
+
* small — no variants, no hashing, no CDN transforms, no multi-tenancy
|
|
7
|
+
* policy. Those are adapter concerns.
|
|
8
|
+
*
|
|
9
|
+
* Adapter authors should verify their implementation with
|
|
10
|
+
* `runStorageContract()` from `@classytic/arc/testing/storage` —
|
|
11
|
+
* passing the contract guarantees compatibility with every arc preset
|
|
12
|
+
* that consumes `Storage`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import type { Storage } from '@classytic/arc/types/storage';
|
|
17
|
+
*
|
|
18
|
+
* export function memoryStorage(): Storage {
|
|
19
|
+
* const rows = new Map<string, { buffer: Buffer; contentType: string }>();
|
|
20
|
+
* return {
|
|
21
|
+
* async upload(input) {
|
|
22
|
+
* const id = crypto.randomUUID();
|
|
23
|
+
* rows.set(id, { buffer: input.buffer, contentType: input.mimeType });
|
|
24
|
+
* return { id, url: `memory://${id}`, pathname: id, contentType: input.mimeType, bytes: input.size };
|
|
25
|
+
* },
|
|
26
|
+
* async read(id) {
|
|
27
|
+
* const row = rows.get(id);
|
|
28
|
+
* if (!row) throw new Error('Not found');
|
|
29
|
+
* return { kind: 'buffer', buffer: row.buffer, contentType: row.contentType };
|
|
30
|
+
* },
|
|
31
|
+
* async delete(id) { return rows.delete(id); },
|
|
32
|
+
* };
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Input passed to `Storage.upload()`.
|
|
38
|
+
* The preset populates this from the parsed multipart file.
|
|
39
|
+
*/
|
|
40
|
+
interface StorageUploadInput {
|
|
41
|
+
/** File bytes. */
|
|
42
|
+
buffer: Buffer;
|
|
43
|
+
/** Original filename from the client. */
|
|
44
|
+
filename: string;
|
|
45
|
+
/** IANA media type — preset validates via `multipartBody`. */
|
|
46
|
+
mimeType: string;
|
|
47
|
+
/** Size in bytes. Equals `buffer.length`. */
|
|
48
|
+
size: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Context threaded through every storage call.
|
|
52
|
+
* Adapters decide which keys they care about.
|
|
53
|
+
*/
|
|
54
|
+
interface StorageContext {
|
|
55
|
+
/**
|
|
56
|
+
* App-defined scope. Arc populates this from `RequestScope` via the preset's
|
|
57
|
+
* `contextFrom` option so adapters can isolate per-tenant / per-user / per-project.
|
|
58
|
+
*/
|
|
59
|
+
scope?: Record<string, unknown>;
|
|
60
|
+
/** Optional request correlation id for logging. */
|
|
61
|
+
requestId?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Handle returned by `Storage.upload()`.
|
|
65
|
+
* The adapter owns the id namespace and URL format.
|
|
66
|
+
*/
|
|
67
|
+
interface StorageFile {
|
|
68
|
+
/** Stable ID — the adapter owns the namespace. Used as the route param for GET/DELETE. */
|
|
69
|
+
id: string;
|
|
70
|
+
/** Public or signed URL — what the frontend stores in an `<img src>`. */
|
|
71
|
+
url: string;
|
|
72
|
+
/** Storage-side path / key. Opaque to arc; useful for admin tooling. */
|
|
73
|
+
pathname: string;
|
|
74
|
+
/** IANA media type — usually echoed from `input.mimeType`. */
|
|
75
|
+
contentType: string;
|
|
76
|
+
/** Size in bytes. */
|
|
77
|
+
bytes: number;
|
|
78
|
+
/** Adapter-defined metadata passed through to the response body. */
|
|
79
|
+
metadata?: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Optional byte range for partial reads.
|
|
83
|
+
* End-inclusive, matching HTTP `Range: bytes=start-end` semantics and
|
|
84
|
+
* media-kit's `StorageDriver.read()` contract.
|
|
85
|
+
*/
|
|
86
|
+
interface StorageReadRange {
|
|
87
|
+
/** First byte offset (inclusive). */
|
|
88
|
+
start: number;
|
|
89
|
+
/** Last byte offset (inclusive). */
|
|
90
|
+
end: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Result of `Storage.read()`.
|
|
94
|
+
* Adapters pick whichever shape is natural; the preset handler branches on `kind`.
|
|
95
|
+
* Large/remote backends should return a stream; small/in-memory backends can
|
|
96
|
+
* return a buffer without wrapping it in a `PassThrough` for no reason.
|
|
97
|
+
*/
|
|
98
|
+
type StorageReadResult = {
|
|
99
|
+
kind: "stream";
|
|
100
|
+
stream: NodeJS.ReadableStream;
|
|
101
|
+
contentType: string; /** Total size of the full object (NOT the ranged slice). Required for `Content-Range`. */
|
|
102
|
+
bytes?: number; /** Actual byte range returned when the caller passed `range`. */
|
|
103
|
+
range?: StorageReadRange;
|
|
104
|
+
} | {
|
|
105
|
+
kind: "buffer";
|
|
106
|
+
buffer: Buffer;
|
|
107
|
+
contentType: string; /** Total size of the full object. Used for `Content-Range` on partial reads. */
|
|
108
|
+
totalBytes?: number; /** Actual byte range returned when the caller passed `range`. */
|
|
109
|
+
range?: StorageReadRange;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Minimal storage contract consumed by `filesUploadPreset`.
|
|
113
|
+
*
|
|
114
|
+
* Adapters are ~50–100 LOC wrappers around S3, GCS, local FS, GridFS,
|
|
115
|
+
* media-kit, or a provider Files API. Ship zero reference adapters in
|
|
116
|
+
* arc core — the one you'd pick is always the wrong default.
|
|
117
|
+
*/
|
|
118
|
+
interface Storage {
|
|
119
|
+
/** Write bytes, return a stable handle. */
|
|
120
|
+
upload(input: StorageUploadInput, ctx: StorageContext): Promise<StorageFile>;
|
|
121
|
+
/**
|
|
122
|
+
* Read bytes by id. Streaming preferred; Buffer fine for small files.
|
|
123
|
+
*
|
|
124
|
+
* If `range` is provided, implementations SHOULD return only the requested
|
|
125
|
+
* slice and set `range` on the result. Implementations that cannot range
|
|
126
|
+
* (e.g. trivial in-memory adapters) MAY return the full object and let the
|
|
127
|
+
* preset slice it — the preset handles both cases.
|
|
128
|
+
*/
|
|
129
|
+
read(id: string, ctx: StorageContext, range?: StorageReadRange): Promise<StorageReadResult>;
|
|
130
|
+
/**
|
|
131
|
+
* Remove bytes by id. MAY be a soft delete — the interface doesn't
|
|
132
|
+
* dictate lifecycle. Return `false` when the id was already absent.
|
|
133
|
+
*/
|
|
134
|
+
delete(id: string, ctx: StorageContext): Promise<boolean>;
|
|
135
|
+
/** Optional — fast existence check without a full read. */
|
|
136
|
+
exists?(id: string, ctx: StorageContext): Promise<boolean>;
|
|
137
|
+
/**
|
|
138
|
+
* Optional — return a fresh URL for an existing id. Called by the preset
|
|
139
|
+
* when the stored `url` may have expired (signed URLs, rotated buckets).
|
|
140
|
+
* Default behavior when omitted: the preset falls back to the url stored
|
|
141
|
+
* on the original `upload()` result.
|
|
142
|
+
*/
|
|
143
|
+
resolveUrl?(id: string, ctx: StorageContext): Promise<string>;
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
export { StorageReadResult as a, StorageReadRange as i, StorageContext as n, StorageUploadInput as o, StorageFile as r, Storage as t };
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Zt as CrudRepository, m as AnyRecord, qt as ResourceDefinition } from "../interface-
|
|
2
|
-
import { d as ResourceLike, r as CreateAppOptions } from "../types-
|
|
1
|
+
import { Zt as CrudRepository, m as AnyRecord, qt as ResourceDefinition } from "../interface-CMRutPfe.mjs";
|
|
2
|
+
import { d as ResourceLike, r as CreateAppOptions } from "../types-Ch9pTQbf.mjs";
|
|
3
|
+
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
3
4
|
import Fastify, { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
4
5
|
import { Connection } from "mongoose";
|
|
5
6
|
import { Mock } from "vitest";
|
|
@@ -921,4 +922,4 @@ declare class TestDataLoader {
|
|
|
921
922
|
cleanup(): Promise<void>;
|
|
922
923
|
}
|
|
923
924
|
//#endregion
|
|
924
|
-
export { type AuthProvider, type AuthResponse, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type CreateTestAppOptions, DatabaseSnapshot, TestFixtures as DbTestFixtures, type GenerateTestFileOptions, HttpTestHarness, type HttpTestHarnessOptions, InMemoryDatabase, type OrgResponse, type SetupBetterAuthOrgOptions, type SetupUserConfig, type TestAppResult, TestDataLoader, TestDatabase, type TestFixtures$1 as TestFixtures, TestHarness, type TestHarnessOptions, type TestOrgContext, TestRequestBuilder, TestSeeder, TestTransaction, type TestUserContext, createBetterAuthProvider, createBetterAuthTestHelpers, createConfigTestSuite, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSnapshotMatcher, createSpy, createTestApp, createTestAuth, createTestHarness, createTestTimer, generateTestFile, preloadResources, preloadResourcesAsync, request, safeParseBody, setupBetterAuthOrg, waitFor, withTestDb };
|
|
925
|
+
export { type AuthProvider, type AuthResponse, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type CreateTestAppOptions, DatabaseSnapshot, TestFixtures as DbTestFixtures, type GenerateTestFileOptions, HttpTestHarness, type HttpTestHarnessOptions, InMemoryDatabase, type OrgResponse, type SetupBetterAuthOrgOptions, type SetupUserConfig, type StorageContractSetup, type StorageContractSetupResult, type TestAppResult, TestDataLoader, TestDatabase, type TestFixtures$1 as TestFixtures, TestHarness, type TestHarnessOptions, type TestOrgContext, TestRequestBuilder, TestSeeder, TestTransaction, type TestUserContext, createBetterAuthProvider, createBetterAuthTestHelpers, createConfigTestSuite, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSnapshotMatcher, createSpy, createTestApp, createTestAuth, createTestHarness, createTestTimer, generateTestFile, preloadResources, preloadResourcesAsync, request, runStorageContract, safeParseBody, setupBetterAuthOrg, waitFor, withTestDb };
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-Cxde4rpC.mjs";
|
|
2
2
|
import { n as applyFieldWritePermissions, t as applyFieldReadPermissions } from "../fields-ipsbIRPK.mjs";
|
|
3
|
+
import { runStorageContract } from "./storageContract.mjs";
|
|
3
4
|
import Fastify from "fastify";
|
|
4
5
|
import mongoose from "mongoose";
|
|
5
6
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
@@ -1796,7 +1797,7 @@ function runEventTests(resourceName, displayName, events) {
|
|
|
1796
1797
|
* ```
|
|
1797
1798
|
*/
|
|
1798
1799
|
async function createTestApp(options = {}) {
|
|
1799
|
-
const { createApp } = await import("../createApp-
|
|
1800
|
+
const { createApp } = await import("../createApp-B1EY8zxa.mjs").then((n) => n.r);
|
|
1800
1801
|
const { useInMemoryDb = true, mongoUri: providedMongoUri, ...appOptions } = options;
|
|
1801
1802
|
const defaultAuth = {
|
|
1802
1803
|
type: "jwt",
|
|
@@ -1994,4 +1995,4 @@ var TestDataLoader = class {
|
|
|
1994
1995
|
}
|
|
1995
1996
|
};
|
|
1996
1997
|
//#endregion
|
|
1997
|
-
export { DatabaseSnapshot, TestFixtures as DbTestFixtures, HttpTestHarness, InMemoryDatabase, TestDataLoader, TestDatabase, TestHarness, TestRequestBuilder, TestSeeder, TestTransaction, createBetterAuthProvider, createBetterAuthTestHelpers, createConfigTestSuite, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSnapshotMatcher, createSpy, createTestApp, createTestAuth, createTestHarness, createTestTimer, generateTestFile, preloadResources, preloadResourcesAsync, request, safeParseBody, setupBetterAuthOrg, waitFor, withTestDb };
|
|
1998
|
+
export { DatabaseSnapshot, TestFixtures as DbTestFixtures, HttpTestHarness, InMemoryDatabase, TestDataLoader, TestDatabase, TestHarness, TestRequestBuilder, TestSeeder, TestTransaction, createBetterAuthProvider, createBetterAuthTestHelpers, createConfigTestSuite, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSnapshotMatcher, createSpy, createTestApp, createTestAuth, createTestHarness, createTestTimer, generateTestFile, preloadResources, preloadResourcesAsync, request, runStorageContract, safeParseBody, setupBetterAuthOrg, waitFor, withTestDb };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { t as Storage } from "../storage-Dfzt4VTl.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/testing/storageContract.d.ts
|
|
4
|
+
interface StorageContractSetupResult {
|
|
5
|
+
storage: Storage;
|
|
6
|
+
teardown: () => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
type StorageContractSetup = () => Promise<StorageContractSetupResult>;
|
|
9
|
+
/**
|
|
10
|
+
* Register the storage contract suite under the caller's name.
|
|
11
|
+
*
|
|
12
|
+
* Assertions covered:
|
|
13
|
+
* 1. upload() returns a StorageFile with every required field populated
|
|
14
|
+
* 2. read(upload.id) round-trips the exact bytes
|
|
15
|
+
* 3. delete() returns true on first call
|
|
16
|
+
* 4. delete() returns false (or throws) on a missing id
|
|
17
|
+
* 5. exists() (if implemented) agrees with upload/delete state
|
|
18
|
+
* 6. resolveUrl() (if implemented) returns a non-empty URL for an existing id
|
|
19
|
+
* 7. Two isolated scopes don't collide (scope threading)
|
|
20
|
+
* 8. Full lifecycle: upload → read → delete → read rejects
|
|
21
|
+
* 9. Both `kind: "stream"` and `kind: "buffer"` read results deliver correct bytes
|
|
22
|
+
* 10. Ranged reads (if adapter supports them) slice correctly
|
|
23
|
+
*/
|
|
24
|
+
declare function runStorageContract(name: string, setup: StorageContractSetup): void;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { StorageContractSetup, StorageContractSetupResult, runStorageContract };
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
//#region src/testing/storageContract.ts
|
|
3
|
+
/**
|
|
4
|
+
* Storage Contract Suite
|
|
5
|
+
*
|
|
6
|
+
* Any implementation of `@classytic/arc/types/storage`'s `Storage` interface
|
|
7
|
+
* can import this and run it against a live instance to guarantee preset
|
|
8
|
+
* compatibility. Passing this suite is the contract.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { runStorageContract } from '@classytic/arc/testing/storage';
|
|
13
|
+
* import { s3Storage } from '../src/storage/s3-storage.js';
|
|
14
|
+
*
|
|
15
|
+
* runStorageContract('s3Storage', async () => {
|
|
16
|
+
* const storage = s3Storage({ bucket: 'test-bucket' });
|
|
17
|
+
* return { storage, teardown: async () => {} };
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* This module statically imports `vitest`. Only load it from test code — arc's
|
|
22
|
+
* production bundle never references this subpath, so the import tree stays
|
|
23
|
+
* clean under tree-shaking.
|
|
24
|
+
*/
|
|
25
|
+
function makeBytes(size, seed = 0) {
|
|
26
|
+
const buf = Buffer.allocUnsafe(size);
|
|
27
|
+
for (let i = 0; i < size; i++) buf[i] = i + seed & 255;
|
|
28
|
+
return buf;
|
|
29
|
+
}
|
|
30
|
+
async function readAll(result) {
|
|
31
|
+
if (result.kind === "buffer") return result.buffer;
|
|
32
|
+
const chunks = [];
|
|
33
|
+
for await (const chunk of result.stream) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
34
|
+
return Buffer.concat(chunks);
|
|
35
|
+
}
|
|
36
|
+
const EMPTY_CTX = { scope: {} };
|
|
37
|
+
function ctxFor(scope) {
|
|
38
|
+
return { scope };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Register the storage contract suite under the caller's name.
|
|
42
|
+
*
|
|
43
|
+
* Assertions covered:
|
|
44
|
+
* 1. upload() returns a StorageFile with every required field populated
|
|
45
|
+
* 2. read(upload.id) round-trips the exact bytes
|
|
46
|
+
* 3. delete() returns true on first call
|
|
47
|
+
* 4. delete() returns false (or throws) on a missing id
|
|
48
|
+
* 5. exists() (if implemented) agrees with upload/delete state
|
|
49
|
+
* 6. resolveUrl() (if implemented) returns a non-empty URL for an existing id
|
|
50
|
+
* 7. Two isolated scopes don't collide (scope threading)
|
|
51
|
+
* 8. Full lifecycle: upload → read → delete → read rejects
|
|
52
|
+
* 9. Both `kind: "stream"` and `kind: "buffer"` read results deliver correct bytes
|
|
53
|
+
* 10. Ranged reads (if adapter supports them) slice correctly
|
|
54
|
+
*/
|
|
55
|
+
function runStorageContract(name, setup) {
|
|
56
|
+
describe(`Storage contract — ${name}`, () => {
|
|
57
|
+
let storage;
|
|
58
|
+
let teardown;
|
|
59
|
+
beforeAll(async () => {
|
|
60
|
+
const result = await setup();
|
|
61
|
+
storage = result.storage;
|
|
62
|
+
teardown = result.teardown;
|
|
63
|
+
});
|
|
64
|
+
afterAll(async () => {
|
|
65
|
+
if (teardown) await teardown();
|
|
66
|
+
});
|
|
67
|
+
it("upload() returns a populated StorageFile", async () => {
|
|
68
|
+
const bytes = makeBytes(64);
|
|
69
|
+
const file = await storage.upload({
|
|
70
|
+
buffer: bytes,
|
|
71
|
+
filename: "contract-1.bin",
|
|
72
|
+
mimeType: "application/octet-stream",
|
|
73
|
+
size: bytes.length
|
|
74
|
+
}, EMPTY_CTX);
|
|
75
|
+
expect(file.id).toBeTruthy();
|
|
76
|
+
expect(file.url).toBeTruthy();
|
|
77
|
+
expect(file.pathname).toBeTruthy();
|
|
78
|
+
expect(file.contentType).toBe("application/octet-stream");
|
|
79
|
+
expect(file.bytes).toBe(bytes.length);
|
|
80
|
+
await storage.delete(file.id, EMPTY_CTX);
|
|
81
|
+
});
|
|
82
|
+
it("read() round-trips the exact bytes uploaded", async () => {
|
|
83
|
+
const bytes = makeBytes(1024, 7);
|
|
84
|
+
const file = await storage.upload({
|
|
85
|
+
buffer: bytes,
|
|
86
|
+
filename: "contract-2.bin",
|
|
87
|
+
mimeType: "application/octet-stream",
|
|
88
|
+
size: bytes.length
|
|
89
|
+
}, EMPTY_CTX);
|
|
90
|
+
const read = await storage.read(file.id, EMPTY_CTX);
|
|
91
|
+
expect((await readAll(read)).equals(bytes)).toBe(true);
|
|
92
|
+
expect(read.contentType).toBe("application/octet-stream");
|
|
93
|
+
await storage.delete(file.id, EMPTY_CTX);
|
|
94
|
+
});
|
|
95
|
+
it("delete() returns true the first time, false (or throws) the second time", async () => {
|
|
96
|
+
const bytes = makeBytes(32);
|
|
97
|
+
const file = await storage.upload({
|
|
98
|
+
buffer: bytes,
|
|
99
|
+
filename: "contract-3.bin",
|
|
100
|
+
mimeType: "application/octet-stream",
|
|
101
|
+
size: bytes.length
|
|
102
|
+
}, EMPTY_CTX);
|
|
103
|
+
expect(await storage.delete(file.id, EMPTY_CTX)).toBe(true);
|
|
104
|
+
let second = "threw";
|
|
105
|
+
try {
|
|
106
|
+
second = await storage.delete(file.id, EMPTY_CTX);
|
|
107
|
+
} catch {
|
|
108
|
+
second = "threw";
|
|
109
|
+
}
|
|
110
|
+
expect(second === false || second === "threw").toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it("exists() agrees with upload/delete state (if implemented)", async () => {
|
|
113
|
+
if (!storage.exists) return;
|
|
114
|
+
const bytes = makeBytes(16);
|
|
115
|
+
const file = await storage.upload({
|
|
116
|
+
buffer: bytes,
|
|
117
|
+
filename: "contract-4.bin",
|
|
118
|
+
mimeType: "application/octet-stream",
|
|
119
|
+
size: bytes.length
|
|
120
|
+
}, EMPTY_CTX);
|
|
121
|
+
expect(await storage.exists(file.id, EMPTY_CTX)).toBe(true);
|
|
122
|
+
await storage.delete(file.id, EMPTY_CTX);
|
|
123
|
+
expect(await storage.exists(file.id, EMPTY_CTX)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
it("resolveUrl() returns a non-empty URL for an existing id (if implemented)", async () => {
|
|
126
|
+
if (!storage.resolveUrl) return;
|
|
127
|
+
const bytes = makeBytes(8);
|
|
128
|
+
const file = await storage.upload({
|
|
129
|
+
buffer: bytes,
|
|
130
|
+
filename: "contract-5.bin",
|
|
131
|
+
mimeType: "application/octet-stream",
|
|
132
|
+
size: bytes.length
|
|
133
|
+
}, EMPTY_CTX);
|
|
134
|
+
const url = await storage.resolveUrl(file.id, EMPTY_CTX);
|
|
135
|
+
expect(typeof url).toBe("string");
|
|
136
|
+
expect(url.length).toBeGreaterThan(0);
|
|
137
|
+
await storage.delete(file.id, EMPTY_CTX);
|
|
138
|
+
});
|
|
139
|
+
it("two different scopes get distinct ids (scope threading)", async () => {
|
|
140
|
+
const bytes = makeBytes(24, 42);
|
|
141
|
+
const scopeA = ctxFor({ organizationId: "org-a" });
|
|
142
|
+
const scopeB = ctxFor({ organizationId: "org-b" });
|
|
143
|
+
const a = await storage.upload({
|
|
144
|
+
buffer: bytes,
|
|
145
|
+
filename: "scoped.bin",
|
|
146
|
+
mimeType: "application/octet-stream",
|
|
147
|
+
size: bytes.length
|
|
148
|
+
}, scopeA);
|
|
149
|
+
const b = await storage.upload({
|
|
150
|
+
buffer: bytes,
|
|
151
|
+
filename: "scoped.bin",
|
|
152
|
+
mimeType: "application/octet-stream",
|
|
153
|
+
size: bytes.length
|
|
154
|
+
}, scopeB);
|
|
155
|
+
expect(a.id).not.toBe(b.id);
|
|
156
|
+
const readA = await readAll(await storage.read(a.id, scopeA));
|
|
157
|
+
const readB = await readAll(await storage.read(b.id, scopeB));
|
|
158
|
+
expect(readA.equals(bytes)).toBe(true);
|
|
159
|
+
expect(readB.equals(bytes)).toBe(true);
|
|
160
|
+
await storage.delete(a.id, scopeA);
|
|
161
|
+
await storage.delete(b.id, scopeB);
|
|
162
|
+
});
|
|
163
|
+
it("full lifecycle: upload → read → delete → read rejects", async () => {
|
|
164
|
+
const bytes = makeBytes(128);
|
|
165
|
+
const file = await storage.upload({
|
|
166
|
+
buffer: bytes,
|
|
167
|
+
filename: "lifecycle.bin",
|
|
168
|
+
mimeType: "application/octet-stream",
|
|
169
|
+
size: bytes.length
|
|
170
|
+
}, EMPTY_CTX);
|
|
171
|
+
expect((await readAll(await storage.read(file.id, EMPTY_CTX))).equals(bytes)).toBe(true);
|
|
172
|
+
expect(await storage.delete(file.id, EMPTY_CTX)).toBe(true);
|
|
173
|
+
let rejected = false;
|
|
174
|
+
try {
|
|
175
|
+
if (!(await readAll(await storage.read(file.id, EMPTY_CTX))).equals(bytes)) rejected = true;
|
|
176
|
+
} catch {
|
|
177
|
+
rejected = true;
|
|
178
|
+
}
|
|
179
|
+
expect(rejected).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
it("read() handles both stream and buffer kinds", async () => {
|
|
182
|
+
const bytes = makeBytes(256, 9);
|
|
183
|
+
const file = await storage.upload({
|
|
184
|
+
buffer: bytes,
|
|
185
|
+
filename: "kind.bin",
|
|
186
|
+
mimeType: "application/octet-stream",
|
|
187
|
+
size: bytes.length
|
|
188
|
+
}, EMPTY_CTX);
|
|
189
|
+
const result = await storage.read(file.id, EMPTY_CTX);
|
|
190
|
+
expect(result.kind === "stream" || result.kind === "buffer").toBe(true);
|
|
191
|
+
expect((await readAll(result)).equals(bytes)).toBe(true);
|
|
192
|
+
await storage.delete(file.id, EMPTY_CTX);
|
|
193
|
+
});
|
|
194
|
+
it("read() with a mid-object range slices correctly (when adapter supports ranges)", async () => {
|
|
195
|
+
const bytes = makeBytes(1024, 13);
|
|
196
|
+
const file = await storage.upload({
|
|
197
|
+
buffer: bytes,
|
|
198
|
+
filename: "range.bin",
|
|
199
|
+
mimeType: "application/octet-stream",
|
|
200
|
+
size: bytes.length
|
|
201
|
+
}, EMPTY_CTX);
|
|
202
|
+
const result = await storage.read(file.id, EMPTY_CTX, {
|
|
203
|
+
start: 100,
|
|
204
|
+
end: 199
|
|
205
|
+
});
|
|
206
|
+
const actual = await readAll(result);
|
|
207
|
+
if (result.range) {
|
|
208
|
+
expect(actual.length).toBe(100);
|
|
209
|
+
expect(actual.equals(bytes.subarray(100, 200))).toBe(true);
|
|
210
|
+
} else expect(actual.length).toBe(bytes.length);
|
|
211
|
+
await storage.delete(file.id, EMPTY_CTX);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
216
|
+
export { runStorageContract };
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as isAuthenticated, c as getOrgRoles, g as hasOrgAccess, n as PUBLIC_SCOPE, p as getTeamId, r as RequestScope, s as getOrgId, t as AUTHENTICATED_SCOPE, v as isElevated, y as isMember } from "../types-
|
|
2
|
-
import { $ as PresetFunction, $t as DeleteOptions, A as EventsDecorator, B as InferResourceDoc, Bt as FastifyHandler, C as ConfigError, Ct as TypedResourceConfig, D as CrudRouterOptions, Dt as ValidationResult, E as CrudRouteKey, Et as ValidateOptions, F as GracefulShutdownOptions, G as LookupOption, H as IntrospectionPluginOptions, Ht as IControllerResponse, I as HealthCheck, J as ObjectId, K as MiddlewareConfig, L as HealthOptions, M as FastifyWithAuth, N as FastifyWithDecorators, O as CrudSchemas, Ot as envelope, P as FieldRule, Q as PopulateOption, Qt as DeleteManyResult, R as InferAdapterDoc, Rt as ControllerHandler, S as AuthenticatorContext, St as TypedRepository, T as CrudController, Tt as UserOrganization, U as JWTPayload, Ut as IRequestContext, V as IntrospectionData, Vt as IController, W as JwtContext, Wt as RouteHandler, X as OwnershipCheck, Xt as BulkWriteResult, Y as OpenApiSchemas, Yt as BulkWriteOperation, Z as ParsedQuery, Zt as CrudRepository, _ as ArcInternalMetadata, _t as RouteMcpConfig, an as PaginationParams, at as RegistryStats, b as AuthPluginOptions, bt as TokenPair, cn as RepositorySession, ct as RequestWithExtras, d as ActionHandlerFn, dt as ResourceHookContext, en as DeleteResult, et as PresetHook, f as ActionsMap, ft as ResourceHooks, g as ArcDecorator, gt as RouteHandlerMethod, h as ApiResponse, ht as RouteDefinition, in as PaginatedResult, it as RegistryEntry, j as FastifyRequestExtras, jt as BaseControllerOptions, k as EventDefinition, kt as getUserId, l as ActionDefinition, ln as UpdateManyResult, lt as ResourceCacheConfig, m as AnyRecord, mt as ResourcePermissions, nn as KeysetPaginatedResult, nt as QueryParserInterface, on as PaginationResult, ot as RequestContext, p as AdditionalRoute, pt as ResourceMetadata, q as MiddlewareHandler, rn as OffsetPaginatedResult, rt as RateLimitConfig, sn as QueryOptions, st as RequestIdOptions, tn as InferDoc, tt as PresetResult, u as ActionEntry, un as WriteOptions, ut as ResourceConfig, v as ArcRequest, vt as RouteSchemaOptions, w as ControllerQueryOptions, wt as UserLike, x as Authenticator, xt as TypedController, y as AuthHelpers, yt as ServiceContext, z as InferDocType, zt as ControllerLike } from "../interface-
|
|
3
|
-
import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-
|
|
4
|
-
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { _ as isAuthenticated, c as getOrgRoles, g as hasOrgAccess, n as PUBLIC_SCOPE, p as getTeamId, r as RequestScope, s as getOrgId, t as AUTHENTICATED_SCOPE, v as isElevated, y as isMember } from "../types-BD85MlEK.mjs";
|
|
2
|
+
import { $ as PresetFunction, $t as DeleteOptions, A as EventsDecorator, B as InferResourceDoc, Bt as FastifyHandler, C as ConfigError, Ct as TypedResourceConfig, D as CrudRouterOptions, Dt as ValidationResult, E as CrudRouteKey, Et as ValidateOptions, F as GracefulShutdownOptions, G as LookupOption, H as IntrospectionPluginOptions, Ht as IControllerResponse, I as HealthCheck, J as ObjectId, K as MiddlewareConfig, L as HealthOptions, M as FastifyWithAuth, N as FastifyWithDecorators, O as CrudSchemas, Ot as envelope, P as FieldRule, Q as PopulateOption, Qt as DeleteManyResult, R as InferAdapterDoc, Rt as ControllerHandler, S as AuthenticatorContext, St as TypedRepository, T as CrudController, Tt as UserOrganization, U as JWTPayload, Ut as IRequestContext, V as IntrospectionData, Vt as IController, W as JwtContext, Wt as RouteHandler, X as OwnershipCheck, Xt as BulkWriteResult, Y as OpenApiSchemas, Yt as BulkWriteOperation, Z as ParsedQuery, Zt as CrudRepository, _ as ArcInternalMetadata, _t as RouteMcpConfig, an as PaginationParams, at as RegistryStats, b as AuthPluginOptions, bt as TokenPair, cn as RepositorySession, ct as RequestWithExtras, d as ActionHandlerFn, dt as ResourceHookContext, en as DeleteResult, et as PresetHook, f as ActionsMap, ft as ResourceHooks, g as ArcDecorator, gt as RouteHandlerMethod, h as ApiResponse, ht as RouteDefinition, in as PaginatedResult, it as RegistryEntry, j as FastifyRequestExtras, jt as BaseControllerOptions, k as EventDefinition, kt as getUserId, l as ActionDefinition, ln as UpdateManyResult, lt as ResourceCacheConfig, m as AnyRecord, mt as ResourcePermissions, nn as KeysetPaginatedResult, nt as QueryParserInterface, on as PaginationResult, ot as RequestContext, p as AdditionalRoute, pt as ResourceMetadata, q as MiddlewareHandler, rn as OffsetPaginatedResult, rt as RateLimitConfig, sn as QueryOptions, st as RequestIdOptions, tn as InferDoc, tt as PresetResult, u as ActionEntry, un as WriteOptions, ut as ResourceConfig, v as ArcRequest, vt as RouteSchemaOptions, w as ControllerQueryOptions, wt as UserLike, x as Authenticator, xt as TypedController, y as AuthHelpers, yt as ServiceContext, z as InferDocType, zt as ControllerLike } from "../interface-CMRutPfe.mjs";
|
|
3
|
+
import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-DZi1aYhm.mjs";
|
|
4
|
+
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-B6S5csVA.mjs";
|
|
5
5
|
export { AUTHENTICATED_SCOPE, ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AdditionalRoute, AnyRecord, ApiResponse, ArcDecorator, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, BaseControllerOptions, BulkWriteOperation, BulkWriteResult, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRepository, CrudRouteKey, CrudRouterOptions, CrudSchemas, DeleteManyResult, DeleteOptions, DeleteResult, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, KeysetPaginatedResult, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OffsetPaginatedResult, OpenApiSchemas, OwnershipCheck, PUBLIC_SCOPE, PaginatedResult, PaginationParams, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryOptions, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RepositorySession, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResourceCacheConfig, ResourceConfig, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UpdateManyResult, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult, WriteOptions, envelope, getOrgId, getOrgRoles, getTeamId, getUserId, hasOrgAccess, isAuthenticated, isElevated, isMember };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { x as Authenticator } from "./interface-
|
|
2
|
-
import { n as ElevationOptions } from "./elevation-
|
|
3
|
-
import { t as ExternalOpenApiPaths } from "./externalPaths-
|
|
4
|
-
import { i as CacheStore } from "./interface-
|
|
5
|
-
import { r as QueryCachePluginOptions } from "./queryCachePlugin-
|
|
6
|
-
import { i as EventTransport } from "./EventTransport-
|
|
7
|
-
import { t as EventPluginOptions } from "./eventPlugin-
|
|
8
|
-
import { f as SSEOptions, h as CachingOptions, i as VersioningOptions, l as MetricsOptions, t as ErrorHandlerOptions } from "./errorHandler-
|
|
9
|
-
import { r as IdempotencyStore } from "./interface-
|
|
1
|
+
import { x as Authenticator } from "./interface-CMRutPfe.mjs";
|
|
2
|
+
import { n as ElevationOptions } from "./elevation-B6S5csVA.mjs";
|
|
3
|
+
import { t as ExternalOpenApiPaths } from "./externalPaths-BnkYrNzp.mjs";
|
|
4
|
+
import { i as CacheStore } from "./interface-4y979v99.mjs";
|
|
5
|
+
import { r as QueryCachePluginOptions } from "./queryCachePlugin-BJJGBTlu.mjs";
|
|
6
|
+
import { i as EventTransport } from "./EventTransport-BXja8NOc.mjs";
|
|
7
|
+
import { t as EventPluginOptions } from "./eventPlugin-D9DKB2zM.mjs";
|
|
8
|
+
import { f as SSEOptions, h as CachingOptions, i as VersioningOptions, l as MetricsOptions, t as ErrorHandlerOptions } from "./errorHandler-Bah5JhBd.mjs";
|
|
9
|
+
import { r as IdempotencyStore } from "./interface-DfLGcus7.mjs";
|
|
10
10
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
|
|
11
11
|
|
|
12
12
|
//#region src/factory/loadResources.d.ts
|