@classytic/arc 2.8.4 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -5
- package/dist/{BaseController-DAGGc5Xn.mjs → BaseController-Vu2yc56T.mjs} +188 -102
- package/dist/EventTransport-CqZ8FyM_.d.mts +293 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/audit/index.d.mts +100 -11
- package/dist/audit/index.mjs +71 -18
- package/dist/auth/index.d.mts +15 -7
- package/dist/auth/index.mjs +13 -6
- package/dist/{betterAuthOpenApi-C5lDyRH2.mjs → betterAuthOpenApi--rdY15Ld.mjs} +1 -1
- package/dist/cache/index.d.mts +71 -1
- package/dist/cache/index.mjs +96 -3
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +4 -5
- package/dist/{core-DKSwNSXf.mjs → core-DNncu0xF.mjs} +1 -1
- package/dist/{createActionRouter-Df1BuawX.mjs → createActionRouter-DH1YFL9m.mjs} +3 -3
- package/dist/{createApp-BOYjBgdI.mjs → createApp-CBJUJKGP.mjs} +6 -5
- package/dist/{defineResource-Bb_Bdhtw.mjs → defineResource-C__jkwvs.mjs} +22 -57
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/dynamic/index.d.mts +2 -2
- package/dist/dynamic/index.mjs +3 -3
- package/dist/{elevation-BBGFjzIP.mjs → elevation-DxQ6ACbt.mjs} +20 -6
- package/dist/{errorHandler-mzqk4cGl.mjs → errorHandler-CZDW4EXS.mjs} +59 -7
- package/dist/{errorHandler-CdZDavNH.d.mts → errorHandler-DixGcttC.d.mts} +37 -2
- package/dist/{eventPlugin-CVxlE6De.d.mts → eventPlugin-BxvaCIZF.d.mts} +14 -2
- package/dist/{eventPlugin-D91S2YF4.mjs → eventPlugin-Dl7MoVWH.mjs} +83 -5
- package/dist/events/index.d.mts +147 -36
- package/dist/events/index.mjs +338 -101
- 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 +1 -1
- package/dist/{fields-DC4So2M2.d.mts → fields-BC7zcmI9.d.mts} +15 -3
- package/dist/{fields-ipsbIRPK.mjs → fields-CU6FlaDV.mjs} +18 -5
- package/dist/filesUpload-q8oHt--L.mjs +377 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +28 -4
- package/dist/idempotency/index.mjs +111 -2
- package/dist/idempotency/redis.d.mts +2 -2
- package/dist/idempotency/redis.mjs +134 -13
- package/dist/{index-CSkeivBx.d.mts → index-C-xjcA6F.d.mts} +2 -2
- package/dist/{index-CpTSDqmD.d.mts → index-Cibkchnx.d.mts} +5 -136
- package/dist/{index-BgmMdpm8.d.mts → index-CtGKT0lf.d.mts} +1 -1
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +8 -8
- package/dist/integrations/event-gateway.d.mts +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 +26 -8
- package/dist/integrations/mcp/index.mjs +96 -17
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +5 -0
- package/dist/integrations/webhooks.mjs +6 -0
- package/dist/{interface-BVuMfeVv.d.mts → interface-YrWsmKqE.d.mts} +324 -194
- package/dist/{openapi-CYCuekCn.mjs → openapi-CXuTG1M9.mjs} +3 -3
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-CH4cNwJi.mjs → permissions-oNZawnkR.mjs} +1 -1
- package/dist/plugins/index.d.mts +6 -6
- package/dist/plugins/index.mjs +4 -4
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/policies/index.d.mts +26 -33
- package/dist/presets/filesUpload.d.mts +71 -0
- package/dist/presets/filesUpload.mjs +2 -0
- package/dist/presets/index.d.mts +4 -2
- package/dist/presets/index.mjs +4 -2
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/presets/search.d.mts +91 -0
- package/dist/presets/search.mjs +150 -0
- package/dist/{presets-C2xgzW6x.mjs → presets-hM4WhNWY.mjs} +1 -1
- package/dist/{queryCachePlugin-D0iIVhW_.mjs → queryCachePlugin-DbUVroUG.mjs} +2 -2
- package/dist/redis-MXLp1oOf.d.mts +115 -0
- package/dist/{redis-stream-D54N5oXs.d.mts → redis-stream-Bz-4q96t.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/{resourceToTools-O_HwWXFa.mjs → resourceToTools-C3cWymnW.mjs} +65 -48
- package/dist/rpc/index.mjs +1 -1
- package/dist/{schemaConverter-OxfCshus.mjs → schemaConverter-BxFDdtXu.mjs} +25 -9
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +1 -1
- package/dist/storage-BwGQXUpd.d.mts +146 -0
- package/dist/store-helpers-DFiZl5TL.mjs +57 -0
- package/dist/testing/index.d.mts +7 -15
- package/dist/testing/index.mjs +23 -76
- package/dist/testing/storageContract.d.mts +26 -0
- package/dist/testing/storageContract.mjs +216 -0
- package/dist/types/index.d.mts +5 -5
- package/dist/types/storage.d.mts +2 -0
- package/dist/types/storage.mjs +1 -0
- package/dist/{types-CcG4avic.d.mts → types-CoSzA-s-.d.mts} +1 -1
- package/dist/{types-Bg2X42_m.d.mts → types-CunEX4UX.d.mts} +7 -5
- 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 +6 -6
- package/dist/{utils-yYT3HDXt.mjs → utils-B7FuRr9w.mjs} +1 -1
- package/package.json +23 -11
- package/skills/arc/SKILL.md +92 -14
- package/skills/arc/references/auth.md +94 -0
- package/skills/arc/references/events.md +229 -12
- package/skills/arc/references/mcp.md +4 -17
- package/skills/arc/references/multi-tenancy.md +43 -0
- package/skills/arc/references/production.md +34 -19
- package/dist/EventTransport-CinyO7zQ.d.mts +0 -135
- package/dist/audit/mongodb.d.mts +0 -2
- package/dist/audit/mongodb.mjs +0 -2
- package/dist/idempotency/mongodb.d.mts +0 -2
- package/dist/idempotency/mongodb.mjs +0 -123
- package/dist/mongodb-B5O6xaW1.mjs +0 -90
- package/dist/mongodb-B8U2xaLj.d.mts +0 -127
- package/dist/mongodb-X7LbEjTN.d.mts +0 -80
- package/dist/redis-z3sFr1UP.d.mts +0 -49
- /package/dist/{applyPermissionResult-D6GPMsvh.mjs → applyPermissionResult-bqGpo9ML.mjs} +0 -0
- /package/dist/{circuitBreaker-cmi5XDv5.mjs → circuitBreaker-l18oRgL5.mjs} +0 -0
- /package/dist/{elevation-s5ykdNHr.d.mts → elevation-B6S5csVA.d.mts} +0 -0
- /package/dist/{errors-Bmn3eZT6.d.mts → errors-BI8kEKsO.d.mts} +0 -0
- /package/dist/{errors-BF2bIOIS.mjs → errors-CqWnSqM-.mjs} +0 -0
- /package/dist/{memory-Cp7_cAko.mjs → memory-BFAYkf8H.mjs} +0 -0
- /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-CWP6MB39.mjs} +0 -0
- /package/dist/{queryParser-CgCtsjti.mjs → queryParser-Cs-6SHQK.mjs} +0 -0
- /package/dist/{requestContext-DYvHl113.mjs → requestContext-DYtmNpm5.mjs} +0 -0
- /package/dist/{tracing-DxjKk7eW.d.mts → tracing-xqXzWeaf.d.mts} +0 -0
- /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-Cj5Rgvlg.mjs} +0 -0
- /package/dist/{types-C72d3NDn.d.mts → types-BD85MlEK.d.mts} +0 -0
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-Cxde4rpC.mjs";
|
|
2
|
-
import { n as applyFieldWritePermissions, t as applyFieldReadPermissions } from "../fields-
|
|
2
|
+
import { n as applyFieldWritePermissions, t as applyFieldReadPermissions } from "../fields-CU6FlaDV.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";
|
|
@@ -1132,8 +1133,11 @@ function pickResource(value) {
|
|
|
1132
1133
|
* harness.runAll();
|
|
1133
1134
|
*
|
|
1134
1135
|
* // Or run specific test suites
|
|
1135
|
-
* harness.runCrud();
|
|
1136
1136
|
* harness.runPresets();
|
|
1137
|
+
* harness.runValidation();
|
|
1138
|
+
*
|
|
1139
|
+
* // For HTTP-level CRUD coverage (auth, permissions, routes), use
|
|
1140
|
+
* // `HttpTestHarness` from `@classytic/arc/testing`.
|
|
1137
1141
|
*/
|
|
1138
1142
|
var TestHarness = class {
|
|
1139
1143
|
resource;
|
|
@@ -1156,12 +1160,12 @@ var TestHarness = class {
|
|
|
1156
1160
|
this.Model = model;
|
|
1157
1161
|
}
|
|
1158
1162
|
/**
|
|
1159
|
-
* Run all baseline tests
|
|
1163
|
+
* Run all baseline tests (schema, presets, field permissions, pipeline, events).
|
|
1160
1164
|
*
|
|
1161
|
-
*
|
|
1165
|
+
* For HTTP-level CRUD coverage (routes, auth, permissions), use
|
|
1166
|
+
* {@link HttpTestHarness} instead.
|
|
1162
1167
|
*/
|
|
1163
1168
|
runAll() {
|
|
1164
|
-
this.runCrud();
|
|
1165
1169
|
this.runValidation();
|
|
1166
1170
|
this.runPresets();
|
|
1167
1171
|
this.runFieldPermissions();
|
|
@@ -1169,67 +1173,6 @@ var TestHarness = class {
|
|
|
1169
1173
|
this.runEvents();
|
|
1170
1174
|
}
|
|
1171
1175
|
/**
|
|
1172
|
-
* Run CRUD operation tests (model-level)
|
|
1173
|
-
*
|
|
1174
|
-
* Tests: create, read (list + getById), update, delete
|
|
1175
|
-
*
|
|
1176
|
-
* @deprecated Use `HttpTestHarness.runCrud()` for HTTP-level CRUD tests.
|
|
1177
|
-
* This method tests Mongoose models directly and does not exercise
|
|
1178
|
-
* HTTP routes, authentication, permissions, or the Arc pipeline.
|
|
1179
|
-
*/
|
|
1180
|
-
runCrud() {
|
|
1181
|
-
const { resource, fixtures, Model } = this;
|
|
1182
|
-
describe(`${resource.displayName} CRUD Operations`, () => {
|
|
1183
|
-
beforeAll(async () => {
|
|
1184
|
-
await mongoose.connect(this.mongoUri);
|
|
1185
|
-
if (this.setupFn) await this.setupFn();
|
|
1186
|
-
});
|
|
1187
|
-
afterAll(async () => {
|
|
1188
|
-
if (this._createdIds.length > 0) await Model.deleteMany({ _id: { $in: this._createdIds } });
|
|
1189
|
-
if (this.teardownFn) await this.teardownFn();
|
|
1190
|
-
await mongoose.disconnect();
|
|
1191
|
-
});
|
|
1192
|
-
describe("Create", () => {
|
|
1193
|
-
it("should create a new document with valid data", async () => {
|
|
1194
|
-
const doc = await Model.create(fixtures.valid);
|
|
1195
|
-
this._createdIds.push(doc._id);
|
|
1196
|
-
expect(doc).toBeDefined();
|
|
1197
|
-
expect(doc._id).toBeDefined();
|
|
1198
|
-
for (const [key, value] of Object.entries(fixtures.valid)) if (typeof value !== "object") expect(doc[key]).toEqual(value);
|
|
1199
|
-
});
|
|
1200
|
-
it("should have timestamps", async () => {
|
|
1201
|
-
const doc = await Model.findById(this._createdIds[0]);
|
|
1202
|
-
expect(doc).toBeDefined();
|
|
1203
|
-
expect(doc?.createdAt).toBeDefined();
|
|
1204
|
-
expect(doc?.updatedAt).toBeDefined();
|
|
1205
|
-
});
|
|
1206
|
-
});
|
|
1207
|
-
describe("Read", () => {
|
|
1208
|
-
it("should find document by ID", async () => {
|
|
1209
|
-
expect(await Model.findById(this._createdIds[0])).toBeDefined();
|
|
1210
|
-
});
|
|
1211
|
-
it("should list documents", async () => {
|
|
1212
|
-
const docs = await Model.find({});
|
|
1213
|
-
expect(Array.isArray(docs)).toBe(true);
|
|
1214
|
-
expect(docs.length).toBeGreaterThan(0);
|
|
1215
|
-
});
|
|
1216
|
-
});
|
|
1217
|
-
describe("Update", () => {
|
|
1218
|
-
it("should update document", async () => {
|
|
1219
|
-
const updateData = fixtures.update || { updatedAt: /* @__PURE__ */ new Date() };
|
|
1220
|
-
expect(await Model.findByIdAndUpdate(this._createdIds[0], updateData, { new: true })).toBeDefined();
|
|
1221
|
-
});
|
|
1222
|
-
});
|
|
1223
|
-
describe("Delete", () => {
|
|
1224
|
-
it("should delete document", async () => {
|
|
1225
|
-
const toDelete = await Model.create(fixtures.valid);
|
|
1226
|
-
await Model.findByIdAndDelete(toDelete._id);
|
|
1227
|
-
expect(await Model.findById(toDelete._id)).toBeNull();
|
|
1228
|
-
});
|
|
1229
|
-
});
|
|
1230
|
-
});
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
1176
|
* Run validation tests
|
|
1234
1177
|
*
|
|
1235
1178
|
* Tests schema validation, required fields, etc.
|
|
@@ -1370,7 +1313,7 @@ var TestHarness = class {
|
|
|
1370
1313
|
expect(result.otherField).toBe("visible");
|
|
1371
1314
|
});
|
|
1372
1315
|
it(`should strip hidden field '${field}' from writes`, () => {
|
|
1373
|
-
const result = applyFieldWritePermissions({
|
|
1316
|
+
const { body: result } = applyFieldWritePermissions({
|
|
1374
1317
|
[field]: "attempt",
|
|
1375
1318
|
name: "test"
|
|
1376
1319
|
}, fieldPerms, []);
|
|
@@ -1391,7 +1334,7 @@ var TestHarness = class {
|
|
|
1391
1334
|
break;
|
|
1392
1335
|
case "writableBy":
|
|
1393
1336
|
it(`should strip field '${field}' from writes by non-privileged users`, () => {
|
|
1394
|
-
const result = applyFieldWritePermissions({
|
|
1337
|
+
const { body: result } = applyFieldWritePermissions({
|
|
1395
1338
|
[field]: "new-value",
|
|
1396
1339
|
name: "test"
|
|
1397
1340
|
}, fieldPerms, ["viewer"]);
|
|
@@ -1401,7 +1344,8 @@ var TestHarness = class {
|
|
|
1401
1344
|
if (perm.roles && perm.roles.length > 0) {
|
|
1402
1345
|
const writeRole = perm.roles[0];
|
|
1403
1346
|
it(`should allow writing field '${field}' by roles: ${[...perm.roles].join(", ")}`, () => {
|
|
1404
|
-
|
|
1347
|
+
const { body: result } = applyFieldWritePermissions({ [field]: "new-value" }, fieldPerms, [writeRole]);
|
|
1348
|
+
expect(result[field]).toBe("new-value");
|
|
1405
1349
|
});
|
|
1406
1350
|
}
|
|
1407
1351
|
break;
|
|
@@ -1653,10 +1597,11 @@ function runFieldPermissionTests(displayName, fieldPerms) {
|
|
|
1653
1597
|
}, fieldPerms, [])[field]).toBeUndefined();
|
|
1654
1598
|
});
|
|
1655
1599
|
it(`should strip hidden field '${field}' from writes`, () => {
|
|
1656
|
-
|
|
1600
|
+
const { body: result } = applyFieldWritePermissions({
|
|
1657
1601
|
[field]: "attempt",
|
|
1658
1602
|
name: "test"
|
|
1659
|
-
}, fieldPerms, [])
|
|
1603
|
+
}, fieldPerms, []);
|
|
1604
|
+
expect(result[field]).toBeUndefined();
|
|
1660
1605
|
});
|
|
1661
1606
|
break;
|
|
1662
1607
|
case "visibleTo":
|
|
@@ -1672,15 +1617,17 @@ function runFieldPermissionTests(displayName, fieldPerms) {
|
|
|
1672
1617
|
break;
|
|
1673
1618
|
case "writableBy":
|
|
1674
1619
|
it(`should strip field '${field}' from writes by non-privileged users`, () => {
|
|
1675
|
-
|
|
1620
|
+
const { body: result } = applyFieldWritePermissions({
|
|
1676
1621
|
[field]: "v",
|
|
1677
1622
|
name: "test"
|
|
1678
|
-
}, fieldPerms, ["_no_role_"])
|
|
1623
|
+
}, fieldPerms, ["_no_role_"]);
|
|
1624
|
+
expect(result[field]).toBeUndefined();
|
|
1679
1625
|
});
|
|
1680
1626
|
if (perm.roles && perm.roles.length > 0) {
|
|
1681
1627
|
const writeRole = perm.roles[0];
|
|
1682
1628
|
it(`should allow writing field '${field}' by roles: ${[...perm.roles].join(", ")}`, () => {
|
|
1683
|
-
|
|
1629
|
+
const { body: result } = applyFieldWritePermissions({ [field]: "v" }, fieldPerms, [writeRole]);
|
|
1630
|
+
expect(result[field]).toBe("v");
|
|
1684
1631
|
});
|
|
1685
1632
|
}
|
|
1686
1633
|
break;
|
|
@@ -1796,7 +1743,7 @@ function runEventTests(resourceName, displayName, events) {
|
|
|
1796
1743
|
* ```
|
|
1797
1744
|
*/
|
|
1798
1745
|
async function createTestApp(options = {}) {
|
|
1799
|
-
const { createApp } = await import("../createApp-
|
|
1746
|
+
const { createApp } = await import("../createApp-CBJUJKGP.mjs").then((n) => n.r);
|
|
1800
1747
|
const { useInMemoryDb = true, mongoUri: providedMongoUri, ...appOptions } = options;
|
|
1801
1748
|
const defaultAuth = {
|
|
1802
1749
|
type: "jwt",
|
|
@@ -1994,4 +1941,4 @@ var TestDataLoader = class {
|
|
|
1994
1941
|
}
|
|
1995
1942
|
};
|
|
1996
1943
|
//#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 };
|
|
1944
|
+
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-BwGQXUpd.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
|
|
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-
|
|
5
|
-
export { AUTHENTICATED_SCOPE, ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap,
|
|
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 PresetHook, $t as DeleteResult, A as FastifyRequestExtras, At as BaseControllerOptions, B as IntrospectionData, Bt as IController, C as ControllerQueryOptions, Ct as UserLike, D as CrudSchemas, Dt as envelope, E as CrudRouterOptions, Et as ValidationResult, F as HealthCheck, G as MiddlewareConfig, H as JWTPayload, Ht as IRequestContext, I as HealthOptions, J as OpenApiSchemas, Jt as BulkWriteOperation, K as MiddlewareHandler, L as InferAdapterDoc, Lt as ControllerHandler, M as FastifyWithDecorators, N as FieldRule, O as EventDefinition, Ot as getUserId, P as GracefulShutdownOptions, Q as PresetFunction, Qt as DeleteOptions, R as InferDocType, Rt as ControllerLike, S as ConfigError, St as TypedResourceConfig, T as CrudRouteKey, Tt as ValidateOptions, U as JwtContext, Ut as RouteHandler, V as IntrospectionPluginOptions, Vt as IControllerResponse, W as LookupOption, X as ParsedQuery, Xt as CrudRepository, Y as OwnershipCheck, Yt as BulkWriteResult, Z as PopulateOption, Zt as DeleteManyResult, _ as ArcRequest, _t as RouteSchemaOptions, an as PaginationParams, at as RequestContext, b as Authenticator, bt as TypedController, cn as RepositorySession, ct as ResourceCacheConfig, d as ActionHandlerFn, dt as ResourceHooks, en as FindOneAndUpdateOptions, et as PresetResult, f as ActionsMap, ft as ResourceMetadata, g as ArcInternalMetadata, gt as RouteMcpConfig, h as ArcDecorator, ht as RouteHandlerMethod, in as PaginatedResult, it as RegistryStats, j as FastifyWithAuth, k as EventsDecorator, l as ActionDefinition, ln as UpdateManyResult, lt as ResourceConfig, m as ApiResponse, mt as RouteDefinition, nn as KeysetPaginatedResult, nt as RateLimitConfig, on as PaginationResult, ot as RequestIdOptions, p as AnyRecord, pt as ResourcePermissions, q as ObjectId, rn as OffsetPaginatedResult, rt as RegistryEntry, sn as QueryOptions, st as RequestWithExtras, tn as InferDoc, tt as QueryParserInterface, u as ActionEntry, un as WriteOptions, ut as ResourceHookContext, v as AuthHelpers, vt as ServiceContext, w as CrudController, wt as UserOrganization, x as AuthenticatorContext, xt as TypedRepository, y as AuthPluginOptions, yt as TokenPair, z as InferResourceDoc, zt as FastifyHandler } from "../interface-YrWsmKqE.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
|
+
export { AUTHENTICATED_SCOPE, ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, 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, FindOneAndUpdateOptions, 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,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ElevationOptions } from "./elevation-
|
|
1
|
+
import { b as Authenticator } from "./interface-YrWsmKqE.mjs";
|
|
2
|
+
import { n as ElevationOptions } from "./elevation-B6S5csVA.mjs";
|
|
3
3
|
import { t as ExternalOpenApiPaths } from "./externalPaths-Bapitwvd.mjs";
|
|
4
4
|
import { i as CacheStore } from "./interface-DplgQO2e.mjs";
|
|
5
5
|
import { r as QueryCachePluginOptions } from "./queryCachePlugin-CnTZZTC5.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import { t as EventPluginOptions } from "./eventPlugin-
|
|
8
|
-
import {
|
|
6
|
+
import { o as EventTransport } from "./EventTransport-CqZ8FyM_.mjs";
|
|
7
|
+
import { t as EventPluginOptions } from "./eventPlugin-BxvaCIZF.mjs";
|
|
8
|
+
import { a as VersioningOptions, g as CachingOptions, p as SSEOptions, t as ErrorHandlerOptions, u as MetricsOptions } from "./errorHandler-DixGcttC.mjs";
|
|
9
9
|
import { r as IdempotencyStore } from "./interface-B-pe8fhj.mjs";
|
|
10
10
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
|
|
11
11
|
|
|
@@ -409,6 +409,8 @@ interface CreateAppOptions {
|
|
|
409
409
|
debug?: boolean | string;
|
|
410
410
|
/** Trust proxy headers (X-Forwarded-For, etc.) */
|
|
411
411
|
trustProxy?: boolean;
|
|
412
|
+
/** Fastify plugin/onReady timeout in ms (default: 10_000). Raise for slow boot work (index materialisation, WAL replay, external warm-up). */
|
|
413
|
+
pluginTimeout?: number;
|
|
412
414
|
/**
|
|
413
415
|
* Auth configuration
|
|
414
416
|
*
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
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 ErrorDetails, s as OrgRequiredError, t as ArcError, u as UnauthorizedError } from "../errors-
|
|
1
|
+
import { J as OpenApiSchemas, X as ParsedQuery, p as AnyRecord, tt as QueryParserInterface } from "../interface-YrWsmKqE.mjs";
|
|
2
|
+
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 ErrorDetails, s as OrgRequiredError, t as ArcError, u as UnauthorizedError } from "../errors-BI8kEKsO.mjs";
|
|
3
3
|
import { a as CircuitBreakerStats, c as createCircuitBreakerRegistry, i as CircuitBreakerRegistry, n as CircuitBreakerError, o as CircuitState, r as CircuitBreakerOptions, s as createCircuitBreaker, t as CircuitBreaker } from "../circuitBreaker-CvXkjfrW.mjs";
|
|
4
4
|
import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
|
|
5
5
|
|
|
@@ -506,6 +506,14 @@ declare function getListQueryParams(): AnyRecord;
|
|
|
506
506
|
declare function getDefaultCrudSchemas(): Record<string, Record<string, unknown>>;
|
|
507
507
|
//#endregion
|
|
508
508
|
//#region src/utils/schemaConverter.d.ts
|
|
509
|
+
/**
|
|
510
|
+
* Supported JSON Schema output targets for Zod v4's `toJSONSchema()`.
|
|
511
|
+
* - `draft-7`: Fastify/AJV validation (default)
|
|
512
|
+
* - `draft-2020-12`: AJV 2020 (opt-in, requires ajv/dist/2020)
|
|
513
|
+
* - `openapi-3.0`: OpenAPI 3.0 document generation
|
|
514
|
+
* - `openapi-3.1`: OpenAPI 3.1 document generation
|
|
515
|
+
*/
|
|
516
|
+
type JsonSchemaTarget = "draft-7" | "draft-2020-12" | "openapi-3.0" | "openapi-3.1";
|
|
509
517
|
/**
|
|
510
518
|
* Check if an object is already a plain JSON Schema.
|
|
511
519
|
* Returns true if it has JSON Schema markers (`type`, `properties`, `$ref`,
|
|
@@ -522,15 +530,22 @@ declare function isZodSchema(input: unknown): boolean;
|
|
|
522
530
|
* Detection order:
|
|
523
531
|
* 1. `null`/`undefined` → `undefined`
|
|
524
532
|
* 2. Already JSON Schema → pass through as-is (zero overhead)
|
|
525
|
-
* 3. Zod v4 schema → `z.toJSONSchema(schema, { target
|
|
533
|
+
* 3. Zod v4 schema → `z.toJSONSchema(schema, { target })`
|
|
526
534
|
* 4. Unrecognized object → return as-is (treat as opaque schema)
|
|
535
|
+
*
|
|
536
|
+
* @param input Schema (Zod, plain JSON Schema, or opaque object)
|
|
537
|
+
* @param target Output target — defaults to `draft-7` for Fastify compatibility.
|
|
538
|
+
* Pass `openapi-3.0`/`openapi-3.1` for OpenAPI document generation.
|
|
527
539
|
*/
|
|
528
|
-
declare function toJsonSchema(input: unknown): Record<string, unknown> | undefined;
|
|
540
|
+
declare function toJsonSchema(input: unknown, target?: JsonSchemaTarget): Record<string, unknown> | undefined;
|
|
529
541
|
/**
|
|
530
542
|
* Convert all schema fields in an OpenApiSchemas object.
|
|
531
543
|
* JSON Schema values pass through unchanged. Only Zod schemas are converted.
|
|
544
|
+
*
|
|
545
|
+
* Defaults to the `openapi-3.0` target since this function feeds OpenAPI doc
|
|
546
|
+
* generation, not Fastify route validation.
|
|
532
547
|
*/
|
|
533
|
-
declare function convertOpenApiSchemas(schemas: OpenApiSchemas): OpenApiSchemas;
|
|
548
|
+
declare function convertOpenApiSchemas(schemas: OpenApiSchemas, target?: JsonSchemaTarget): OpenApiSchemas;
|
|
534
549
|
/**
|
|
535
550
|
* Convert schema values in a Fastify route schema record.
|
|
536
551
|
*
|
|
@@ -539,9 +554,12 @@ declare function convertOpenApiSchemas(schemas: OpenApiSchemas): OpenApiSchemas;
|
|
|
539
554
|
*
|
|
540
555
|
* JSON Schema values pass through unchanged. Only Zod schemas are converted.
|
|
541
556
|
*
|
|
542
|
-
* Used for both
|
|
557
|
+
* Used for both custom routes and customSchemas (CRUD overrides).
|
|
558
|
+
*
|
|
559
|
+
* Defaults to `draft-7` so Fastify v5's bundled AJV 8 accepts the output.
|
|
560
|
+
* Pass `openapi-3.0` (or `openapi-3.1`) when generating OpenAPI documents.
|
|
543
561
|
*/
|
|
544
|
-
declare function convertRouteSchema(schema: Record<string, unknown
|
|
562
|
+
declare function convertRouteSchema(schema: Record<string, unknown>, target?: JsonSchemaTarget): Record<string, unknown>;
|
|
545
563
|
//#endregion
|
|
546
564
|
//#region src/utils/stateMachine.d.ts
|
|
547
565
|
/**
|
|
@@ -673,4 +691,4 @@ declare function hasEvents(instance: FastifyInstance): instance is FastifyInstan
|
|
|
673
691
|
events: EventsDecorator;
|
|
674
692
|
};
|
|
675
693
|
//#endregion
|
|
676
|
-
export { ArcError, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, ConflictError, type ErrorDetails, type EventsDecorator, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, type StateMachine, type TransitionConfig, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineGuard, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, handleRaw, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
|
|
694
|
+
export { ArcError, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, ConflictError, type ErrorDetails, type EventsDecorator, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, type JsonSchemaTarget, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, type StateMachine, type TransitionConfig, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineGuard, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, handleRaw, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
3
|
-
import { a as createCircuitBreaker, i as CircuitState, n as CircuitBreakerError, o as createCircuitBreakerRegistry, r as CircuitBreakerRegistry, t as CircuitBreaker } from "../circuitBreaker-
|
|
4
|
-
import { _ as withCompensation, a as getListQueryParams, c as mutationResponse, d as responses, f as successResponseSchema, g as defineCompensation, h as defineGuard, i as getDefaultCrudSchemas, l as paginationSchema, m as handleRaw, n as deleteResponse, o as itemResponse, p as wrapResponse, r as errorResponseSchema, s as listResponse, t as createStateMachine, u as queryParams } from "../utils-
|
|
5
|
-
import { a as
|
|
6
|
-
import { t as hasEvents } from "../typeGuards-
|
|
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-CqWnSqM-.mjs";
|
|
2
|
+
import { n as createQueryParser, t as ArcQueryParser } from "../queryParser-Cs-6SHQK.mjs";
|
|
3
|
+
import { a as createCircuitBreaker, i as CircuitState, n as CircuitBreakerError, o as createCircuitBreakerRegistry, r as CircuitBreakerRegistry, t as CircuitBreaker } from "../circuitBreaker-l18oRgL5.mjs";
|
|
4
|
+
import { _ as withCompensation, a as getListQueryParams, c as mutationResponse, d as responses, f as successResponseSchema, g as defineCompensation, h as defineGuard, i as getDefaultCrudSchemas, l as paginationSchema, m as handleRaw, n as deleteResponse, o as itemResponse, p as wrapResponse, r as errorResponseSchema, s as listResponse, t as createStateMachine, u as queryParams } from "../utils-B7FuRr9w.mjs";
|
|
5
|
+
import { a as toJsonSchema, i as isZodSchema, n as convertRouteSchema, r as isJsonSchema, t as convertOpenApiSchemas } from "../schemaConverter-BxFDdtXu.mjs";
|
|
6
|
+
import { t as hasEvents } from "../typeGuards-Cj5Rgvlg.mjs";
|
|
7
7
|
export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineGuard, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, handleRaw, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
|