@classytic/arc 2.2.5 → 2.4.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 +187 -18
- package/bin/arc.js +11 -3
- package/dist/BaseController-CkM5dUh_.mjs +1031 -0
- package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
- package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
- package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
- package/dist/adapters/index.d.mts +3 -5
- package/dist/adapters/index.mjs +2 -3
- package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
- package/dist/audit/index.d.mts +4 -7
- package/dist/audit/index.mjs +2 -29
- package/dist/audit/mongodb.d.mts +1 -4
- package/dist/audit/mongodb.mjs +2 -3
- package/dist/auth/index.d.mts +7 -9
- package/dist/auth/index.mjs +65 -63
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/auth/redis-session.mjs +1 -2
- package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
- package/dist/cache/index.d.mts +23 -23
- package/dist/cache/index.mjs +4 -6
- package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
- package/dist/chunk-BpYLSNr0.mjs +14 -0
- package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
- package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
- package/dist/cli/commands/describe.mjs +24 -7
- package/dist/cli/commands/docs.mjs +6 -7
- package/dist/cli/commands/doctor.d.mts +10 -0
- package/dist/cli/commands/doctor.mjs +156 -0
- package/dist/cli/commands/generate.mjs +66 -17
- package/dist/cli/commands/init.mjs +315 -45
- package/dist/cli/commands/introspect.mjs +2 -4
- package/dist/cli/index.d.mts +1 -10
- package/dist/cli/index.mjs +4 -153
- package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
- package/dist/core/index.d.mts +3 -5
- package/dist/core/index.mjs +5 -4
- package/dist/core-C1XCMtqM.mjs +185 -0
- package/dist/{createApp-BKHSl2nT.mjs → createApp-ByWNRsZj.mjs} +65 -36
- package/dist/{defineResource-DO9ONe_D.mjs → defineResource-D9aY5Cy6.mjs} +154 -1165
- package/dist/discovery/index.mjs +37 -5
- package/dist/docs/index.d.mts +6 -9
- package/dist/docs/index.mjs +3 -21
- package/dist/dynamic/index.d.mts +93 -0
- package/dist/dynamic/index.mjs +122 -0
- package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
- package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
- package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
- package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
- package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
- package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
- package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
- package/dist/events/index.d.mts +72 -7
- package/dist/events/index.mjs +216 -4
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +19 -7
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +3 -4
- package/dist/factory/index.d.mts +23 -9
- package/dist/factory/index.mjs +48 -3
- package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
- package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
- package/dist/hooks/index.d.mts +1 -3
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +5 -5
- package/dist/idempotency/index.mjs +3 -7
- package/dist/idempotency/mongodb.d.mts +1 -1
- package/dist/idempotency/mongodb.mjs +4 -5
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +2 -5
- package/dist/{fastifyAdapter-CyAA2zlB.d.mts → index-BL8CaQih.d.mts} +56 -57
- package/dist/index-Diqcm14c.d.mts +369 -0
- package/dist/{prisma-xjhMEq_S.d.mts → index-yhxyjqNb.d.mts} +4 -5
- package/dist/index.d.mts +100 -105
- package/dist/index.mjs +85 -58
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +8 -4
- package/dist/integrations/index.d.mts +4 -2
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs.d.mts +2 -2
- package/dist/integrations/jobs.mjs +63 -14
- package/dist/integrations/mcp/index.d.mts +219 -0
- package/dist/integrations/mcp/index.mjs +572 -0
- package/dist/integrations/mcp/testing.d.mts +53 -0
- package/dist/integrations/mcp/testing.mjs +104 -0
- package/dist/integrations/streamline.mjs +39 -19
- package/dist/integrations/webhooks.d.mts +56 -0
- package/dist/integrations/webhooks.mjs +139 -0
- package/dist/integrations/websocket-redis.d.mts +46 -0
- package/dist/integrations/websocket-redis.mjs +50 -0
- package/dist/integrations/websocket.d.mts +68 -2
- package/dist/integrations/websocket.mjs +96 -13
- package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
- package/dist/interface-DGmPxakH.d.mts +2213 -0
- package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
- package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
- package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
- package/dist/metrics-Csh4nsvv.mjs +224 -0
- package/dist/migrations/index.mjs +3 -7
- package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
- package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
- package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
- package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
- package/dist/org/index.d.mts +12 -14
- package/dist/org/index.mjs +92 -119
- package/dist/org/types.d.mts +2 -2
- package/dist/org/types.mjs +1 -1
- package/dist/permissions/index.d.mts +4 -278
- package/dist/permissions/index.mjs +4 -579
- package/dist/permissions-CA5zg0yK.mjs +751 -0
- package/dist/plugins/index.d.mts +104 -107
- package/dist/plugins/index.mjs +203 -313
- package/dist/plugins/response-cache.mjs +4 -69
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +24 -11
- package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
- package/dist/policies/index.d.mts +2 -2
- package/dist/policies/index.mjs +80 -83
- package/dist/presets/index.d.mts +26 -19
- package/dist/presets/index.mjs +2 -142
- package/dist/presets/multiTenant.d.mts +1 -4
- package/dist/presets/multiTenant.mjs +4 -6
- package/dist/presets-C9QXJV1u.mjs +422 -0
- package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
- package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
- package/dist/queryParser-CgCtsjti.mjs +352 -0
- package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
- package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
- package/dist/registry/index.d.mts +1 -4
- package/dist/registry/index.mjs +3 -4
- package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
- package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
- package/dist/resourceToTools-B6ZN9Ing.mjs +489 -0
- package/dist/rpc/index.d.mts +90 -0
- package/dist/rpc/index.mjs +248 -0
- package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
- package/dist/schemas/index.d.mts +30 -30
- package/dist/schemas/index.mjs +4 -6
- package/dist/scope/index.d.mts +13 -2
- package/dist/scope/index.mjs +18 -5
- package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
- package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
- package/dist/testing/index.d.mts +551 -567
- package/dist/testing/index.mjs +1744 -1799
- package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
- package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
- package/dist/types/index.d.mts +4 -946
- package/dist/types/index.mjs +2 -4
- package/dist/types-BJmgxNbF.d.mts +275 -0
- package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
- package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
- package/dist/{types-DMSBMkaZ.d.mts → types-Dt0-AI6E.d.mts} +85 -27
- package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
- package/dist/utils/index.d.mts +255 -352
- package/dist/utils/index.mjs +7 -6
- package/dist/utils-Dc0WhlIl.mjs +594 -0
- package/dist/versioning-BzfeHmhj.mjs +37 -0
- package/package.json +46 -12
- package/skills/arc/SKILL.md +506 -0
- package/skills/arc/references/auth.md +250 -0
- package/skills/arc/references/events.md +272 -0
- package/skills/arc/references/integrations.md +385 -0
- package/skills/arc/references/mcp.md +386 -0
- package/skills/arc/references/production.md +610 -0
- package/skills/arc/references/testing.md +183 -0
- package/dist/audited-CGdLiSlE.mjs +0 -140
- package/dist/chunk-C7Uep-_p.mjs +0 -20
- package/dist/circuitBreaker-DYhWBW_D.mjs +0 -1096
- package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
- package/dist/interface-DZYNK9bb.d.mts +0 -1112
- package/dist/presets-BTeYbw7h.d.mts +0 -57
- package/dist/presets-CeFtfDR8.mjs +0 -119
- /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
- /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
- /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Arc Testing Utilities
|
|
2
|
+
|
|
3
|
+
In-memory MongoDB, test app creation, mocks, data factories, and test harness.
|
|
4
|
+
|
|
5
|
+
## createTestApp()
|
|
6
|
+
|
|
7
|
+
Creates an isolated Fastify instance with in-memory MongoDB:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -D mongodb-memory-server
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createTestApp } from '@classytic/arc/testing';
|
|
15
|
+
import type { TestAppResult } from '@classytic/arc/testing';
|
|
16
|
+
|
|
17
|
+
describe('API Tests', () => {
|
|
18
|
+
let testApp: TestAppResult;
|
|
19
|
+
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
testApp = await createTestApp({
|
|
22
|
+
auth: { type: 'jwt', jwt: { secret: 'test-secret-32-chars-minimum-len' } },
|
|
23
|
+
// All security plugins disabled by default in testing preset
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Connect models to in-memory DB
|
|
27
|
+
await mongoose.connect(testApp.mongoUri);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterAll(async () => {
|
|
31
|
+
await testApp.close(); // Cleans up DB + closes app
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('GET /products', async () => {
|
|
35
|
+
const response = await testApp.app.inject({
|
|
36
|
+
method: 'GET',
|
|
37
|
+
url: '/products',
|
|
38
|
+
});
|
|
39
|
+
expect(response.statusCode).toBe(200);
|
|
40
|
+
expect(response.json().success).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('POST /products (authenticated)', async () => {
|
|
44
|
+
const token = testApp.app.jwt.sign({ _id: 'user-1', role: ['admin'] });
|
|
45
|
+
|
|
46
|
+
const response = await testApp.app.inject({
|
|
47
|
+
method: 'POST',
|
|
48
|
+
url: '/products',
|
|
49
|
+
headers: { authorization: `Bearer ${token}` },
|
|
50
|
+
payload: { name: 'Test Product', price: 99 },
|
|
51
|
+
});
|
|
52
|
+
expect(response.statusCode).toBe(201);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### External MongoDB
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const testApp = await createTestApp({
|
|
61
|
+
auth: { type: 'jwt', jwt: { secret: 'test-secret-32-chars-minimum-len' } },
|
|
62
|
+
useInMemoryDb: false,
|
|
63
|
+
mongoUri: 'mongodb://localhost:27017/test-db',
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## TestHarness
|
|
68
|
+
|
|
69
|
+
Full lifecycle test helper — setup, fixtures, assertions, teardown:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { TestHarness } from '@classytic/arc/testing';
|
|
73
|
+
|
|
74
|
+
const harness = new TestHarness({
|
|
75
|
+
auth: { type: 'jwt', jwt: { secret: 'test-secret-32-chars-minimum-len' } },
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Product API', () => {
|
|
79
|
+
beforeAll(() => harness.setup());
|
|
80
|
+
afterAll(() => harness.teardown());
|
|
81
|
+
afterEach(() => harness.cleanup()); // Clear collections between tests
|
|
82
|
+
|
|
83
|
+
test('full CRUD', async () => {
|
|
84
|
+
// Create
|
|
85
|
+
const created = await harness.inject('POST', '/products', {
|
|
86
|
+
body: { name: 'Widget', price: 10 },
|
|
87
|
+
auth: { _id: 'user-1', role: ['admin'] },
|
|
88
|
+
});
|
|
89
|
+
expect(created.statusCode).toBe(201);
|
|
90
|
+
|
|
91
|
+
// Read
|
|
92
|
+
const fetched = await harness.inject('GET', `/products/${created.json().data._id}`);
|
|
93
|
+
expect(fetched.json().data.name).toBe('Widget');
|
|
94
|
+
|
|
95
|
+
// Update
|
|
96
|
+
const updated = await harness.inject('PATCH', `/products/${created.json().data._id}`, {
|
|
97
|
+
body: { price: 15 },
|
|
98
|
+
auth: { _id: 'user-1', role: ['admin'] },
|
|
99
|
+
});
|
|
100
|
+
expect(updated.json().data.price).toBe(15);
|
|
101
|
+
|
|
102
|
+
// Delete
|
|
103
|
+
const deleted = await harness.inject('DELETE', `/products/${created.json().data._id}`, {
|
|
104
|
+
auth: { _id: 'user-1', role: ['admin'] },
|
|
105
|
+
});
|
|
106
|
+
expect(deleted.statusCode).toBe(200);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Mock Repository
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { createMockRepository } from '@classytic/arc/testing';
|
|
115
|
+
|
|
116
|
+
const mockRepo = createMockRepository({
|
|
117
|
+
findById: jest.fn().mockResolvedValue({ _id: '123', name: 'Test' }),
|
|
118
|
+
findAll: jest.fn().mockResolvedValue({ docs: [], total: 0 }),
|
|
119
|
+
create: jest.fn().mockResolvedValue({ _id: '123', name: 'New' }),
|
|
120
|
+
update: jest.fn().mockResolvedValue({ _id: '123', name: 'Updated' }),
|
|
121
|
+
delete: jest.fn().mockResolvedValue(true),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Use in tests
|
|
125
|
+
const controller = new ProductController(mockRepo);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Data Factory
|
|
129
|
+
|
|
130
|
+
Generate test fixtures:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { createDataFactory } from '@classytic/arc/testing';
|
|
134
|
+
|
|
135
|
+
const productFactory = createDataFactory({
|
|
136
|
+
name: (i) => `Product ${i}`,
|
|
137
|
+
price: (i) => 100 + i * 10,
|
|
138
|
+
isActive: () => true,
|
|
139
|
+
category: () => 'electronics',
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const product = productFactory.build(); // { name: 'Product 1', price: 110, ... }
|
|
143
|
+
const products = productFactory.buildMany(5); // 5 products
|
|
144
|
+
const custom = productFactory.build({ price: 0 }); // Override specific fields
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Database Helpers
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { withTestDb } from '@classytic/arc/testing';
|
|
151
|
+
|
|
152
|
+
describe('Repository', () => {
|
|
153
|
+
withTestDb((db) => {
|
|
154
|
+
// db.uri — MongoDB connection string
|
|
155
|
+
// db.cleanup() — Clear all collections
|
|
156
|
+
|
|
157
|
+
test('create and find', async () => {
|
|
158
|
+
await mongoose.connect(db.uri);
|
|
159
|
+
const product = await Product.create({ name: 'Test' });
|
|
160
|
+
expect(product.name).toBe('Test');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Testing Preset
|
|
167
|
+
|
|
168
|
+
When using `createTestApp()` or `createApp({ preset: 'testing' })`:
|
|
169
|
+
|
|
170
|
+
- Silent logging (no noise)
|
|
171
|
+
- No CORS restrictions
|
|
172
|
+
- Rate limiting disabled
|
|
173
|
+
- Minimal security overhead
|
|
174
|
+
- In-memory MongoDB (10x faster than external)
|
|
175
|
+
- No health monitoring
|
|
176
|
+
|
|
177
|
+
## Tips
|
|
178
|
+
|
|
179
|
+
1. **Use `app.inject()`** — No real HTTP, fastest possible
|
|
180
|
+
2. **Issue tokens via `app.jwt.sign()`** — Don't mock auth, test the real flow
|
|
181
|
+
3. **Use `afterEach` cleanup** — Clear collections between tests for isolation
|
|
182
|
+
4. **Use data factories** — Consistent, reproducible test data
|
|
183
|
+
5. **Test permissions** — Verify 401/403 responses with wrong/missing tokens
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { c as isElevated, n as PUBLIC_SCOPE } from "./types-Beqn1Un7.mjs";
|
|
2
|
-
import { allowPublic, requireRoles } from "./permissions/index.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/presets/softDelete.ts
|
|
5
|
-
function softDeletePreset() {
|
|
6
|
-
return {
|
|
7
|
-
name: "softDelete",
|
|
8
|
-
additionalRoutes: (permissions) => [{
|
|
9
|
-
method: "GET",
|
|
10
|
-
path: "/deleted",
|
|
11
|
-
handler: "getDeleted",
|
|
12
|
-
summary: "Get soft-deleted items",
|
|
13
|
-
permissions: permissions.list ?? requireRoles(["admin"]),
|
|
14
|
-
wrapHandler: true,
|
|
15
|
-
operation: "listDeleted"
|
|
16
|
-
}, {
|
|
17
|
-
method: "POST",
|
|
18
|
-
path: "/:id/restore",
|
|
19
|
-
handler: "restore",
|
|
20
|
-
summary: "Restore soft-deleted item",
|
|
21
|
-
permissions: permissions.update ?? requireRoles(["admin"]),
|
|
22
|
-
wrapHandler: true,
|
|
23
|
-
operation: "restore"
|
|
24
|
-
}]
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
//#endregion
|
|
29
|
-
//#region src/presets/slugLookup.ts
|
|
30
|
-
function slugLookupPreset(options = {}) {
|
|
31
|
-
const { slugField = "slug" } = options;
|
|
32
|
-
return {
|
|
33
|
-
name: "slugLookup",
|
|
34
|
-
additionalRoutes: (permissions) => [{
|
|
35
|
-
method: "GET",
|
|
36
|
-
path: `/slug/:${slugField}`,
|
|
37
|
-
handler: "getBySlug",
|
|
38
|
-
summary: "Get by slug",
|
|
39
|
-
permissions: permissions.get ?? allowPublic(),
|
|
40
|
-
wrapHandler: true,
|
|
41
|
-
operation: "getBySlug"
|
|
42
|
-
}],
|
|
43
|
-
controllerOptions: { slugField }
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
//#endregion
|
|
48
|
-
//#region src/presets/ownedByUser.ts
|
|
49
|
-
/**
|
|
50
|
-
* Create ownership check middleware.
|
|
51
|
-
* Elevated scope (platform admin) bypasses ownership checks.
|
|
52
|
-
*/
|
|
53
|
-
function createOwnershipCheck(ownerField) {
|
|
54
|
-
return async (request, _reply) => {
|
|
55
|
-
const user = request.user;
|
|
56
|
-
if (!user) return;
|
|
57
|
-
if (isElevated(request.scope ?? PUBLIC_SCOPE)) return;
|
|
58
|
-
const userWithId = user;
|
|
59
|
-
const userId = userWithId._id ?? userWithId.id;
|
|
60
|
-
if (userId) request._ownershipCheck = {
|
|
61
|
-
field: ownerField,
|
|
62
|
-
userId
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function ownedByUserPreset(options = {}) {
|
|
67
|
-
const { ownerField = "userId" } = options;
|
|
68
|
-
const ownershipMiddleware = createOwnershipCheck(ownerField);
|
|
69
|
-
return {
|
|
70
|
-
name: "ownedByUser",
|
|
71
|
-
middlewares: {
|
|
72
|
-
update: [ownershipMiddleware],
|
|
73
|
-
delete: [ownershipMiddleware]
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
//#endregion
|
|
79
|
-
//#region src/presets/tree.ts
|
|
80
|
-
function treePreset(options = {}) {
|
|
81
|
-
const { parentField = "parent" } = options;
|
|
82
|
-
return {
|
|
83
|
-
name: "tree",
|
|
84
|
-
additionalRoutes: (permissions) => [{
|
|
85
|
-
method: "GET",
|
|
86
|
-
path: "/tree",
|
|
87
|
-
handler: "getTree",
|
|
88
|
-
summary: "Get hierarchical tree",
|
|
89
|
-
permissions: permissions.list ?? allowPublic(),
|
|
90
|
-
wrapHandler: true,
|
|
91
|
-
operation: "getTree"
|
|
92
|
-
}, {
|
|
93
|
-
method: "GET",
|
|
94
|
-
path: `/:${parentField}/children`,
|
|
95
|
-
handler: "getChildren",
|
|
96
|
-
summary: "Get children of parent",
|
|
97
|
-
permissions: permissions.list ?? allowPublic(),
|
|
98
|
-
wrapHandler: true,
|
|
99
|
-
operation: "getChildren"
|
|
100
|
-
}],
|
|
101
|
-
controllerOptions: { parentField }
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
//#endregion
|
|
106
|
-
//#region src/presets/audited.ts
|
|
107
|
-
/**
|
|
108
|
-
* Audited preset - adds createdBy/updatedBy tracking
|
|
109
|
-
*/
|
|
110
|
-
function auditedPreset(options = {}) {
|
|
111
|
-
const { createdByField = "createdBy", updatedByField = "updatedBy" } = options;
|
|
112
|
-
const injectCreatedBy = async (request, _reply) => {
|
|
113
|
-
const userWithId = request.user;
|
|
114
|
-
if (userWithId?._id || userWithId?.id) {
|
|
115
|
-
const userId = userWithId._id ?? userWithId.id;
|
|
116
|
-
request.body[createdByField] = userId;
|
|
117
|
-
request.body[updatedByField] = userId;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
const injectUpdatedBy = async (request, _reply) => {
|
|
121
|
-
const userWithId = request.user;
|
|
122
|
-
if (userWithId?._id || userWithId?.id) request.body[updatedByField] = userWithId._id ?? userWithId.id;
|
|
123
|
-
};
|
|
124
|
-
return {
|
|
125
|
-
name: "audited",
|
|
126
|
-
schemaOptions: { fieldRules: {
|
|
127
|
-
[createdByField]: { systemManaged: true },
|
|
128
|
-
[updatedByField]: { systemManaged: true },
|
|
129
|
-
createdAt: { systemManaged: true },
|
|
130
|
-
updatedAt: { systemManaged: true }
|
|
131
|
-
} },
|
|
132
|
-
middlewares: {
|
|
133
|
-
create: [injectCreatedBy],
|
|
134
|
-
update: [injectUpdatedBy]
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
//#endregion
|
|
140
|
-
export { softDeletePreset as a, slugLookupPreset as i, treePreset as n, ownedByUserPreset as r, auditedPreset as t };
|
package/dist/chunk-C7Uep-_p.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
//#region \0rolldown/runtime.js
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __exportAll = (all, no_symbols) => {
|
|
6
|
-
let target = {};
|
|
7
|
-
for (var name in all) {
|
|
8
|
-
__defProp(target, name, {
|
|
9
|
-
get: all[name],
|
|
10
|
-
enumerable: true
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
if (!no_symbols) {
|
|
14
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
15
|
-
}
|
|
16
|
-
return target;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
//#endregion
|
|
20
|
-
export { __exportAll as t };
|