@caronte-sdk/node 0.2.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.
Files changed (40) hide show
  1. package/README.md +198 -0
  2. package/dist/adapters/apollo.cjs +71 -0
  3. package/dist/adapters/apollo.cjs.map +1 -0
  4. package/dist/adapters/apollo.d.cts +34 -0
  5. package/dist/adapters/apollo.d.ts +34 -0
  6. package/dist/adapters/apollo.js +45 -0
  7. package/dist/adapters/apollo.js.map +1 -0
  8. package/dist/adapters/express.cjs +57 -0
  9. package/dist/adapters/express.cjs.map +1 -0
  10. package/dist/adapters/express.d.cts +19 -0
  11. package/dist/adapters/express.d.ts +19 -0
  12. package/dist/adapters/express.js +41 -0
  13. package/dist/adapters/express.js.map +1 -0
  14. package/dist/adapters/fastify.cjs +66 -0
  15. package/dist/adapters/fastify.cjs.map +1 -0
  16. package/dist/adapters/fastify.d.cts +19 -0
  17. package/dist/adapters/fastify.d.ts +19 -0
  18. package/dist/adapters/fastify.js +42 -0
  19. package/dist/adapters/fastify.js.map +1 -0
  20. package/dist/adapters/nest/index.cjs +299 -0
  21. package/dist/adapters/nest/index.cjs.map +1 -0
  22. package/dist/adapters/nest/index.d.cts +60 -0
  23. package/dist/adapters/nest/index.d.ts +60 -0
  24. package/dist/adapters/nest/index.js +109 -0
  25. package/dist/adapters/nest/index.js.map +1 -0
  26. package/dist/chunk-AKUM2UEO.js +21 -0
  27. package/dist/chunk-AKUM2UEO.js.map +1 -0
  28. package/dist/chunk-G73YTEJQ.js +146 -0
  29. package/dist/chunk-G73YTEJQ.js.map +1 -0
  30. package/dist/chunk-HV6X5RJY.js +37 -0
  31. package/dist/chunk-HV6X5RJY.js.map +1 -0
  32. package/dist/client-Cgm1csuM.d.cts +49 -0
  33. package/dist/client-Cgm1csuM.d.ts +49 -0
  34. package/dist/index.cjs +199 -0
  35. package/dist/index.cjs.map +1 -0
  36. package/dist/index.d.cts +27 -0
  37. package/dist/index.d.ts +27 -0
  38. package/dist/index.js +5 -0
  39. package/dist/index.js.map +1 -0
  40. package/package.json +78 -0
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # @argos/node
2
+
3
+ Node.js client for the **Argos** authorizer — handles app authentication,
4
+ JWT validation, permission checking and automatic operation sync.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install @argos/node
10
+ # With framework-specific adapter
11
+ npm install @argos/node express # Express
12
+ npm install @argos/node fastify # Fastify
13
+ npm install @argos/node @nestjs/common # NestJS
14
+ npm install @argos/node @apollo/server # Apollo Server
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ### Express
20
+
21
+ ```ts
22
+ import express from 'express';
23
+ import { ArgosClient, getRegistry } from '@argos/node';
24
+ import { argos } from '@argos/node/express';
25
+
26
+ const client = new ArgosClient({
27
+ authorizerUrl: process.env.AUTHORIZER_URL!,
28
+ realmId: process.env.REALM_ID!,
29
+ appId: process.env.APP_ID!,
30
+ secret: process.env.APP_SECRET!,
31
+ });
32
+
33
+ const { middleware, guard } = argos(client);
34
+
35
+ const app = express();
36
+ app.use(express.json(), middleware);
37
+
38
+ app.get('/health', guard('health:check', 'public'), (_req, res) => res.json({ status: 'ok' }));
39
+ app.get('/tasks', guard('tasks:list', 'private'), (_req, res) => res.json(tasks));
40
+ app.post('/tasks', guard('tasks:create', 'protected'), (req, res) => { /* req.argosUser */ });
41
+
42
+ for (const op of getRegistry()) client.registerOperation(op);
43
+ await client.startup();
44
+
45
+ app.listen(3001);
46
+ ```
47
+
48
+ ### Fastify
49
+
50
+ ```ts
51
+ import Fastify from 'fastify';
52
+ import { ArgosClient, getRegistry } from '@argos/node';
53
+ import { argosFastifyPlugin } from '@argos/node/fastify';
54
+
55
+ const client = new ArgosClient({ ... });
56
+ const fastify = Fastify();
57
+
58
+ await fastify.register(argosFastifyPlugin, { client });
59
+
60
+ fastify.get('/health',
61
+ { preHandler: fastify.argosGuard('health:check', 'public') },
62
+ async () => ({ status: 'ok' }),
63
+ );
64
+
65
+ fastify.get('/tasks',
66
+ { preHandler: fastify.argosGuard('tasks:list', 'private') },
67
+ async (req) => { /* req.argosUser */ },
68
+ );
69
+
70
+ for (const op of getRegistry()) client.registerOperation(op);
71
+ await client.startup();
72
+
73
+ await fastify.listen({ port: 3002 });
74
+ ```
75
+
76
+ ### NestJS
77
+
78
+ ```ts
79
+ // app.module.ts
80
+ import { Module } from '@nestjs/common';
81
+ import { ArgosModule } from '@argos/node/nest';
82
+
83
+ @Module({
84
+ imports: [
85
+ ArgosModule.forRootAsync({
86
+ authorizerUrl: process.env.AUTHORIZER_URL!,
87
+ realmId: process.env.REALM_ID!,
88
+ appId: process.env.APP_ID!,
89
+ secret: process.env.APP_SECRET!,
90
+ }),
91
+ ],
92
+ })
93
+ export class AppModule {}
94
+ ```
95
+
96
+ ```ts
97
+ // tasks.controller.ts
98
+ import { Controller, Get, Post, UseGuards } from '@nestjs/common';
99
+ import { ArgosGuard, ArgosUser, Operation } from '@argos/node/nest';
100
+ import type { TokenClaims } from '@argos/node';
101
+
102
+ @Controller('tasks')
103
+ @UseGuards(ArgosGuard)
104
+ export class TasksController {
105
+
106
+ @Get()
107
+ @Operation('tasks:list', 'private')
108
+ list() { return tasks; }
109
+
110
+ @Post()
111
+ @Operation('tasks:create', 'protected')
112
+ create(@ArgosUser() user: TokenClaims) {
113
+ // user.sub, user.groups
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### Apollo Server
119
+
120
+ ```ts
121
+ import { ApolloServer } from '@apollo/server';
122
+ import { ArgosClient, getRegistry } from '@argos/node';
123
+ import { argosPlugin, createGuard, type ArgosContext } from '@argos/node/apollo';
124
+
125
+ const client = new ArgosClient({ ... });
126
+ const guard = createGuard(client);
127
+
128
+ const resolvers = {
129
+ Query: {
130
+ health: guard('health:check', 'public', () => 'ok'),
131
+ tasks: guard('tasks:list', 'private', (_p, _a, ctx: ArgosContext) => {
132
+ const user = ctx.argosUser; // TokenClaims
133
+ return tasks;
134
+ }),
135
+ },
136
+ Mutation: {
137
+ createTask: guard('tasks:create', 'protected', (_p, { input }, ctx: ArgosContext) => {
138
+ // ctx.argosUser is the authenticated user
139
+ }),
140
+ },
141
+ };
142
+
143
+ for (const op of getRegistry()) client.registerOperation(op);
144
+ await client.startup();
145
+
146
+ const server = new ApolloServer<ArgosContext>({
147
+ typeDefs,
148
+ resolvers,
149
+ plugins: [argosPlugin(client)],
150
+ });
151
+ ```
152
+
153
+ ## Operation levels
154
+
155
+ | Level | Who can access |
156
+ |-------------|----------------|
157
+ | `public` | Everyone — no token required |
158
+ | `private` | Any authenticated user (token with at least one group) |
159
+ | `protected` | Only users whose groups intersect the operation's `allowed_groups` |
160
+
161
+ ## Method auto-detection
162
+
163
+ The method is inferred from the last segment of the operation id:
164
+
165
+ | Operation id suffix | Detected method |
166
+ |---------------------|-----------------|
167
+ | `list`, `get`, `fetch`, `read` | `read` |
168
+ | `delete`, `remove`, `destroy` | `delete` |
169
+ | `stream`, `subscribe`, `watch`, `listen` | `stream` |
170
+ | anything else | `write` |
171
+
172
+ You can always pass the method explicitly as the third argument to `guard()`.
173
+
174
+ ## Configuration
175
+
176
+ | Parameter | Description |
177
+ |----------------|-------------|
178
+ | `authorizerUrl` | Base URL of Argos, including the API prefix (e.g. `http://host/api`) |
179
+ | `realmId` | Name or UUID of the realm (e.g. `purp`) |
180
+ | `appId` | UUID of the app registered in `auth.apps` |
181
+ | `secret` | Plain-text app secret — use env vars, never commit |
182
+
183
+ ## How it works
184
+
185
+ On `startup()`, the client:
186
+ 1. **Authenticates** — exchanges `appId` + `secret` for a short-lived app JWT
187
+ 2. **Fetches JWKS** — caches the public keys for token validation
188
+ 3. **Syncs operations** — pushes the registered operation catalogue to the authorizer
189
+ 4. **Fetches operations** — retrieves the authorised operation list with group bindings
190
+
191
+ On each request, the adapter:
192
+ - Validates the Bearer token using the cached JWKS
193
+ - Checks `TokenClaims.groups` against the operation's `allowedGroups`
194
+ - Injects `argosUser` (`TokenClaims`) into the request context
195
+
196
+ ## License
197
+
198
+ CC0 1.0 Universal — public domain.
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ // src/exceptions.ts
4
+ var ArgosError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = this.constructor.name;
8
+ }
9
+ };
10
+ var ArgosTokenError = class extends ArgosError {
11
+ };
12
+
13
+ // src/registry.ts
14
+ var _registry = [];
15
+ function detectMethod(id) {
16
+ const last = (id.split(":").pop() ?? "").toLowerCase();
17
+ if (/^(get|list|fetch|read)/.test(last)) return "read";
18
+ if (/^(delete|remove|destroy)/.test(last)) return "delete";
19
+ if (/(stream|subscribe|watch|listen)/.test(last)) return "stream";
20
+ return "write";
21
+ }
22
+ function registerOperation(op) {
23
+ const exists = _registry.some(
24
+ (o) => o.identifier === op.identifier && o.method === op.method
25
+ );
26
+ if (!exists) _registry.push(op);
27
+ }
28
+
29
+ // src/adapters/apollo.ts
30
+ function argosPlugin(client) {
31
+ return {
32
+ async requestDidStart() {
33
+ return {
34
+ async didResolveOperation(ctx) {
35
+ const httpAuth = ctx.request.http?.headers.get("authorization") ?? "";
36
+ const wsAuth = ctx.contextValue?.connectionParams?.Authorization ?? "";
37
+ const auth = httpAuth || wsAuth;
38
+ const [scheme, token] = auth.split(" ");
39
+ if (scheme?.toLowerCase() === "bearer" && token) {
40
+ try {
41
+ ctx.contextValue.argosUser = await client.validateToken(token);
42
+ } catch (err) {
43
+ if (err instanceof ArgosTokenError) {
44
+ ctx.contextValue.argosUser = void 0;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ };
50
+ }
51
+ };
52
+ }
53
+ function createGuard(client) {
54
+ return function guard(id, level, resolver, method) {
55
+ const resolvedMethod = method ?? detectMethod(id);
56
+ registerOperation({ identifier: id, method: resolvedMethod, level });
57
+ return (parent, args, context, info) => {
58
+ if (level === "public") return resolver(parent, args, context, info);
59
+ if (!context.argosUser) throw new Error("Unauthorized: Bearer token required.");
60
+ if (!client.checkPermission(context.argosUser, id, resolvedMethod)) {
61
+ throw new Error("Forbidden: Insufficient permissions.");
62
+ }
63
+ return resolver(parent, args, context, info);
64
+ };
65
+ };
66
+ }
67
+
68
+ exports.argosPlugin = argosPlugin;
69
+ exports.createGuard = createGuard;
70
+ //# sourceMappingURL=apollo.cjs.map
71
+ //# sourceMappingURL=apollo.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exceptions.ts","../../src/registry.ts","../../src/adapters/apollo.ts"],"names":[],"mappings":"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","file":"apollo.cjs","sourcesContent":["export class ArgosError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\nexport class ArgosAuthError extends ArgosError {}\nexport class ArgosTokenError extends ArgosError {}\nexport class ArgosForbiddenError extends ArgosError {}\nexport class ArgosSyncError extends ArgosError {}\nexport class ArgosConfigError extends ArgosError {}","import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import type { ApolloServerPlugin, BaseContext, GraphQLRequestContext } from '@apollo/server';\nimport { ArgosClient } from '../client.js';\nimport { ArgosTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\nexport interface ArgosContext extends BaseContext {\n argosUser?: TokenClaims;\n}\n\n/**\n * Apollo Server plugin for argos.\n *\n * Validates the Bearer token on every request and stores the claims in\n * `context.argosUser`. Use `createGuard(client)` to enforce per-resolver\n * permissions.\n */\nexport function argosPlugin(client: ArgosClient): ApolloServerPlugin<ArgosContext> {\n return {\n async requestDidStart() {\n return {\n async didResolveOperation(ctx: GraphQLRequestContext<ArgosContext>) {\n const httpAuth = ctx.request.http?.headers.get('authorization') ?? '';\n const wsAuth = ((ctx.contextValue as any)?.connectionParams?.Authorization as string | undefined) ?? '';\n const auth = httpAuth || wsAuth;\n\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n ctx.contextValue.argosUser = await client.validateToken(token);\n } catch (err) {\n if (err instanceof ArgosTokenError) {\n ctx.contextValue.argosUser = undefined;\n }\n }\n }\n },\n };\n },\n };\n}\n\n/**\n * Factory that returns a per-resolver guard bound to a `ArgosClient`.\n *\n * @example\n * ```ts\n * const guard = createGuard(client);\n *\n * const resolvers = {\n * Query: {\n * tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {\n * const user = context.argosUser;\n * return db.tasks.findAll();\n * }),\n * },\n * };\n * ```\n */\nexport function createGuard(client: ArgosClient) {\n return function guard<TParent, TArgs, TContext extends ArgosContext, TReturn>(\n id: string,\n level: string,\n resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn,\n method?: string,\n ): (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (parent, args, context, info) => {\n if (level === 'public') return resolver(parent, args, context, info);\n if (!context.argosUser) throw new Error('Unauthorized: Bearer token required.');\n if (!client.checkPermission(context.argosUser, id, resolvedMethod)) {\n throw new Error('Forbidden: Insufficient permissions.');\n }\n return resolver(parent, args, context, info);\n };\n };\n}"]}
@@ -0,0 +1,34 @@
1
+ import { BaseContext, ApolloServerPlugin } from '@apollo/server';
2
+ import { T as TokenClaims, A as ArgosClient } from '../client-Cgm1csuM.cjs';
3
+
4
+ interface ArgosContext extends BaseContext {
5
+ argosUser?: TokenClaims;
6
+ }
7
+ /**
8
+ * Apollo Server plugin for argos.
9
+ *
10
+ * Validates the Bearer token on every request and stores the claims in
11
+ * `context.argosUser`. Use `createGuard(client)` to enforce per-resolver
12
+ * permissions.
13
+ */
14
+ declare function argosPlugin(client: ArgosClient): ApolloServerPlugin<ArgosContext>;
15
+ /**
16
+ * Factory that returns a per-resolver guard bound to a `ArgosClient`.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const guard = createGuard(client);
21
+ *
22
+ * const resolvers = {
23
+ * Query: {
24
+ * tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {
25
+ * const user = context.argosUser;
26
+ * return db.tasks.findAll();
27
+ * }),
28
+ * },
29
+ * };
30
+ * ```
31
+ */
32
+ declare function createGuard(client: ArgosClient): <TParent, TArgs, TContext extends ArgosContext, TReturn>(id: string, level: string, resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn, method?: string) => (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn;
33
+
34
+ export { type ArgosContext, argosPlugin, createGuard };
@@ -0,0 +1,34 @@
1
+ import { BaseContext, ApolloServerPlugin } from '@apollo/server';
2
+ import { T as TokenClaims, A as ArgosClient } from '../client-Cgm1csuM.js';
3
+
4
+ interface ArgosContext extends BaseContext {
5
+ argosUser?: TokenClaims;
6
+ }
7
+ /**
8
+ * Apollo Server plugin for argos.
9
+ *
10
+ * Validates the Bearer token on every request and stores the claims in
11
+ * `context.argosUser`. Use `createGuard(client)` to enforce per-resolver
12
+ * permissions.
13
+ */
14
+ declare function argosPlugin(client: ArgosClient): ApolloServerPlugin<ArgosContext>;
15
+ /**
16
+ * Factory that returns a per-resolver guard bound to a `ArgosClient`.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const guard = createGuard(client);
21
+ *
22
+ * const resolvers = {
23
+ * Query: {
24
+ * tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {
25
+ * const user = context.argosUser;
26
+ * return db.tasks.findAll();
27
+ * }),
28
+ * },
29
+ * };
30
+ * ```
31
+ */
32
+ declare function createGuard(client: ArgosClient): <TParent, TArgs, TContext extends ArgosContext, TReturn>(id: string, level: string, resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn, method?: string) => (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn;
33
+
34
+ export { type ArgosContext, argosPlugin, createGuard };
@@ -0,0 +1,45 @@
1
+ import { ArgosTokenError } from '../chunk-AKUM2UEO.js';
2
+ import { detectMethod, registerOperation } from '../chunk-HV6X5RJY.js';
3
+
4
+ // src/adapters/apollo.ts
5
+ function argosPlugin(client) {
6
+ return {
7
+ async requestDidStart() {
8
+ return {
9
+ async didResolveOperation(ctx) {
10
+ const httpAuth = ctx.request.http?.headers.get("authorization") ?? "";
11
+ const wsAuth = ctx.contextValue?.connectionParams?.Authorization ?? "";
12
+ const auth = httpAuth || wsAuth;
13
+ const [scheme, token] = auth.split(" ");
14
+ if (scheme?.toLowerCase() === "bearer" && token) {
15
+ try {
16
+ ctx.contextValue.argosUser = await client.validateToken(token);
17
+ } catch (err) {
18
+ if (err instanceof ArgosTokenError) {
19
+ ctx.contextValue.argosUser = void 0;
20
+ }
21
+ }
22
+ }
23
+ }
24
+ };
25
+ }
26
+ };
27
+ }
28
+ function createGuard(client) {
29
+ return function guard(id, level, resolver, method) {
30
+ const resolvedMethod = method ?? detectMethod(id);
31
+ registerOperation({ identifier: id, method: resolvedMethod, level });
32
+ return (parent, args, context, info) => {
33
+ if (level === "public") return resolver(parent, args, context, info);
34
+ if (!context.argosUser) throw new Error("Unauthorized: Bearer token required.");
35
+ if (!client.checkPermission(context.argosUser, id, resolvedMethod)) {
36
+ throw new Error("Forbidden: Insufficient permissions.");
37
+ }
38
+ return resolver(parent, args, context, info);
39
+ };
40
+ };
41
+ }
42
+
43
+ export { argosPlugin, createGuard };
44
+ //# sourceMappingURL=apollo.js.map
45
+ //# sourceMappingURL=apollo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/apollo.ts"],"names":[],"mappings":";;;;AAiBO,SAAS,YAAY,MAAA,EAAuD;AACjF,EAAA,OAAO;AAAA,IACL,MAAM,eAAA,GAAkB;AACtB,MAAA,OAAO;AAAA,QACL,MAAM,oBAAoB,GAAA,EAA0C;AAClE,UAAA,MAAM,WAAY,GAAA,CAAI,OAAA,CAAQ,MAAM,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA,IAAK,EAAA;AACpE,UAAA,MAAM,MAAA,GAAc,GAAA,CAAI,YAAA,EAAsB,gBAAA,EAAkB,aAAA,IAAwC,EAAA;AACxG,UAAA,MAAM,OAAY,QAAA,IAAY,MAAA;AAE9B,UAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,UAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,YAAA,IAAI;AACF,cAAA,GAAA,CAAI,YAAA,CAAa,SAAA,GAAY,MAAM,MAAA,CAAO,cAAc,KAAK,CAAA;AAAA,YAC/D,SAAS,GAAA,EAAK;AACZ,cAAA,IAAI,eAAe,eAAA,EAAiB;AAClC,gBAAA,GAAA,CAAI,aAAa,SAAA,GAAY,MAAA;AAAA,cAC/B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,OACF;AAAA,IACF;AAAA,GACF;AACF;AAmBO,SAAS,YAAY,MAAA,EAAqB;AAC/C,EAAA,OAAO,SAAS,KAAA,CACd,EAAA,EACA,KAAA,EACA,UACA,MAAA,EAC6E;AAC7E,IAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,IAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,IAAA,OAAO,CAAC,MAAA,EAAQ,IAAA,EAAM,OAAA,EAAS,IAAA,KAAS;AACtC,MAAA,IAAI,UAAU,QAAA,EAAU,OAAO,SAAS,MAAA,EAAQ,IAAA,EAAM,SAAS,IAAI,CAAA;AACnE,MAAA,IAAI,CAAC,OAAA,CAAQ,SAAA,EAAW,MAAM,IAAI,MAAM,sCAAsC,CAAA;AAC9E,MAAA,IAAI,CAAC,MAAA,CAAO,eAAA,CAAgB,QAAQ,SAAA,EAAW,EAAA,EAAI,cAAc,CAAA,EAAG;AAClE,QAAA,MAAM,IAAI,MAAM,sCAAsC,CAAA;AAAA,MACxD;AACA,MAAA,OAAO,QAAA,CAAS,MAAA,EAAQ,IAAA,EAAM,OAAA,EAAS,IAAI,CAAA;AAAA,IAC7C,CAAA;AAAA,EACF,CAAA;AACF","file":"apollo.js","sourcesContent":["import type { ApolloServerPlugin, BaseContext, GraphQLRequestContext } from '@apollo/server';\nimport { ArgosClient } from '../client.js';\nimport { ArgosTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\nexport interface ArgosContext extends BaseContext {\n argosUser?: TokenClaims;\n}\n\n/**\n * Apollo Server plugin for argos.\n *\n * Validates the Bearer token on every request and stores the claims in\n * `context.argosUser`. Use `createGuard(client)` to enforce per-resolver\n * permissions.\n */\nexport function argosPlugin(client: ArgosClient): ApolloServerPlugin<ArgosContext> {\n return {\n async requestDidStart() {\n return {\n async didResolveOperation(ctx: GraphQLRequestContext<ArgosContext>) {\n const httpAuth = ctx.request.http?.headers.get('authorization') ?? '';\n const wsAuth = ((ctx.contextValue as any)?.connectionParams?.Authorization as string | undefined) ?? '';\n const auth = httpAuth || wsAuth;\n\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n ctx.contextValue.argosUser = await client.validateToken(token);\n } catch (err) {\n if (err instanceof ArgosTokenError) {\n ctx.contextValue.argosUser = undefined;\n }\n }\n }\n },\n };\n },\n };\n}\n\n/**\n * Factory that returns a per-resolver guard bound to a `ArgosClient`.\n *\n * @example\n * ```ts\n * const guard = createGuard(client);\n *\n * const resolvers = {\n * Query: {\n * tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {\n * const user = context.argosUser;\n * return db.tasks.findAll();\n * }),\n * },\n * };\n * ```\n */\nexport function createGuard(client: ArgosClient) {\n return function guard<TParent, TArgs, TContext extends ArgosContext, TReturn>(\n id: string,\n level: string,\n resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn,\n method?: string,\n ): (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (parent, args, context, info) => {\n if (level === 'public') return resolver(parent, args, context, info);\n if (!context.argosUser) throw new Error('Unauthorized: Bearer token required.');\n if (!client.checkPermission(context.argosUser, id, resolvedMethod)) {\n throw new Error('Forbidden: Insufficient permissions.');\n }\n return resolver(parent, args, context, info);\n };\n };\n}"]}
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ // src/registry.ts
4
+ var _registry = [];
5
+ function detectMethod(id) {
6
+ const last = (id.split(":").pop() ?? "").toLowerCase();
7
+ if (/^(get|list|fetch|read)/.test(last)) return "read";
8
+ if (/^(delete|remove|destroy)/.test(last)) return "delete";
9
+ if (/(stream|subscribe|watch|listen)/.test(last)) return "stream";
10
+ return "write";
11
+ }
12
+ function registerOperation(op) {
13
+ const exists = _registry.some(
14
+ (o) => o.identifier === op.identifier && o.method === op.method
15
+ );
16
+ if (!exists) _registry.push(op);
17
+ }
18
+
19
+ // src/adapters/express.ts
20
+ function argos(client) {
21
+ const middleware = async (req, _res, next) => {
22
+ const auth = req.headers.authorization ?? "";
23
+ const [scheme, token] = auth.split(" ");
24
+ if (scheme?.toLowerCase() === "bearer" && token) {
25
+ try {
26
+ req.argosUser = await client.validateToken(token);
27
+ } catch {
28
+ }
29
+ }
30
+ next();
31
+ };
32
+ const guard = (id, level, method) => {
33
+ const resolvedMethod = method ?? detectMethod(id);
34
+ registerOperation({ identifier: id, method: resolvedMethod, level });
35
+ return (req, res, next) => {
36
+ if (level === "public") {
37
+ next();
38
+ return;
39
+ }
40
+ if (!req.argosUser) {
41
+ res.status(401).json({ error: "unauthorized", detail: "Bearer token required." });
42
+ return;
43
+ }
44
+ const allowed = client.checkPermission(req.argosUser, id, resolvedMethod);
45
+ if (!allowed) {
46
+ res.status(403).json({ error: "forbidden", detail: "Insufficient permissions." });
47
+ return;
48
+ }
49
+ next();
50
+ };
51
+ };
52
+ return { middleware, guard };
53
+ }
54
+
55
+ exports.argos = argos;
56
+ //# sourceMappingURL=express.cjs.map
57
+ //# sourceMappingURL=express.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/registry.ts","../../src/adapters/express.ts"],"names":[],"mappings":";;;AAEA,IAAM,YAAmC,EAAC;AAKnC,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,MAAM,IAAA,GAAA,CAAQ,GAAG,KAAA,CAAM,GAAG,EAAE,GAAA,EAAI,IAAK,IAAI,WAAA,EAAY;AACrD,EAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,IAAI,CAAA,EAAS,OAAO,MAAA;AACtD,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA,EAAQ,OAAO,QAAA;AACvD,EAAA,IAAI,iCAAA,CAAkC,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,QAAA;AACzD,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,kBAAkB,EAAA,EAA+B;AAC/D,EAAA,MAAM,SAAS,SAAA,CAAU,IAAA;AAAA,IACvB,OAAK,CAAA,CAAE,UAAA,KAAe,GAAG,UAAA,IAAc,CAAA,CAAE,WAAW,EAAA,CAAG;AAAA,GACzD;AACA,EAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAChC;;;ACCO,SAAS,MAAM,MAAA,EAAsC;AAE1D,EAAA,MAAM,UAAA,GAAa,OAAO,GAAA,EAAc,IAAA,EAAgB,IAAA,KAAsC;AAC5F,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,OAAA,CAAQ,aAAA,IAAiB,EAAA;AAC1C,IAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,IAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,MAAA,IAAI;AACF,QAAA,GAAA,CAAI,SAAA,GAAY,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,MAClD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,IAAA,EAAK;AAAA,EACP,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,EAAY,KAAA,EAAe,MAAA,KAAoB;AAC5D,IAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,IAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,IAAA,OAAO,CAAC,GAAA,EAAc,GAAA,EAAe,IAAA,KAA6B;AAChE,MAAA,IAAI,UAAU,QAAA,EAAU;AAAE,QAAA,IAAA,EAAK;AAAG,QAAA;AAAA,MAAQ;AAE1C,MAAA,IAAI,CAAC,IAAI,SAAA,EAAW;AAClB,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,MAAA,EAAQ,wBAAA,EAA0B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,GAAA,CAAI,SAAA,EAAW,IAAI,cAAc,CAAA;AACxE,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,2BAAA,EAA6B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,YAAY,KAAA,EAAM;AAC7B","file":"express.cjs","sourcesContent":["import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import type { NextFunction, Request, Response } from 'express';\nimport { ArgosClient } from '../client.js';\nimport { ArgosTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n argosUser?: TokenClaims;\n }\n }\n}\n\nexport interface ArgosMiddleware {\n /** Global middleware — validates the Bearer token and injects `req.argosUser`. */\n middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;\n /** Per-route guard — registers the operation and enforces permission at request time. */\n guard: (id: string, level: string, method?: string) =>\n (req: Request, res: Response, next: NextFunction) => void;\n}\n\nexport function argos(client: ArgosClient): ArgosMiddleware {\n\n const middleware = async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n const auth = req.headers.authorization ?? '';\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n req.argosUser = await client.validateToken(token);\n } catch {\n // invalid token — argosUser stays undefined; guard handles the 401\n }\n }\n next();\n };\n\n const guard = (id: string, level: string, method?: string) => {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (req: Request, res: Response, next: NextFunction): void => {\n if (level === 'public') { next(); return; }\n\n if (!req.argosUser) {\n res.status(401).json({ error: 'unauthorized', detail: 'Bearer token required.' });\n return;\n }\n\n const allowed = client.checkPermission(req.argosUser, id, resolvedMethod);\n if (!allowed) {\n res.status(403).json({ error: 'forbidden', detail: 'Insufficient permissions.' });\n return;\n }\n\n next();\n };\n };\n\n return { middleware, guard };\n}"]}
@@ -0,0 +1,19 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { T as TokenClaims, A as ArgosClient } from '../client-Cgm1csuM.cjs';
3
+
4
+ declare global {
5
+ namespace Express {
6
+ interface Request {
7
+ argosUser?: TokenClaims;
8
+ }
9
+ }
10
+ }
11
+ interface ArgosMiddleware {
12
+ /** Global middleware — validates the Bearer token and injects `req.argosUser`. */
13
+ middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;
14
+ /** Per-route guard — registers the operation and enforces permission at request time. */
15
+ guard: (id: string, level: string, method?: string) => (req: Request, res: Response, next: NextFunction) => void;
16
+ }
17
+ declare function argos(client: ArgosClient): ArgosMiddleware;
18
+
19
+ export { type ArgosMiddleware, argos };
@@ -0,0 +1,19 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { T as TokenClaims, A as ArgosClient } from '../client-Cgm1csuM.js';
3
+
4
+ declare global {
5
+ namespace Express {
6
+ interface Request {
7
+ argosUser?: TokenClaims;
8
+ }
9
+ }
10
+ }
11
+ interface ArgosMiddleware {
12
+ /** Global middleware — validates the Bearer token and injects `req.argosUser`. */
13
+ middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;
14
+ /** Per-route guard — registers the operation and enforces permission at request time. */
15
+ guard: (id: string, level: string, method?: string) => (req: Request, res: Response, next: NextFunction) => void;
16
+ }
17
+ declare function argos(client: ArgosClient): ArgosMiddleware;
18
+
19
+ export { type ArgosMiddleware, argos };
@@ -0,0 +1,41 @@
1
+ import { detectMethod, registerOperation } from '../chunk-HV6X5RJY.js';
2
+
3
+ // src/adapters/express.ts
4
+ function argos(client) {
5
+ const middleware = async (req, _res, next) => {
6
+ const auth = req.headers.authorization ?? "";
7
+ const [scheme, token] = auth.split(" ");
8
+ if (scheme?.toLowerCase() === "bearer" && token) {
9
+ try {
10
+ req.argosUser = await client.validateToken(token);
11
+ } catch {
12
+ }
13
+ }
14
+ next();
15
+ };
16
+ const guard = (id, level, method) => {
17
+ const resolvedMethod = method ?? detectMethod(id);
18
+ registerOperation({ identifier: id, method: resolvedMethod, level });
19
+ return (req, res, next) => {
20
+ if (level === "public") {
21
+ next();
22
+ return;
23
+ }
24
+ if (!req.argosUser) {
25
+ res.status(401).json({ error: "unauthorized", detail: "Bearer token required." });
26
+ return;
27
+ }
28
+ const allowed = client.checkPermission(req.argosUser, id, resolvedMethod);
29
+ if (!allowed) {
30
+ res.status(403).json({ error: "forbidden", detail: "Insufficient permissions." });
31
+ return;
32
+ }
33
+ next();
34
+ };
35
+ };
36
+ return { middleware, guard };
37
+ }
38
+
39
+ export { argos };
40
+ //# sourceMappingURL=express.js.map
41
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/express.ts"],"names":[],"mappings":";;;AAsBO,SAAS,MAAM,MAAA,EAAsC;AAE1D,EAAA,MAAM,UAAA,GAAa,OAAO,GAAA,EAAc,IAAA,EAAgB,IAAA,KAAsC;AAC5F,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,OAAA,CAAQ,aAAA,IAAiB,EAAA;AAC1C,IAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,IAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,MAAA,IAAI;AACF,QAAA,GAAA,CAAI,SAAA,GAAY,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,MAClD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,IAAA,EAAK;AAAA,EACP,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,EAAY,KAAA,EAAe,MAAA,KAAoB;AAC5D,IAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,IAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,IAAA,OAAO,CAAC,GAAA,EAAc,GAAA,EAAe,IAAA,KAA6B;AAChE,MAAA,IAAI,UAAU,QAAA,EAAU;AAAE,QAAA,IAAA,EAAK;AAAG,QAAA;AAAA,MAAQ;AAE1C,MAAA,IAAI,CAAC,IAAI,SAAA,EAAW;AAClB,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,MAAA,EAAQ,wBAAA,EAA0B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,GAAA,CAAI,SAAA,EAAW,IAAI,cAAc,CAAA;AACxE,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,2BAAA,EAA6B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,YAAY,KAAA,EAAM;AAC7B","file":"express.js","sourcesContent":["import type { NextFunction, Request, Response } from 'express';\nimport { ArgosClient } from '../client.js';\nimport { ArgosTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n argosUser?: TokenClaims;\n }\n }\n}\n\nexport interface ArgosMiddleware {\n /** Global middleware — validates the Bearer token and injects `req.argosUser`. */\n middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;\n /** Per-route guard — registers the operation and enforces permission at request time. */\n guard: (id: string, level: string, method?: string) =>\n (req: Request, res: Response, next: NextFunction) => void;\n}\n\nexport function argos(client: ArgosClient): ArgosMiddleware {\n\n const middleware = async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n const auth = req.headers.authorization ?? '';\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n req.argosUser = await client.validateToken(token);\n } catch {\n // invalid token — argosUser stays undefined; guard handles the 401\n }\n }\n next();\n };\n\n const guard = (id: string, level: string, method?: string) => {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (req: Request, res: Response, next: NextFunction): void => {\n if (level === 'public') { next(); return; }\n\n if (!req.argosUser) {\n res.status(401).json({ error: 'unauthorized', detail: 'Bearer token required.' });\n return;\n }\n\n const allowed = client.checkPermission(req.argosUser, id, resolvedMethod);\n if (!allowed) {\n res.status(403).json({ error: 'forbidden', detail: 'Insufficient permissions.' });\n return;\n }\n\n next();\n };\n };\n\n return { middleware, guard };\n}"]}
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ var fp = require('fastify-plugin');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var fp__default = /*#__PURE__*/_interopDefault(fp);
8
+
9
+ // src/adapters/fastify.ts
10
+
11
+ // src/registry.ts
12
+ var _registry = [];
13
+ function detectMethod(id) {
14
+ const last = (id.split(":").pop() ?? "").toLowerCase();
15
+ if (/^(get|list|fetch|read)/.test(last)) return "read";
16
+ if (/^(delete|remove|destroy)/.test(last)) return "delete";
17
+ if (/(stream|subscribe|watch|listen)/.test(last)) return "stream";
18
+ return "write";
19
+ }
20
+ function registerOperation(op) {
21
+ const exists = _registry.some(
22
+ (o) => o.identifier === op.identifier && o.method === op.method
23
+ );
24
+ if (!exists) _registry.push(op);
25
+ }
26
+
27
+ // src/adapters/fastify.ts
28
+ var argosPlugin = async (fastify, { client }) => {
29
+ fastify.decorateRequest("argosUser", void 0);
30
+ fastify.addHook("onRequest", async (request) => {
31
+ const auth = request.headers.authorization ?? "";
32
+ const [scheme, token] = auth.split(" ");
33
+ if (scheme?.toLowerCase() === "bearer" && token) {
34
+ try {
35
+ request.argosUser = await client.validateToken(token);
36
+ } catch {
37
+ }
38
+ }
39
+ });
40
+ fastify.decorate(
41
+ "argosGuard",
42
+ (id, level, method) => {
43
+ const resolvedMethod = method ?? detectMethod(id);
44
+ registerOperation({ identifier: id, method: resolvedMethod, level });
45
+ return async (request, reply) => {
46
+ if (level === "public") return;
47
+ if (!request.argosUser) {
48
+ await reply.status(401).send({ error: "unauthorized", detail: "Bearer token required." });
49
+ return;
50
+ }
51
+ const allowed = client.checkPermission(request.argosUser, id, resolvedMethod);
52
+ if (!allowed) {
53
+ await reply.status(403).send({ error: "forbidden", detail: "Insufficient permissions." });
54
+ }
55
+ };
56
+ }
57
+ );
58
+ };
59
+ var argosFastifyPlugin = fp__default.default(argosPlugin, {
60
+ name: "argos",
61
+ fastify: ">=4"
62
+ });
63
+
64
+ exports.argosFastifyPlugin = argosFastifyPlugin;
65
+ //# sourceMappingURL=fastify.cjs.map
66
+ //# sourceMappingURL=fastify.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/registry.ts","../../src/adapters/fastify.ts"],"names":["fp"],"mappings":";;;;;;;;;;;AAEA,IAAM,YAAmC,EAAC;AAKnC,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,MAAM,IAAA,GAAA,CAAQ,GAAG,KAAA,CAAM,GAAG,EAAE,GAAA,EAAI,IAAK,IAAI,WAAA,EAAY;AACrD,EAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,IAAI,CAAA,EAAS,OAAO,MAAA;AACtD,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA,EAAQ,OAAO,QAAA;AACvD,EAAA,IAAI,iCAAA,CAAkC,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,QAAA;AACzD,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,kBAAkB,EAAA,EAA+B;AAC/D,EAAA,MAAM,SAAS,SAAA,CAAU,IAAA;AAAA,IACvB,OAAK,CAAA,CAAE,UAAA,KAAe,GAAG,UAAA,IAAc,CAAA,CAAE,WAAW,EAAA,CAAG;AAAA,GACzD;AACA,EAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAChC;;;ACLA,IAAM,WAAA,GAAsD,OAC1D,OAAA,EACA,EAAE,QAAO,KACN;AAEH,EAAA,OAAA,CAAQ,eAAA,CAAyC,aAAa,MAAS,CAAA;AAGvE,EAAA,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,OAAO,OAAA,KAA4B;AAC9D,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,aAAA,IAAiB,EAAA;AAC9C,IAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,IAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,SAAA,GAAY,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,MACtD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAGD,EAAA,OAAA,CAAQ,QAAA;AAAA,IACN,YAAA;AAAA,IACA,CAAC,EAAA,EAAY,KAAA,EAAe,MAAA,KAAoB;AAC9C,MAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,MAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,MAAA,OAAO,OAAO,SAAyB,KAAA,KAAuC;AAC5E,QAAA,IAAI,UAAU,QAAA,EAAU;AAExB,QAAA,IAAI,CAAC,QAAQ,SAAA,EAAW;AACtB,UAAA,MAAM,KAAA,CAAM,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,MAAA,EAAQ,wBAAA,EAA0B,CAAA;AACxF,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,OAAA,CAAQ,SAAA,EAAW,IAAI,cAAc,CAAA;AAC5E,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,KAAA,CAAM,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,2BAAA,EAA6B,CAAA;AAAA,QAC1F;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACF;AACF,CAAA;AAEO,IAAM,kBAAA,GAAqBA,oBAAG,WAAA,EAAa;AAAA,EAChD,IAAA,EAAM,OAAA;AAAA,EACN,OAAA,EAAS;AACX,CAAC","file":"fastify.cjs","sourcesContent":["import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { ArgosClient } from '../client.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n argosUser?: TokenClaims;\n }\n}\n\nexport interface ArgosPluginOptions {\n client: ArgosClient;\n}\n\nconst argosPlugin: FastifyPluginAsync<ArgosPluginOptions> = async (\n fastify: FastifyInstance,\n { client }: ArgosPluginOptions,\n) => {\n // Decorate request with argosUser\n fastify.decorateRequest<TokenClaims | undefined>('argosUser', undefined);\n\n // Global hook — validates Bearer token on every request\n fastify.addHook('onRequest', async (request: FastifyRequest) => {\n const auth = request.headers.authorization ?? '';\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n request.argosUser = await client.validateToken(token);\n } catch {\n // invalid token — argosUser stays null; guard handles the 401\n }\n }\n });\n\n // Decorate fastify with a guard factory\n fastify.decorate(\n 'argosGuard',\n (id: string, level: string, method?: string) => {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {\n if (level === 'public') return;\n\n if (!request.argosUser) {\n await reply.status(401).send({ error: 'unauthorized', detail: 'Bearer token required.' });\n return;\n }\n\n const allowed = client.checkPermission(request.argosUser, id, resolvedMethod);\n if (!allowed) {\n await reply.status(403).send({ error: 'forbidden', detail: 'Insufficient permissions.' });\n }\n };\n },\n );\n};\n\nexport const argosFastifyPlugin = fp(argosPlugin, {\n name: 'argos',\n fastify: '>=4',\n});\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n argosGuard: (\n id: string,\n level: string,\n method?: string,\n ) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;\n }\n}"]}