@classytic/arc 2.19.0 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/arc.js +37 -0
- package/dist/{BaseController-Dy6EQHjM.mjs → BaseController-BnLYgE9A.mjs} +4 -4
- package/dist/{EventTransport-IEn7Tn-Q.d.mts → EventTransport-QmY7A258.d.mts} +0 -1
- package/dist/{QueryCache-TRfXau3W.d.mts → QueryCache-Z1B38koh.d.mts} +0 -1
- package/dist/{ResourceRegistry-5fKZwhK_.mjs → ResourceRegistry-7RnrEWNP.mjs} +1 -1
- package/dist/{actionPermissions-D7NGwcAR.mjs → actionPermissions-ze70xXJi.mjs} +1 -1
- package/dist/audit/index.d.mts +9 -5
- package/dist/audit/index.mjs +1 -1
- package/dist/auth/audit.d.mts +7 -4
- package/dist/auth/audit.mjs +5 -0
- package/dist/auth/index.d.mts +7 -14
- package/dist/auth/index.mjs +7 -5
- package/dist/auth/redis-session.d.mts +1 -2
- package/dist/{buildHandler-B0qvAguf.mjs → buildHandler-D_2ULWxZ.mjs} +3 -3
- package/dist/cache/index.d.mts +2 -3
- package/dist/cache/index.mjs +2 -2
- package/dist/cli/commands/describe.d.mts +1 -2
- package/dist/cli/commands/describe.mjs +2 -2
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +4 -13
- package/dist/cli/index.mjs +2 -2
- package/dist/{constants-gaEeMlz-.mjs → constants-DNre0wRg.mjs} +1 -1
- package/dist/context/index.d.mts +2 -2
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -5
- package/dist/{core-Ba-6Sue6.mjs → core-DZ6Xe1jR.mjs} +182 -55
- package/dist/{createActionRouter-B_WzBFyf.mjs → createActionRouter-BWMDW-dL.mjs} +4 -4
- package/dist/{createAggregationRouter-B_G_nIjm.mjs → createAggregationRouter-l5mNdYrZ.mjs} +5 -4
- package/dist/{createApp--703hQE1.mjs → createApp-Bi61XxqU.mjs} +229 -24
- package/dist/discovery/index.d.mts +0 -1
- package/dist/docs/index.d.mts +1 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/{elevation-xGUBq6p_.d.mts → elevation-C9s5hFP_.d.mts} +0 -1
- package/dist/encryption/index.d.mts +2 -144
- package/dist/{errorHandler-BtUUDPEa.d.mts → errorHandler-B0e-bq-O.d.mts} +0 -1
- package/dist/{errors-DOHkjQlD.d.mts → errors-DHcHVAgk.d.mts} +0 -1
- package/dist/{eventPlugin-DbBFd3x_.d.mts → eventPlugin-A6EbthXR.d.mts} +10 -6
- package/dist/events/index.d.mts +9 -6
- package/dist/events/index.mjs +9 -6
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -2
- package/dist/factory/index.d.mts +5 -4
- package/dist/factory/index.mjs +3 -3
- package/dist/{fastify-augmentation-D-5_yQJi.d.mts → fastify-augmentation-DafigGQp.d.mts} +12 -2
- package/dist/{fields-C7ahdGYf.d.mts → fields-BGqrAa_u.d.mts} +1 -2
- package/dist/{generate-Brans3fW.mjs → generate-usRSdQmP.mjs} +4 -3
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +4 -3
- package/dist/idempotency/index.mjs +1 -1
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-CP6A1Ffj.d.mts → index-B5N-NaHs.d.mts} +88 -7
- package/dist/{index-CRKiiP5o.d.mts → index-B5PoNbfu.d.mts} +41 -7
- package/dist/{index-CUTG2Cuo.d.mts → index-Dtrj6XZk.d.mts} +90 -29
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +7 -7
- package/dist/{init-Bqpt1Hxf.mjs → init-0YhHkXGk.mjs} +239 -140
- package/dist/integrations/event-gateway.d.mts +2 -3
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/jobs.d.mts +0 -1
- package/dist/integrations/mcp/index.d.mts +6 -7
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -2
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/streamline.d.mts +19 -7
- package/dist/integrations/streamline.mjs +9 -5
- package/dist/integrations/webhooks.d.mts +0 -1
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -2
- package/dist/integrations/websocket-redis.d.mts +1 -2
- package/dist/integrations/websocket.d.mts +2 -2
- package/dist/{loadResources-B8UmpITt.mjs → loadResources-DpU8pfjV.mjs} +4 -3
- package/dist/{loadResourcesFromEntry-BTlFEx05.mjs → loadResourcesFromEntry-OMCvIEE7.mjs} +1 -1
- package/dist/{mcpPlugin-C088Nd-G.mjs → mcpPlugin-B9XjjNVG.mjs} +148 -49
- package/dist/middleware/index.d.mts +1 -2
- package/dist/middleware/index.mjs +1 -1
- package/dist/module-WeuU5jfd.d.mts +429 -0
- package/dist/{openapi-DZ-5a5Op.mjs → openapi-Kyod_di6.mjs} +3 -3
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-BPB1xqGf.mjs → permissions-BJMuKMl7.mjs} +43 -3
- package/dist/pipeline/index.d.mts +1 -2
- package/dist/plugins/index.d.mts +87 -10
- package/dist/plugins/index.mjs +91 -4
- package/dist/plugins/response-cache.d.mts +5 -3
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{pluralize-DccFV9R7.mjs → pluralize-BhrgwZb9.mjs} +3 -3
- package/dist/presets/filesUpload.d.mts +4 -5
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +16 -3
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -2
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/presets/search.d.mts +2 -3
- package/dist/presets/search.mjs +3 -2
- package/dist/{presets-BtyFwTO4.mjs → presets-gw0ciIUD.mjs} +1 -1
- package/dist/{pushref-registry-DnCqL3aa.d.mts → pushref-registry-BbcnpNTm.d.mts} +4 -2
- package/dist/{queryCachePlugin-xzFyKAjT.d.mts → queryCachePlugin-CfYWq-Ak.d.mts} +1 -2
- package/dist/{queryCachePlugin-emHb0CuX.mjs → queryCachePlugin-DzqLN904.mjs} +2 -2
- package/dist/{redis-C3W0qbz8.d.mts → redis-UDtLaNj6.d.mts} +0 -1
- package/dist/{redis-stream-DsP1vqZB.d.mts → redis-stream-iVR2l7aS.d.mts} +1 -2
- package/dist/{registerRoute-BdV8DDwU.mjs → registerRoute-oGAl0oA8.mjs} +1 -1
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +2 -2
- package/dist/{registry-Bm9LjWKs.mjs → registry-vcOCtPij.mjs} +1 -1
- package/dist/{schemaIR-D1MJwqmp.mjs → schemaIR-ajetw3Fv.mjs} +1 -42
- package/dist/schemas/index.d.mts +0 -1
- package/dist/scim/index.d.mts +0 -1
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +2 -3
- package/dist/{sessionManager-BXG-HGwG.d.mts → sessionManager-BHOM3N_0.d.mts} +10 -6
- package/dist/{storage-Co_PQpeQ.d.mts → storage-DLUZ04kK.d.mts} +8 -4
- package/dist/sync/changelog.d.mts +2 -0
- package/dist/sync/changelog.mjs +2 -0
- package/dist/testing/index.d.mts +2 -3
- package/dist/testing/index.mjs +6 -3
- package/dist/testing/storageContract.d.mts +1 -2
- package/dist/{tracing-D8jlGXP7.d.mts → tracing-CsGKQJGg.d.mts} +0 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/types-Cc91nXuA.d.mts +132 -0
- package/dist/{types-LNebCoJx.d.mts → types-DH0GupI7.d.mts} +8 -4
- package/dist/{types-DpvSF5Hh.d.mts → types-DRj-Ut4n.d.mts} +1 -2
- package/dist/{types-9_wtNPsI.d.mts → types-DpxoNX5v.d.mts} +112 -214
- package/dist/utils/index.d.mts +182 -128
- package/dist/utils/index.mjs +2 -2
- package/dist/{utils-C6QQ9-hd.mjs → utils-CO8a3B_1.mjs} +1435 -1391
- package/dist/{validate-CRcaWzp2.mjs → validate-C2mGgSXg.mjs} +3 -3
- package/dist/{versioning-D_eBCfNr.d.mts → versioning-DQ4mCvg0.d.mts} +1 -2
- package/dist/{websocket-MlYlrYq0.d.mts → websocket-uuWqM6UY.d.mts} +5 -4
- package/package.json +18 -13
- package/skills/arc/SKILL.md +10 -0
- package/skills/arc/references/mcp.md +2 -0
- package/skills/arc-module-publishing/SKILL.md +242 -0
- package/skills/arc-module-publishing/references/authoring-modules.md +272 -0
- package/skills/arc-module-publishing/references/package-manifest.md +155 -0
- package/skills/arc-module-publishing/references/publishing-workflow.md +182 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { accessSync, existsSync, readFileSync } from "node:fs";
|
|
4
6
|
import * as readline from "node:readline";
|
|
5
7
|
import { execSync, spawn } from "node:child_process";
|
|
6
8
|
//#region src/cli/commands/init/templates/adapter.ts
|
|
@@ -15,7 +17,7 @@ function createAdapterTemplate(config) {
|
|
|
15
17
|
|
|
16
18
|
import { createMongooseAdapter } from '@classytic/mongokit/adapter';
|
|
17
19
|
import { buildCrudSchemasFromModel } from '@classytic/mongokit';
|
|
18
|
-
${ts ? "import type { Model } from 'mongoose';\nimport type { Repository } from '@classytic/mongokit';" : ""}
|
|
20
|
+
${ts ? "import type { DataAdapter } from '@classytic/repo-core/adapter';\nimport type { Model } from 'mongoose';\nimport type { Repository } from '@classytic/mongokit';" : ""}
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* Create a MongoKit-powered adapter for a resource.
|
|
@@ -28,7 +30,10 @@ ${ts ? "import type { Model } from 'mongoose';\nimport type { Repository } from
|
|
|
28
30
|
export function createAdapter${ts ? "<TDoc = unknown>" : ""}(
|
|
29
31
|
model${ts ? ": Model<TDoc>" : ""},
|
|
30
32
|
repository${ts ? ": Repository<TDoc>" : ""}
|
|
31
|
-
) {
|
|
33
|
+
)${ts ? ": DataAdapter<TDoc>" : ""} {
|
|
34
|
+
// Explicit return type keeps the declaration portable — under pnpm's
|
|
35
|
+
// non-hoisted layout the inferred type references mongokit's internal
|
|
36
|
+
// paths and tsc fails with TS2742/TS2883.
|
|
32
37
|
return createMongooseAdapter({
|
|
33
38
|
model,
|
|
34
39
|
repository,
|
|
@@ -482,20 +487,13 @@ ${[
|
|
|
482
487
|
].filter(Boolean).join("\n")}
|
|
483
488
|
import config from '#config/index.js';
|
|
484
489
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
export function getAuth()${ts ? ": ReturnType<typeof betterAuth>" : ""} {
|
|
493
|
-
if (process.env.NODE_ENV === 'production' && !process.env.BETTER_AUTH_SECRET) {
|
|
494
|
-
throw new Error('BETTER_AUTH_SECRET is required in production (min 32 chars)');
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
if (!_auth) {
|
|
498
|
-
_auth = betterAuth({
|
|
490
|
+
// Factory keeps the CONCRETE betterAuth({...}) instantiation as the source
|
|
491
|
+
// of truth for the instance type — annotating with the wide
|
|
492
|
+
// \`Auth<BetterAuthOptions>\` (aka \`ReturnType<typeof betterAuth>\`) does not
|
|
493
|
+
// typecheck because Better Auth's \`Auth<T>\` is generic over the exact
|
|
494
|
+
// options object.
|
|
495
|
+
function createAuth() {
|
|
496
|
+
return betterAuth({
|
|
499
497
|
secret: config.betterAuth.secret,
|
|
500
498
|
baseURL: process.env.BETTER_AUTH_URL || \`http://localhost:\${config.server.port}\`,
|
|
501
499
|
basePath: '/api/auth',
|
|
@@ -543,6 +541,26 @@ ${orgPluginUsage}
|
|
|
543
541
|
enabled: process.env.NODE_ENV === 'production',
|
|
544
542
|
},
|
|
545
543
|
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
let _auth${ts ? ": ReturnType<typeof createAuth> | null" : ""} = null;
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Get the Better Auth instance (lazy singleton)
|
|
550
|
+
*
|
|
551
|
+
* Must be called AFTER database connection is established.
|
|
552
|
+
*/
|
|
553
|
+
export function getAuth()${ts ? ": ReturnType<typeof createAuth>" : ""} {
|
|
554
|
+
if (process.env.NODE_ENV === 'production' && !process.env.BETTER_AUTH_SECRET) {
|
|
555
|
+
throw new Error('BETTER_AUTH_SECRET is required in production (min 32 chars)');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Narrow through a local — assigning the module-level \`_auth\` directly
|
|
559
|
+
// would lose the non-null narrowing across the calls below.
|
|
560
|
+
let auth = _auth;
|
|
561
|
+
if (!auth) {
|
|
562
|
+
auth = createAuth();
|
|
563
|
+
_auth = auth;
|
|
546
564
|
${useStubs ? `
|
|
547
565
|
// Register stub Mongoose models for Better Auth's collections so
|
|
548
566
|
// \`.populate('user')\` / \`ref: 'organization'\` resolve against BA-owned
|
|
@@ -550,7 +568,7 @@ ${useStubs ? `
|
|
|
550
568
|
registerBetterAuthStubs(mongoose, {${stubPluginsList ? ` plugins: [${stubPluginsList}]` : ""}${stubExtras} });
|
|
551
569
|
` : ""} }
|
|
552
570
|
|
|
553
|
-
return
|
|
571
|
+
return auth;
|
|
554
572
|
}
|
|
555
573
|
|
|
556
574
|
export default getAuth;
|
|
@@ -690,6 +708,9 @@ export async function getUserProfile(request${ts ? ": FastifyRequest" : ""}, rep
|
|
|
690
708
|
try {
|
|
691
709
|
const requestUser = (request${ts ? " as unknown as { user?: { _id?: string; id?: string } }" : ""}).user;
|
|
692
710
|
const userId = requestUser?._id || requestUser?.id;
|
|
711
|
+
if (!userId) {
|
|
712
|
+
return reply.code(401).send({ error: 'Authentication required', code: 'arc.unauthorized' });
|
|
713
|
+
}
|
|
693
714
|
const user = await userRepository.getById(userId);
|
|
694
715
|
|
|
695
716
|
if (!user) {
|
|
@@ -710,6 +731,9 @@ export async function updateUserProfile(request${ts ? ": FastifyRequest" : ""},
|
|
|
710
731
|
try {
|
|
711
732
|
const requestUser = (request${ts ? " as unknown as { user?: { _id?: string; id?: string } }" : ""}).user;
|
|
712
733
|
const userId = requestUser?._id || requestUser?.id;
|
|
734
|
+
if (!userId) {
|
|
735
|
+
return reply.code(401).send({ error: 'Authentication required', code: 'arc.unauthorized' });
|
|
736
|
+
}
|
|
713
737
|
const updates${ts ? ": Record<string, unknown>" : ""} = { ...request.body${ts ? " as Record<string, unknown>" : ""} };
|
|
714
738
|
|
|
715
739
|
// Prevent updating protected fields — auth-managed only
|
|
@@ -838,20 +862,29 @@ function authTestTemplate(config) {
|
|
|
838
862
|
*/
|
|
839
863
|
|
|
840
864
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
841
|
-
${config.adapter === "mongokit" ? "import mongoose from 'mongoose';\n" : ""}import { createAppInstance } from '../src/app.js';
|
|
865
|
+
${config.adapter === "mongokit" ? "import mongoose from 'mongoose';\nimport { MongoMemoryServer } from 'mongodb-memory-server';\n" : ""}import { createAppInstance } from '../src/app.js';
|
|
842
866
|
${ts ? "import type { FastifyInstance } from 'fastify';\n" : ""}
|
|
867
|
+
// Resources mount under the app's resourcePrefix (see src/app.${ts ? "ts" : "js"}).
|
|
868
|
+
const API = '/api';
|
|
869
|
+
|
|
843
870
|
describe('Auth', () => {
|
|
844
871
|
let app${ts ? ": FastifyInstance" : ""};
|
|
845
|
-
const testUser = {
|
|
872
|
+
${config.adapter === "mongokit" ? ` let mongo${ts ? ": MongoMemoryServer | undefined" : ""};\n` : ""} const testUser = {
|
|
846
873
|
name: 'Test User',
|
|
847
874
|
email: 'test@example.com',
|
|
848
875
|
password: 'password123',
|
|
849
876
|
};
|
|
850
877
|
|
|
851
878
|
beforeAll(async () => {
|
|
852
|
-
${config.adapter === "mongokit" ? `
|
|
853
|
-
|
|
854
|
-
|
|
879
|
+
${config.adapter === "mongokit" ? ` // In-memory MongoDB — no local mongod required. Set MONGODB_URI to use
|
|
880
|
+
// a real instance instead (e.g. a CI service container).
|
|
881
|
+
let uri = process.env.MONGODB_URI;
|
|
882
|
+
if (!uri) {
|
|
883
|
+
mongo = await MongoMemoryServer.create();
|
|
884
|
+
uri = mongo.getUri();
|
|
885
|
+
}
|
|
886
|
+
await mongoose.connect(uri);
|
|
887
|
+
// Clean up test data (matters when MONGODB_URI points at a shared DB)
|
|
855
888
|
await mongoose.connection.collection('users').deleteMany({ email: testUser.email });
|
|
856
889
|
` : ""}
|
|
857
890
|
app = await createAppInstance();
|
|
@@ -860,27 +893,29 @@ ${config.adapter === "mongokit" ? ` const testDbUri = process.env.MONGODB_URI
|
|
|
860
893
|
|
|
861
894
|
afterAll(async () => {
|
|
862
895
|
${config.adapter === "mongokit" ? ` await mongoose.connection.collection('users').deleteMany({ email: testUser.email });
|
|
896
|
+
await app.close();
|
|
863
897
|
await mongoose.connection.close();
|
|
864
|
-
|
|
865
|
-
|
|
898
|
+
await mongo?.stop();
|
|
899
|
+
` : ` await app.close();
|
|
900
|
+
`} });
|
|
866
901
|
|
|
867
902
|
describe('POST /auth/register', () => {
|
|
868
903
|
it('should register a new user', async () => {
|
|
869
904
|
const response = await app.inject({
|
|
870
905
|
method: 'POST',
|
|
871
|
-
url:
|
|
906
|
+
url: \`\${API}/auth/register\`,
|
|
872
907
|
payload: testUser,
|
|
873
908
|
});
|
|
874
909
|
|
|
875
910
|
expect(response.statusCode).toBe(201);
|
|
876
911
|
const body = JSON.parse(response.body);
|
|
877
|
-
expect(body.
|
|
912
|
+
expect(body.message).toBeDefined();
|
|
878
913
|
});
|
|
879
914
|
|
|
880
915
|
it('should reject duplicate email', async () => {
|
|
881
916
|
const response = await app.inject({
|
|
882
917
|
method: 'POST',
|
|
883
|
-
url:
|
|
918
|
+
url: \`\${API}/auth/register\`,
|
|
884
919
|
payload: testUser,
|
|
885
920
|
});
|
|
886
921
|
|
|
@@ -892,13 +927,13 @@ ${config.adapter === "mongokit" ? ` await mongoose.connection.collection('use
|
|
|
892
927
|
it('should login with valid credentials', async () => {
|
|
893
928
|
const response = await app.inject({
|
|
894
929
|
method: 'POST',
|
|
895
|
-
url:
|
|
930
|
+
url: \`\${API}/auth/login\`,
|
|
896
931
|
payload: { email: testUser.email, password: testUser.password },
|
|
897
932
|
});
|
|
898
933
|
|
|
899
934
|
expect(response.statusCode).toBe(200);
|
|
900
935
|
const body = JSON.parse(response.body);
|
|
901
|
-
expect(body.
|
|
936
|
+
expect(body.user).toBeDefined();
|
|
902
937
|
expect(body.accessToken).toBeDefined();
|
|
903
938
|
expect(body.refreshToken).toBeDefined();
|
|
904
939
|
});
|
|
@@ -906,7 +941,7 @@ ${config.adapter === "mongokit" ? ` await mongoose.connection.collection('use
|
|
|
906
941
|
it('should reject invalid credentials', async () => {
|
|
907
942
|
const response = await app.inject({
|
|
908
943
|
method: 'POST',
|
|
909
|
-
url:
|
|
944
|
+
url: \`\${API}/auth/login\`,
|
|
910
945
|
payload: { email: testUser.email, password: 'wrongpassword' },
|
|
911
946
|
});
|
|
912
947
|
|
|
@@ -918,7 +953,7 @@ ${config.adapter === "mongokit" ? ` await mongoose.connection.collection('use
|
|
|
918
953
|
it('should require authentication', async () => {
|
|
919
954
|
const response = await app.inject({
|
|
920
955
|
method: 'GET',
|
|
921
|
-
url:
|
|
956
|
+
url: \`\${API}/users/me\`,
|
|
922
957
|
});
|
|
923
958
|
|
|
924
959
|
expect(response.statusCode).toBe(401);
|
|
@@ -929,11 +964,32 @@ ${config.adapter === "mongokit" ? ` await mongoose.connection.collection('use
|
|
|
929
964
|
}
|
|
930
965
|
//#endregion
|
|
931
966
|
//#region src/cli/commands/init/dependency-plan.ts
|
|
967
|
+
/**
|
|
968
|
+
* Single source of truth for scaffolded project dependencies.
|
|
969
|
+
*
|
|
970
|
+
* Two layers keep the scaffold from ever contradicting the arc release
|
|
971
|
+
* that ships it:
|
|
972
|
+
*
|
|
973
|
+
* 1. The static table below — tracks published npm latest, bumped on
|
|
974
|
+
* release. Serves as the offline fallback.
|
|
975
|
+
* 2. A runtime overlay from arc's OWN package.json (version + declared
|
|
976
|
+
* peer floors). The 2.20.0 review found the static pins had drifted
|
|
977
|
+
* (`arc ^2.18.5` + `primitives ^0.6.0` while arc 2.20 peers demand
|
|
978
|
+
* >=0.9.0 → ERESOLVE on the first `arc init` a new user runs).
|
|
979
|
+
* Deriving from the installed package.json makes that class of
|
|
980
|
+
* breakage structurally impossible: the scaffold always pins the
|
|
981
|
+
* arc version actually running and ranges that satisfy its peers.
|
|
982
|
+
*
|
|
983
|
+
* Used by both `packageJsonTemplate` (declares the deps in the generated
|
|
984
|
+
* `package.json` so `npm install` works without a pre-pass) and
|
|
985
|
+
* `installDependencies` (runs the package manager's `install` against
|
|
986
|
+
* the declared ranges). One source — no drift.
|
|
987
|
+
*/
|
|
932
988
|
const SCAFFOLD_DEP_VERSIONS = {
|
|
933
989
|
core: {
|
|
934
|
-
"@classytic/arc": "^2.
|
|
935
|
-
"@classytic/primitives": "^0.
|
|
936
|
-
"@classytic/repo-core": "^0.
|
|
990
|
+
"@classytic/arc": "^2.20.0",
|
|
991
|
+
"@classytic/primitives": "^0.11.0",
|
|
992
|
+
"@classytic/repo-core": "^0.8.0",
|
|
937
993
|
"@fastify/cors": "^11.2.0",
|
|
938
994
|
"@fastify/helmet": "^13.0.2",
|
|
939
995
|
"@fastify/rate-limit": "^10.3.0",
|
|
@@ -953,7 +1009,7 @@ const SCAFFOLD_DEP_VERSIONS = {
|
|
|
953
1009
|
},
|
|
954
1010
|
authBetterAuthApiKey: { "@better-auth/api-key": "^1.6.11" },
|
|
955
1011
|
adapterMongokit: {
|
|
956
|
-
"@classytic/mongokit": "^3.
|
|
1012
|
+
"@classytic/mongokit": "^3.21.0",
|
|
957
1013
|
mongoose: "^9.6.2"
|
|
958
1014
|
},
|
|
959
1015
|
devCommon: {
|
|
@@ -965,11 +1021,69 @@ const SCAFFOLD_DEP_VERSIONS = {
|
|
|
965
1021
|
devTypescript: {
|
|
966
1022
|
"@types/node": "^22.10.0",
|
|
967
1023
|
tsx: "^4.22.3",
|
|
968
|
-
typescript: "^
|
|
1024
|
+
typescript: "^7.0.2"
|
|
969
1025
|
},
|
|
970
1026
|
typesJwt: { "@types/bcryptjs": "^3.0.0" }
|
|
971
1027
|
};
|
|
972
1028
|
/**
|
|
1029
|
+
* Locate arc's own package.json by walking up from the executing file.
|
|
1030
|
+
* A path walk (vs `require.resolve('@classytic/arc/package.json')`) works
|
|
1031
|
+
* regardless of bundle chunk layout and of package.json not being in the
|
|
1032
|
+
* exports map. Returns undefined when not found — callers fall back to
|
|
1033
|
+
* the static table.
|
|
1034
|
+
*/
|
|
1035
|
+
function readOwnPackageJson() {
|
|
1036
|
+
try {
|
|
1037
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
1038
|
+
for (let depth = 0; depth < 6; depth++) {
|
|
1039
|
+
const candidate = join(dir, "package.json");
|
|
1040
|
+
if (existsSync(candidate)) {
|
|
1041
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
1042
|
+
if (parsed.name === "@classytic/arc") return parsed;
|
|
1043
|
+
}
|
|
1044
|
+
const parent = dirname(dir);
|
|
1045
|
+
if (parent === dir) break;
|
|
1046
|
+
dir = parent;
|
|
1047
|
+
}
|
|
1048
|
+
} catch {}
|
|
1049
|
+
}
|
|
1050
|
+
/** First x.y.z triple in a range string, or undefined. */
|
|
1051
|
+
function parseFloor(range) {
|
|
1052
|
+
const m = /(\d+)\.(\d+)\.(\d+)/.exec(range);
|
|
1053
|
+
if (!m) return void 0;
|
|
1054
|
+
return [
|
|
1055
|
+
Number(m[1]),
|
|
1056
|
+
Number(m[2]),
|
|
1057
|
+
Number(m[3])
|
|
1058
|
+
];
|
|
1059
|
+
}
|
|
1060
|
+
function floorLessThan(a, b) {
|
|
1061
|
+
if (a[0] !== b[0]) return a[0] < b[0];
|
|
1062
|
+
if (a[1] !== b[1]) return a[1] < b[1];
|
|
1063
|
+
return a[2] < b[2];
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Overlay live truth from arc's own package.json onto the static pins:
|
|
1067
|
+
* - `@classytic/arc` pins the exact running version (`^<version>`).
|
|
1068
|
+
* - Any scaffolded dep that is also an arc peer gets raised to `^<peer
|
|
1069
|
+
* floor>` when the static pin's floor is BELOW the declared peer floor
|
|
1070
|
+
* (never lowered — static pins may legitimately be ahead of the floor).
|
|
1071
|
+
*/
|
|
1072
|
+
function overlayLiveArcVersions(dependencies) {
|
|
1073
|
+
const own = readOwnPackageJson();
|
|
1074
|
+
if (!own) return;
|
|
1075
|
+
if (own.version && dependencies["@classytic/arc"]) dependencies["@classytic/arc"] = `^${own.version}`;
|
|
1076
|
+
const peers = own.peerDependencies ?? {};
|
|
1077
|
+
for (const [name, staticRange] of Object.entries(dependencies)) {
|
|
1078
|
+
const peerRange = peers[name];
|
|
1079
|
+
if (!peerRange) continue;
|
|
1080
|
+
const peerFloor = parseFloor(peerRange);
|
|
1081
|
+
const staticFloor = parseFloor(staticRange);
|
|
1082
|
+
if (!peerFloor || !staticFloor) continue;
|
|
1083
|
+
if (floorLessThan(staticFloor, peerFloor)) dependencies[name] = `^${peerFloor.join(".")}`;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
973
1087
|
* Resolve the dependency manifest for a scaffold configuration.
|
|
974
1088
|
*
|
|
975
1089
|
* Returns sorted records (alphabetical by package name) so the generated
|
|
@@ -980,6 +1094,7 @@ function resolveScaffoldDependencies(config) {
|
|
|
980
1094
|
const devDependencies = { ...SCAFFOLD_DEP_VERSIONS.devCommon };
|
|
981
1095
|
if (config.auth === "better-auth") {
|
|
982
1096
|
Object.assign(dependencies, SCAFFOLD_DEP_VERSIONS.authBetterAuth);
|
|
1097
|
+
devDependencies["@fastify/jwt"] = SCAFFOLD_DEP_VERSIONS.authJwt["@fastify/jwt"];
|
|
983
1098
|
if (config.apiKey) Object.assign(dependencies, SCAFFOLD_DEP_VERSIONS.authBetterAuthApiKey);
|
|
984
1099
|
} else {
|
|
985
1100
|
Object.assign(dependencies, SCAFFOLD_DEP_VERSIONS.authJwt);
|
|
@@ -987,6 +1102,7 @@ function resolveScaffoldDependencies(config) {
|
|
|
987
1102
|
}
|
|
988
1103
|
if (config.adapter === "mongokit") Object.assign(dependencies, SCAFFOLD_DEP_VERSIONS.adapterMongokit);
|
|
989
1104
|
if (config.typescript) Object.assign(devDependencies, SCAFFOLD_DEP_VERSIONS.devTypescript);
|
|
1105
|
+
overlayLiveArcVersions(dependencies);
|
|
990
1106
|
return {
|
|
991
1107
|
dependencies: sortByKey(dependencies),
|
|
992
1108
|
devDependencies: sortByKey(devDependencies)
|
|
@@ -1077,6 +1193,7 @@ function tsconfigTemplate() {
|
|
|
1077
1193
|
declarationMap: true,
|
|
1078
1194
|
sourceMap: true,
|
|
1079
1195
|
resolveJsonModule: true,
|
|
1196
|
+
types: ["node"],
|
|
1080
1197
|
paths: {
|
|
1081
1198
|
"#shared/*": ["./src/shared/*"],
|
|
1082
1199
|
"#resources/*": ["./src/resources/*"],
|
|
@@ -1971,7 +2088,13 @@ import { describe, it, beforeAll, afterAll } from 'vitest';
|
|
|
1971
2088
|
import { createTestApp, expectArc } from '@classytic/arc/testing';
|
|
1972
2089
|
import type { TestAppContext${ts ? ", TestAuthProvider" : ""} } from '@classytic/arc/testing';
|
|
1973
2090
|
import exampleResource from '../src/resources/example/example.resource.js';
|
|
1974
|
-
|
|
2091
|
+
${config.tenant === "multi" ? `
|
|
2092
|
+
// Valid ObjectId hex — the example model types organizationId and createdBy
|
|
2093
|
+
// as ObjectId (createdBy is auto-stamped from the caller's user id).
|
|
2094
|
+
const TEST_ORG_ID = '64f000000000000000000001';
|
|
2095
|
+
const MANAGER_ID = '64f0000000000000000000a1';
|
|
2096
|
+
const STAFF_ID = '64f0000000000000000000a2';
|
|
2097
|
+
` : ""}
|
|
1975
2098
|
describe('Example Resource', () => {
|
|
1976
2099
|
let ctx${ts ? ": TestAppContext" : ""};
|
|
1977
2100
|
// Narrowed once below — \`authMode: 'jwt'\` guarantees a provider, but
|
|
@@ -1986,17 +2109,70 @@ ${config.adapter === "mongokit" ? " connectMongoose: true,\n" : ""} });
|
|
|
1986
2109
|
|
|
1987
2110
|
if (!ctx.auth) throw new Error('Test auth provider was not configured');
|
|
1988
2111
|
auth = ctx.auth;
|
|
1989
|
-
|
|
2112
|
+
${config.tenant === "multi" ? `
|
|
2113
|
+
// Arc's JWT auth builds a MEMBER scope straight from token claims —
|
|
2114
|
+
// \`organizationId\` + \`orgRoles\` land on \`request.scope\`. The org-staff
|
|
2115
|
+
// permission set reads those org roles; the tenant preset filters and
|
|
2116
|
+
// stamps rows by the org id.
|
|
2117
|
+
auth.register('manager', {
|
|
2118
|
+
user: { id: MANAGER_ID, role: 'user', organizationId: TEST_ORG_ID, orgRoles: ['manager'] },
|
|
2119
|
+
orgId: TEST_ORG_ID,
|
|
2120
|
+
});
|
|
2121
|
+
auth.register('staff', {
|
|
2122
|
+
user: { id: STAFF_ID, role: 'user', organizationId: TEST_ORG_ID, orgRoles: ['staff'] },
|
|
2123
|
+
orgId: TEST_ORG_ID,
|
|
2124
|
+
});
|
|
2125
|
+
` : `
|
|
1990
2126
|
// Arc's permission engine reads singular user.role — string,
|
|
1991
2127
|
// comma-separated string, or array all normalise via getUserRoles().
|
|
2128
|
+
// The id must be ObjectId-shaped: the example model's createdBy is an
|
|
2129
|
+
// ObjectId, auto-stamped from the caller's user id.
|
|
1992
2130
|
auth.register('admin', {
|
|
1993
|
-
user: { id: '
|
|
1994
|
-
orgId: 'org-1',
|
|
2131
|
+
user: { id: '64f0000000000000000000ad', role: 'admin' },
|
|
1995
2132
|
});
|
|
1996
|
-
});
|
|
2133
|
+
`} });
|
|
1997
2134
|
|
|
1998
2135
|
afterAll(() => ctx.close());
|
|
2136
|
+
${config.tenant === "multi" ? `
|
|
2137
|
+
describe('GET /examples', () => {
|
|
2138
|
+
it('should reject unauthenticated listing (org-staff gated)', async () => {
|
|
2139
|
+
const res = await ctx.app.inject({ method: 'GET', url: '/examples' });
|
|
2140
|
+
expectArc(res).unauthorized();
|
|
2141
|
+
});
|
|
2142
|
+
|
|
2143
|
+
it('should list for an org member (rows scoped to their org)', async () => {
|
|
2144
|
+
const res = await ctx.app.inject({
|
|
2145
|
+
method: 'GET',
|
|
2146
|
+
url: '/examples',
|
|
2147
|
+
headers: auth.as('staff').headers,
|
|
2148
|
+
});
|
|
2149
|
+
expectArc(res).ok().paginated();
|
|
2150
|
+
});
|
|
2151
|
+
});
|
|
1999
2152
|
|
|
2153
|
+
describe('POST /examples', () => {
|
|
2154
|
+
it('should require authentication', async () => {
|
|
2155
|
+
const res = await ctx.app.inject({
|
|
2156
|
+
method: 'POST',
|
|
2157
|
+
url: '/examples',
|
|
2158
|
+
payload: { name: 'Test Example' },
|
|
2159
|
+
});
|
|
2160
|
+
expectArc(res).unauthorized();
|
|
2161
|
+
});
|
|
2162
|
+
|
|
2163
|
+
it('should create for an org manager — organizationId stamped server-side', async () => {
|
|
2164
|
+
const res = await ctx.app.inject({
|
|
2165
|
+
method: 'POST',
|
|
2166
|
+
url: '/examples',
|
|
2167
|
+
headers: auth.as('manager').headers,
|
|
2168
|
+
// No organizationId in the body — the tenant preset injects it from
|
|
2169
|
+
// the caller's scope (the field is systemManaged in the schema).
|
|
2170
|
+
payload: { name: 'Test Example' },
|
|
2171
|
+
});
|
|
2172
|
+
expectArc(res).ok(201);
|
|
2173
|
+
});
|
|
2174
|
+
});
|
|
2175
|
+
` : `
|
|
2000
2176
|
describe('GET /examples', () => {
|
|
2001
2177
|
it('should return a list of examples (public)', async () => {
|
|
2002
2178
|
const res = await ctx.app.inject({ method: 'GET', url: '/examples' });
|
|
@@ -2021,9 +2197,10 @@ ${config.adapter === "mongokit" ? " connectMongoose: true,\n" : ""} });
|
|
|
2021
2197
|
headers: auth.as('admin').headers,
|
|
2022
2198
|
payload: { name: 'Test Example' },
|
|
2023
2199
|
});
|
|
2024
|
-
expectArc(res).ok();
|
|
2200
|
+
expectArc(res).ok(201);
|
|
2025
2201
|
});
|
|
2026
2202
|
});
|
|
2203
|
+
`}
|
|
2027
2204
|
|
|
2028
2205
|
// Add more tests as needed:
|
|
2029
2206
|
// - GET /examples/:id (expectArc(res).ok().hasData({ name: '...' }))
|
|
@@ -2413,113 +2590,35 @@ function flexibleMultiTenantPresetTemplate(config) {
|
|
|
2413
2590
|
return `/**
|
|
2414
2591
|
* Flexible Multi-Tenant Preset
|
|
2415
2592
|
*
|
|
2416
|
-
*
|
|
2593
|
+
* Thin wrapper around arc's built-in 'multiTenantPreset' with public reads:
|
|
2417
2594
|
*
|
|
2418
|
-
*
|
|
2419
|
-
* -
|
|
2420
|
-
* -
|
|
2421
|
-
* -
|
|
2595
|
+
* - list/get WITHOUT org context → allowed through unfiltered (public data)
|
|
2596
|
+
* - org context present → rows filtered + stamped to the caller's org
|
|
2597
|
+
* - elevated (platform admin) → unfiltered, cross-tenant
|
|
2598
|
+
* - create/update fail closed without org context, and update overwrites any
|
|
2599
|
+
* client-supplied tenant field (no cross-tenant document hops)
|
|
2422
2600
|
*
|
|
2423
|
-
*
|
|
2601
|
+
* The built-in preset also registers 'systemManaged' field rules for the
|
|
2602
|
+
* tenant field, so generated request schemas never demand it in the body —
|
|
2603
|
+
* the server stamps it from the caller's scope instead. Don't hand-roll
|
|
2604
|
+
* tenant middleware: without those field rules Fastify's validation rejects
|
|
2605
|
+
* creates before injection can run.
|
|
2606
|
+
*
|
|
2607
|
+
* Want members-only reads? Drop 'allowPublic' below, or use
|
|
2608
|
+
* 'multiTenantPreset({ tenantField })' directly on the resource.
|
|
2424
2609
|
*/
|
|
2425
2610
|
${ts ? `
|
|
2426
|
-
import {
|
|
2427
|
-
import type { RequestScope } from '@classytic/arc/scope';
|
|
2611
|
+
import { multiTenantPreset } from '@classytic/arc/presets/tenant';
|
|
2428
2612
|
|
|
2429
2613
|
interface FlexibleMultiTenantOptions {
|
|
2430
2614
|
tenantField?: string;
|
|
2431
2615
|
}
|
|
2432
|
-
|
|
2433
|
-
interface PresetMiddlewares {
|
|
2434
|
-
list: ((request: any, reply: any) => Promise<void>)[];
|
|
2435
|
-
get: ((request: any, reply: any) => Promise<void>)[];
|
|
2436
|
-
create: ((request: any, reply: any) => Promise<void>)[];
|
|
2437
|
-
update: ((request: any, reply: any) => Promise<void>)[];
|
|
2438
|
-
delete: ((request: any, reply: any) => Promise<void>)[];
|
|
2439
|
-
}
|
|
2440
|
-
|
|
2441
|
-
interface Preset {
|
|
2442
|
-
[key: string]: unknown;
|
|
2443
|
-
name: string;
|
|
2444
|
-
middlewares: PresetMiddlewares;
|
|
2445
|
-
}
|
|
2446
2616
|
` : `
|
|
2447
|
-
const {
|
|
2617
|
+
const { multiTenantPreset } = require('@classytic/arc/presets/tenant');
|
|
2448
2618
|
`}
|
|
2449
|
-
|
|
2450
|
-
* Create flexible tenant filter middleware.
|
|
2451
|
-
* Only filters when org context is present.
|
|
2452
|
-
*/
|
|
2453
|
-
function createFlexibleTenantFilter(tenantField${ts ? ": string" : ""}) {
|
|
2454
|
-
return async (request${ts ? ": any" : ""}, reply${ts ? ": any" : ""}) => {
|
|
2455
|
-
const scope${ts ? ": RequestScope" : ""} = request.scope ?? { kind: 'public' };
|
|
2456
|
-
|
|
2457
|
-
// Elevated scope — platform admin sees all, no filter
|
|
2458
|
-
if (isElevated(scope)) {
|
|
2459
|
-
request.log?.debug?.({ msg: 'Elevated scope — no tenant filter' });
|
|
2460
|
-
return;
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
// Member scope — filter by org
|
|
2464
|
-
if (isMember(scope)) {
|
|
2465
|
-
request.query = request.query ?? {};
|
|
2466
|
-
request.query._policyFilters = {
|
|
2467
|
-
...(request.query._policyFilters ?? {}),
|
|
2468
|
-
[tenantField]: scope.organizationId,
|
|
2469
|
-
};
|
|
2470
|
-
request.log?.debug?.({ msg: 'Tenant filter applied', orgId: scope.organizationId, tenantField });
|
|
2471
|
-
return;
|
|
2472
|
-
}
|
|
2473
|
-
|
|
2474
|
-
// Public / authenticated — no org context, show all data (public routes)
|
|
2475
|
-
request.log?.debug?.({ msg: 'No org context — showing all data' });
|
|
2476
|
-
};
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
/**
|
|
2480
|
-
* Create tenant injection middleware.
|
|
2481
|
-
* Injects tenant ID into request body on create.
|
|
2482
|
-
*/
|
|
2483
|
-
function createTenantInjection(tenantField${ts ? ": string" : ""}) {
|
|
2484
|
-
return async (request${ts ? ": any" : ""}, reply${ts ? ": any" : ""}) => {
|
|
2485
|
-
const scope${ts ? ": RequestScope" : ""} = request.scope ?? { kind: 'public' };
|
|
2486
|
-
const orgId = getOrgId(scope);
|
|
2487
|
-
|
|
2488
|
-
// Fail-closed: Require orgId for create operations
|
|
2489
|
-
if (!orgId) {
|
|
2490
|
-
return reply.code(403).send({
|
|
2491
|
-
error: 'Organization context required to create resources',
|
|
2492
|
-
code: 'arc.org_required',
|
|
2493
|
-
});
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
if (request.body) {
|
|
2497
|
-
request.body[tenantField] = orgId;
|
|
2498
|
-
}
|
|
2499
|
-
};
|
|
2500
|
-
}
|
|
2501
|
-
|
|
2502
|
-
/**
|
|
2503
|
-
* Flexible Multi-Tenant Preset
|
|
2504
|
-
*
|
|
2505
|
-
* @param options.tenantField - Field name in database (default: 'organizationId')
|
|
2506
|
-
*/
|
|
2507
|
-
export function flexibleMultiTenantPreset(options${ts ? ": FlexibleMultiTenantOptions = {}" : " = {}"})${ts ? ": Preset" : ""} {
|
|
2619
|
+
export function flexibleMultiTenantPreset(options${ts ? ": FlexibleMultiTenantOptions = {}" : " = {}"}) {
|
|
2508
2620
|
const { tenantField = 'organizationId' } = options;
|
|
2509
|
-
|
|
2510
|
-
const tenantFilter = createFlexibleTenantFilter(tenantField);
|
|
2511
|
-
const tenantInjection = createTenantInjection(tenantField);
|
|
2512
|
-
|
|
2513
|
-
return {
|
|
2514
|
-
name: 'flexibleMultiTenant',
|
|
2515
|
-
middlewares: {
|
|
2516
|
-
list: [tenantFilter],
|
|
2517
|
-
get: [tenantFilter],
|
|
2518
|
-
create: [tenantInjection],
|
|
2519
|
-
update: [tenantFilter],
|
|
2520
|
-
delete: [tenantFilter],
|
|
2521
|
-
},
|
|
2522
|
-
};
|
|
2621
|
+
return multiTenantPreset({ tenantField, allowPublic: ['list', 'get'] });
|
|
2523
2622
|
}
|
|
2524
2623
|
|
|
2525
2624
|
export default flexibleMultiTenantPreset;
|
|
@@ -3022,7 +3121,7 @@ async function gatherConfig(options) {
|
|
|
3022
3121
|
output: process.stdout
|
|
3023
3122
|
});
|
|
3024
3123
|
const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
|
|
3025
|
-
const nonInteractive = !!options.name;
|
|
3124
|
+
const nonInteractive = !!options.name || !process.stdin.isTTY;
|
|
3026
3125
|
try {
|
|
3027
3126
|
const name = options.name || await question("Project name: ") || "my-arc-app";
|
|
3028
3127
|
let adapter = options.adapter || "mongokit";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as DomainEvent } from "../EventTransport-
|
|
2
|
-
import { c as WebSocketClient, l as WebSocketMessage } from "../websocket-
|
|
1
|
+
import { n as DomainEvent } from "../EventTransport-QmY7A258.mjs";
|
|
2
|
+
import { c as WebSocketClient, l as WebSocketMessage } from "../websocket-uuWqM6UY.mjs";
|
|
3
3
|
import { FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
4
|
-
|
|
5
4
|
//#region src/integrations/event-gateway.d.ts
|
|
6
5
|
interface EventGatewayOptions {
|
|
7
6
|
/** Require auth for all real-time connections (default: true) */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as WebSocketClient, l as WebSocketMessage, u as WebSocketPluginOptions } from "../websocket-
|
|
1
|
+
import { c as WebSocketClient, l as WebSocketMessage, u as WebSocketPluginOptions } from "../websocket-uuWqM6UY.mjs";
|
|
2
2
|
import { EventGatewayOptions } from "./event-gateway.mjs";
|
|
3
3
|
import { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats } from "./jobs.mjs";
|
|
4
|
-
import { c as McpAuthResult, d as PromptDefinition, g as ToolDefinition, h as ToolContext, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, r as CreateMcpServerConfig, t as BetterAuthHandler, u as McpResourceConfig } from "../types-
|
|
4
|
+
import { c as McpAuthResult, d as PromptDefinition, g as ToolDefinition, h as ToolContext, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, r as CreateMcpServerConfig, t as BetterAuthHandler, u as McpResourceConfig } from "../types-DRj-Ut4n.mjs";
|
|
5
5
|
import { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike } from "./streamline.mjs";
|
|
6
6
|
import { WebhookDeliveryRecord, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription } from "./webhooks.mjs";
|
|
7
7
|
export type { BetterAuthHandler, CallToolResult, CreateMcpServerConfig, CrudOperation, EventGatewayOptions, JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, McpAuthResult, McpPluginOptions, McpResourceConfig, PromptDefinition, QueueStats, StreamlinePluginOptions, ToolAnnotations, ToolContext, ToolDefinition, WebSocketClient, WebSocketMessage, WebSocketPluginOptions, WebhookDeliveryRecord, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription, WorkflowLike, WorkflowRunLike };
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { V as ResourceDefinition, bt as IControllerResponse, xt as IRequestContext } from "../../index-
|
|
2
|
-
import { i as RequestScope } from "../../types-
|
|
3
|
-
import { c as PermissionCheck } from "../../fields-
|
|
4
|
-
import { a as CrudDescriptionOverride, c as McpAuthResult, d as PromptDefinition, f as PromptResult, g as ToolDefinition, h as ToolContext, i as CrudDescriptionMeta, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, p as SessionEntry, r as CreateMcpServerConfig, s as McpAuthResolver, t as BetterAuthHandler, u as McpResourceConfig } from "../../types-
|
|
1
|
+
import { V as ResourceDefinition, bt as IControllerResponse, xt as IRequestContext } from "../../index-Dtrj6XZk.mjs";
|
|
2
|
+
import { i as RequestScope } from "../../types-DH0GupI7.mjs";
|
|
3
|
+
import { c as PermissionCheck } from "../../fields-BGqrAa_u.mjs";
|
|
4
|
+
import { a as CrudDescriptionOverride, c as McpAuthResult, d as PromptDefinition, f as PromptResult, g as ToolDefinition, h as ToolContext, i as CrudDescriptionMeta, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, p as SessionEntry, r as CreateMcpServerConfig, s as McpAuthResolver, t as BetterAuthHandler, u as McpResourceConfig } from "../../types-DRj-Ut4n.mjs";
|
|
5
5
|
import { ErrorContract } from "@classytic/repo-core/errors";
|
|
6
6
|
import { FastifyPluginAsync } from "fastify";
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
|
|
9
8
|
//#region src/integrations/mcp/aiSdkBridge.d.ts
|
|
10
9
|
/** Minimal AI SDK tool shape we need to invoke. */
|
|
11
10
|
interface AiSdkExecutable {
|
|
@@ -170,7 +169,7 @@ interface DefinePromptConfig<TArgs extends Record<string, z.ZodTypeAny>> {
|
|
|
170
169
|
title?: string;
|
|
171
170
|
/** Flat Zod shape for prompt arguments */
|
|
172
171
|
args?: TArgs;
|
|
173
|
-
handler: (args: { [K in keyof TArgs]: z.infer<TArgs[K]
|
|
172
|
+
handler: (args: { [K in keyof TArgs]: z.infer<TArgs[K]>; }) => PromptResult;
|
|
174
173
|
}
|
|
175
174
|
/**
|
|
176
175
|
* Define a type-safe MCP prompt.
|
|
@@ -197,7 +196,7 @@ interface DefineToolConfig<TInput extends Record<string, z.ZodTypeAny>, TSession
|
|
|
197
196
|
/** Flat Zod shape for structured output */
|
|
198
197
|
output?: Record<string, z.ZodTypeAny>;
|
|
199
198
|
annotations?: ToolAnnotations;
|
|
200
|
-
handler: (input: { [K in keyof TInput]: z.infer<TInput[K]
|
|
199
|
+
handler: (input: { [K in keyof TInput]: z.infer<TInput[K]>; }, ctx: ToolContext<TSession>) => Promise<CallToolResult>;
|
|
201
200
|
}
|
|
202
201
|
/**
|
|
203
202
|
* Define a type-safe MCP tool.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, h as resolveToolCollisions, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-
|
|
1
|
+
import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, h as resolveToolCollisions, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-B9XjjNVG.mjs";
|
|
2
2
|
//#region src/integrations/mcp/defineTool.ts
|
|
3
3
|
/**
|
|
4
4
|
* Define a type-safe MCP tool.
|