@facetlayer/prism-framework 0.4.0 → 0.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 +176 -8
- package/dist/Errors.d.ts +38 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Metrics.d.ts +5 -0
- package/dist/Metrics.d.ts.map +1 -0
- package/dist/RequestContext.d.ts +17 -0
- package/dist/RequestContext.d.ts.map +1 -0
- package/dist/ServiceDefinition.d.ts +16 -0
- package/dist/ServiceDefinition.d.ts.map +1 -0
- package/dist/app/PrismApp.d.ts +31 -0
- package/dist/app/PrismApp.d.ts.map +1 -0
- package/dist/app/callEndpoint.d.ts +13 -0
- package/dist/app/callEndpoint.d.ts.map +1 -0
- package/dist/app/validateApp.d.ts +20 -0
- package/dist/app/validateApp.d.ts.map +1 -0
- package/dist/authorization/AuthSource.d.ts +8 -0
- package/dist/authorization/AuthSource.d.ts.map +1 -0
- package/dist/authorization/Authorization.d.ts +24 -0
- package/dist/authorization/Authorization.d.ts.map +1 -0
- package/dist/authorization/Resource.d.ts +5 -0
- package/dist/authorization/Resource.d.ts.map +1 -0
- package/dist/authorization/index.d.ts +5 -0
- package/dist/authorization/index.d.ts.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/databases/DatabaseInitializationOptions.d.ts +9 -0
- package/dist/databases/DatabaseInitializationOptions.d.ts.map +1 -0
- package/dist/databases/DatabaseSetup.d.ts +3 -0
- package/dist/databases/DatabaseSetup.d.ts.map +1 -0
- package/dist/endpoints/createEndpoint.d.ts +4 -0
- package/dist/endpoints/createEndpoint.d.ts.map +1 -0
- package/dist/endpoints/getEffectiveOperationId.d.ts +19 -0
- package/dist/endpoints/getEffectiveOperationId.d.ts.map +1 -0
- package/dist/env/Env.d.ts +2 -0
- package/dist/env/Env.d.ts.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1364 -0
- package/dist/launch/launchConfig.d.ts +18 -0
- package/dist/launch/launchConfig.d.ts.map +1 -0
- package/dist/logging/index.d.ts +9 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/sse/ConnectionManager.d.ts +23 -0
- package/dist/sse/ConnectionManager.d.ts.map +1 -0
- package/dist/stdin/StdinServer.d.ts +38 -0
- package/dist/stdin/StdinServer.d.ts.map +1 -0
- package/dist/web/EndpointListing.d.ts +3 -0
- package/dist/web/EndpointListing.d.ts.map +1 -0
- package/dist/web/ExpressAppSetup.d.ts +18 -0
- package/dist/web/ExpressAppSetup.d.ts.map +1 -0
- package/dist/web/ExpressEndpointSetup.d.ts +31 -0
- package/dist/web/ExpressEndpointSetup.d.ts.map +1 -0
- package/dist/web/SseResponse.d.ts +15 -0
- package/dist/web/SseResponse.d.ts.map +1 -0
- package/dist/web/ViteIntegration.d.ts +19 -0
- package/dist/web/ViteIntegration.d.ts.map +1 -0
- package/dist/web/corsMiddleware.d.ts +14 -0
- package/dist/web/corsMiddleware.d.ts.map +1 -0
- package/dist/web/localhostOnlyMiddleware.d.ts +3 -0
- package/dist/web/localhostOnlyMiddleware.d.ts.map +1 -0
- package/dist/web/openapi/OpenAPI.d.ts +37 -0
- package/dist/web/openapi/OpenAPI.d.ts.map +1 -0
- package/dist/web/openapi/validateServicesForOpenapi.d.ts +32 -0
- package/dist/web/openapi/validateServicesForOpenapi.d.ts.map +1 -0
- package/dist/web/requestContextMiddleware.d.ts +3 -0
- package/dist/web/requestContextMiddleware.d.ts.map +1 -0
- package/docs/authorization.md +281 -0
- package/docs/cors-setup.md +172 -0
- package/docs/creating-services.md +220 -0
- package/docs/database-setup.md +134 -0
- package/docs/endpoint-tools.md +1 -11
- package/docs/env-files.md +12 -1
- package/docs/error-handling.md +70 -0
- package/docs/getting-started.md +22 -12
- package/docs/launch-configuration.md +223 -0
- package/docs/overview.md +62 -0
- package/docs/server-setup.md +144 -0
- package/docs/source-directory-organization.md +115 -0
- package/docs/stdin-protocol.md +176 -0
- package/package.json +42 -9
- package/src/Errors.ts +120 -0
- package/src/Metrics.ts +53 -0
- package/src/RequestContext.ts +36 -0
- package/src/ServiceDefinition.ts +35 -0
- package/src/__tests__/Authorization.test.ts +350 -0
- package/src/__tests__/Errors.test.ts +378 -0
- package/src/__tests__/ListEndpoints.test.ts +98 -0
- package/src/__tests__/PrismApp.test.ts +274 -0
- package/src/__tests__/RequestContext.test.ts +295 -0
- package/src/__tests__/SseResponse.test.ts +189 -0
- package/src/__tests__/StdinServer.test.ts +304 -0
- package/src/__tests__/corsMiddleware.test.ts +293 -0
- package/src/__tests__/createEndpoint.test.ts +412 -0
- package/src/__tests__/validateApp.test.ts +206 -0
- package/src/app/PrismApp.ts +117 -0
- package/src/app/callEndpoint.ts +55 -0
- package/src/app/validateApp.ts +78 -0
- package/src/authorization/AuthSource.ts +14 -0
- package/src/authorization/Authorization.ts +78 -0
- package/src/authorization/Resource.ts +8 -0
- package/src/authorization/index.ts +4 -0
- package/src/databases/DatabaseInitializationOptions.ts +9 -0
- package/src/databases/DatabaseSetup.ts +19 -0
- package/src/endpoints/createEndpoint.ts +39 -0
- package/src/endpoints/getEffectiveOperationId.ts +90 -0
- package/src/env/Env.ts +23 -0
- package/src/index.ts +78 -0
- package/src/launch/launchConfig.ts +59 -0
- package/src/list-endpoints-command.ts +1 -1
- package/src/logging/index.ts +25 -0
- package/src/sse/ConnectionManager.ts +79 -0
- package/src/stdin/StdinServer.ts +129 -0
- package/src/web/EndpointListing.ts +166 -0
- package/src/web/ExpressAppSetup.ts +125 -0
- package/src/web/ExpressEndpointSetup.ts +178 -0
- package/src/web/SseResponse.ts +78 -0
- package/src/web/ViteIntegration.ts +72 -0
- package/src/web/__tests__/OpenAPI.invalidZodSchemas.test.ts +250 -0
- package/src/web/corsMiddleware.ts +63 -0
- package/src/web/localhostOnlyMiddleware.ts +19 -0
- package/src/web/openapi/OpenAPI.ts +248 -0
- package/src/web/openapi/validateServicesForOpenapi.ts +76 -0
- package/src/web/requestContextMiddleware.ts +25 -0
- package/.claude/settings.local.json +0 -20
- package/CHANGELOG +0 -28
- package/CLAUDE.md +0 -44
- package/build.mts +0 -8
- package/test/call-command.test.ts +0 -96
- package/test/generate-api-clients.test.ts +0 -33
- package/test/generate-api-clients.test.ts.disabled +0 -75
- package/tsconfig.json +0 -21
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DatabaseInitializationOptions } from '../databases/DatabaseInitializationOptions.ts';
|
|
2
|
+
import type { LoadDatabaseFn } from '@facetlayer/sqlite-wrapper';
|
|
3
|
+
export interface LoggingSettings {
|
|
4
|
+
databaseFilename: string;
|
|
5
|
+
enableConsoleLogging: boolean;
|
|
6
|
+
loadDatabase: LoadDatabaseFn;
|
|
7
|
+
}
|
|
8
|
+
export interface LaunchConfig {
|
|
9
|
+
logging?: LoggingSettings;
|
|
10
|
+
database?: {
|
|
11
|
+
[databaseName: string]: DatabaseInitializationOptions;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function getLaunchConfig(): LaunchConfig;
|
|
15
|
+
export declare function setLaunchConfig(config: LaunchConfig): void;
|
|
16
|
+
export declare function getDatabaseConfig(databaseName: string): DatabaseInitializationOptions;
|
|
17
|
+
export declare function getLoggingConfig(): LoggingSettings;
|
|
18
|
+
//# sourceMappingURL=launchConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchConfig.d.ts","sourceRoot":"","sources":["../../src/launch/launchConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,+CAA+C,CAAC;AACnG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AASjE,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,cAAc,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE;QACT,CAAC,YAAY,EAAE,MAAM,GAAG,6BAA6B,CAAC;KACvD,CAAC;CACH;AAID,wBAAgB,eAAe,IAAI,YAAY,CAK9C;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAK1D;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,6BAA6B,CAYrF;AAED,wBAAgB,gBAAgB,IAAI,eAAe,CAKlD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When enabled, logInfo writes to stderr instead of stdout.
|
|
3
|
+
* This is used in stdin protocol mode to avoid corrupting the JSON protocol on stdout.
|
|
4
|
+
*/
|
|
5
|
+
export declare function setLogStderr(enabled: boolean): void;
|
|
6
|
+
export declare function logInfo(...args: any[]): void;
|
|
7
|
+
export declare function logWarn(...args: any[]): void;
|
|
8
|
+
export declare function logError(...args: any[]): void;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logging/index.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEnD;AAED,wBAAgB,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,QAMrC;AAED,wBAAgB,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,QAErC;AAED,wBAAgB,QAAQ,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,QAEtC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SseResponse } from '../web/SseResponse.ts';
|
|
2
|
+
interface SetupOptions {
|
|
3
|
+
managerName: string;
|
|
4
|
+
logDebug?: (message: string) => void;
|
|
5
|
+
logError?: (message: string, error?: any) => void;
|
|
6
|
+
}
|
|
7
|
+
interface Connection {
|
|
8
|
+
key: string;
|
|
9
|
+
sseResponse: SseResponse;
|
|
10
|
+
connectedAt: Date;
|
|
11
|
+
}
|
|
12
|
+
export declare class ConnectionManager<EventType extends object> {
|
|
13
|
+
private connections;
|
|
14
|
+
private managerName;
|
|
15
|
+
private logDebug;
|
|
16
|
+
private logError;
|
|
17
|
+
constructor(options: SetupOptions);
|
|
18
|
+
addConnection(key: string, sseResponse: SseResponse): void;
|
|
19
|
+
getConnections(key: string): Connection[];
|
|
20
|
+
postEvent(key: string, event: EventType): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=ConnectionManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectionManager.d.ts","sourceRoot":"","sources":["../../src/sse/ConnectionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,UAAU,YAAY;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;CACnD;AAED,UAAU,UAAU;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,IAAI,CAAC;CACnB;AAWD,qBAAa,iBAAiB,CAAC,SAAS,SAAS,MAAM;IACrD,OAAO,CAAC,WAAW,CAAwC;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,QAAQ,CAAyC;gBAE7C,OAAO,EAAE,YAAY;IAMjC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;IA0B1D,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE;IAInC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAa9D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PrismApp } from '../app/PrismApp.ts';
|
|
2
|
+
/**
|
|
3
|
+
* JSON message sent from the parent process to the stdin server.
|
|
4
|
+
*/
|
|
5
|
+
export interface StdinRequest {
|
|
6
|
+
/** Unique request ID for correlating responses. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** HTTP method (GET, POST, PUT, DELETE, PATCH). */
|
|
9
|
+
method: string;
|
|
10
|
+
/** Endpoint path, e.g. "/users" or "/users/123". */
|
|
11
|
+
path: string;
|
|
12
|
+
/** Request body / input data. */
|
|
13
|
+
body?: any;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* JSON message sent back from the stdin server to the parent process.
|
|
17
|
+
*/
|
|
18
|
+
export interface StdinResponse {
|
|
19
|
+
/** Matches the request ID. */
|
|
20
|
+
id: string;
|
|
21
|
+
/** HTTP-style status code. */
|
|
22
|
+
status: number;
|
|
23
|
+
/** Response body. */
|
|
24
|
+
body: any;
|
|
25
|
+
}
|
|
26
|
+
export interface StdinServerConfig {
|
|
27
|
+
app: PrismApp;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Start processing requests from stdin and writing responses to stdout.
|
|
31
|
+
*
|
|
32
|
+
* Each line on stdin must be a JSON-encoded StdinRequest.
|
|
33
|
+
* Each response is a JSON-encoded StdinResponse written as a single line to stdout.
|
|
34
|
+
*
|
|
35
|
+
* When stdin closes, the process exits.
|
|
36
|
+
*/
|
|
37
|
+
export declare function startStdinServer(config: StdinServerConfig): void;
|
|
38
|
+
//# sourceMappingURL=StdinServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StdinServer.d.ts","sourceRoot":"","sources":["../../src/stdin/StdinServer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAK9C;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,IAAI,EAAE,GAAG,CAAC;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAoFhE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EndpointListing.d.ts","sourceRoot":"","sources":["../../src/web/EndpointListing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAKpF,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,kBAAkB,EAAE,wBA+DrE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import type { Server } from 'http';
|
|
3
|
+
import { PrismApp } from '../app/PrismApp.ts';
|
|
4
|
+
import { type CorsConfig } from './corsMiddleware.ts';
|
|
5
|
+
import { type OpenAPIConfig } from './openapi/OpenAPI.ts';
|
|
6
|
+
import { type WebConfig } from './ViteIntegration.ts';
|
|
7
|
+
export type { WebConfig } from './ViteIntegration.ts';
|
|
8
|
+
export interface ServerSetupConfig {
|
|
9
|
+
port: number;
|
|
10
|
+
app: PrismApp;
|
|
11
|
+
openapiConfig?: OpenAPIConfig;
|
|
12
|
+
corsConfig?: CorsConfig;
|
|
13
|
+
/** Serve web files alongside the API. When provided, API endpoints are mounted at /api/. */
|
|
14
|
+
web?: WebConfig;
|
|
15
|
+
}
|
|
16
|
+
export declare function createExpressApp(config: ServerSetupConfig): express.Application;
|
|
17
|
+
export declare function startServer(config: ServerSetupConfig): Promise<Server>;
|
|
18
|
+
//# sourceMappingURL=ExpressAppSetup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressAppSetup.d.ts","sourceRoot":"","sources":["../../src/web/ExpressAppSetup.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItE,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAKjF,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE1E,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,4FAA4F;IAC5F,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAoE/E;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2B5E"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import express, { type NextFunction, type Request, type Response } from 'express';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { PrismApp } from '../app/PrismApp.ts';
|
|
4
|
+
export { createEndpoint } from '../endpoints/createEndpoint.ts';
|
|
5
|
+
type EndpointRequireOption = 'authenticated-user';
|
|
6
|
+
export interface EndpointDefinition {
|
|
7
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
8
|
+
path: string;
|
|
9
|
+
handler: (input: any) => Promise<any> | any;
|
|
10
|
+
requestSchema?: z.ZodSchema;
|
|
11
|
+
responseSchema?: z.ZodSchema;
|
|
12
|
+
description?: string;
|
|
13
|
+
requires?: EndpointRequireOption[];
|
|
14
|
+
/**
|
|
15
|
+
* Unique identifier for this endpoint in the OpenAPI schema.
|
|
16
|
+
* If not provided, one will be generated from the method and path.
|
|
17
|
+
* Must be unique across all endpoints in the app.
|
|
18
|
+
*/
|
|
19
|
+
operationId?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function getRequestDataFromReq(req: Request): any;
|
|
22
|
+
export declare function mountPrismApp(app: express.Application | express.Router, prismApp: PrismApp): void;
|
|
23
|
+
export declare function mountMiddleware(app: express.Application, middleware: {
|
|
24
|
+
path: string;
|
|
25
|
+
handler: (req: Request, res: Response, next: NextFunction) => void;
|
|
26
|
+
}): void;
|
|
27
|
+
export declare function mountMiddlewares(app: express.Application, middlewares: {
|
|
28
|
+
path: string;
|
|
29
|
+
handler: (req: Request, res: Response, next: NextFunction) => void;
|
|
30
|
+
}[]): void;
|
|
31
|
+
//# sourceMappingURL=ExpressEndpointSetup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressEndpointSetup.d.ts","sourceRoot":"","sources":["../../src/web/ExpressEndpointSetup.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,QAAQ,EAAe,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,KAAK,qBAAqB,GAAG,oBAAoB,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAC5C,aAAa,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IAC5B,cAAc,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACnC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,CAgBvD;AAiFD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,EACzC,QAAQ,EAAE,QAAQ,GACjB,IAAI,CA2BN;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,CAAC,WAAW,EACxB,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;CAAE,GAC/F,IAAI,CAEN;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,OAAO,CAAC,WAAW,EACxB,WAAW,EAAE;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACpE,EAAE,GACF,IAAI,CAEN"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
export declare class SseResponse {
|
|
3
|
+
response: Response;
|
|
4
|
+
private isResponseOpen;
|
|
5
|
+
private _onClose;
|
|
6
|
+
constructor(response: Response);
|
|
7
|
+
private setupSseHeaders;
|
|
8
|
+
private setupCloseHandlers;
|
|
9
|
+
send(data: object): void;
|
|
10
|
+
isOpen(): boolean;
|
|
11
|
+
close(): void;
|
|
12
|
+
_triggerOnClose(): void;
|
|
13
|
+
onClose(callback: () => void): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=SseResponse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SseResponse.d.ts","sourceRoot":"","sources":["../../src/web/SseResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQxC,qBAAa,WAAW;IACf,QAAQ,EAAE,QAAQ,CAAC;IAC1B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAa;gBAEjB,QAAQ,EAAE,QAAQ;IAM9B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,kBAAkB;IAU1B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASxB,MAAM,IAAI,OAAO;IAKjB,KAAK,IAAI,IAAI;IAWb,eAAe,IAAI,IAAI;IASvB,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;CAMpC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
export interface WebConfig {
|
|
3
|
+
/** Directory containing web files (index.html, etc.). In production, serves from dir/dist if it exists. */
|
|
4
|
+
dir: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Sets up web file serving on the Express app. This should be called AFTER
|
|
8
|
+
* API routes are mounted so that /api/* takes priority.
|
|
9
|
+
*
|
|
10
|
+
* In development (NODE_ENV !== 'production'):
|
|
11
|
+
* - Tries to use Vite dev server in middleware mode
|
|
12
|
+
* - Falls back to express.static if Vite is not installed
|
|
13
|
+
*
|
|
14
|
+
* In production:
|
|
15
|
+
* - Serves static files from dir/dist (or dir if dist doesn't exist)
|
|
16
|
+
* - SPA fallback: unmatched GET requests serve index.html
|
|
17
|
+
*/
|
|
18
|
+
export declare function setupWebMiddleware(expressApp: express.Application, webConfig: WebConfig): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=ViteIntegration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ViteIntegration.d.ts","sourceRoot":"","sources":["../../src/web/ViteIntegration.ts"],"names":[],"mappings":"AAQA,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,MAAM,WAAW,SAAS;IACxB,2GAA2G;IAC3G,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,OAAO,CAAC,WAAW,EAC/B,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
export interface CorsConfig {
|
|
3
|
+
/** Base URL for web application (e.g., 'example.com' or 'https://example.com') */
|
|
4
|
+
webBaseUrl?: string;
|
|
5
|
+
/** Allow any localhost origin (http://localhost:*) for local development */
|
|
6
|
+
allowLocalhost?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Use `allowLocalhost` instead. This field controls both test endpoints and localhost CORS.
|
|
9
|
+
* When `allowLocalhost` is set, it takes precedence for CORS behavior.
|
|
10
|
+
*/
|
|
11
|
+
enableTestEndpoints?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function corsMiddleware(config?: CorsConfig): (req: express.Request, res: express.Response, next: express.NextFunction) => void;
|
|
14
|
+
//# sourceMappingURL=corsMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corsMiddleware.d.ts","sourceRoot":"","sources":["../../src/web/corsMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,WAAW,UAAU;IACzB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AA+BD,wBAAgB,cAAc,CAAC,MAAM,GAAE,UAAe,SACvC,OAAO,CAAC,OAAO,OAAO,OAAO,CAAC,QAAQ,QAAQ,OAAO,CAAC,YAAY,UAkBhF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localhostOnlyMiddleware.d.ts","sourceRoot":"","sources":["../../src/web/localhostOnlyMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQ/D,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,sCAUtF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
2
|
+
import type { ServiceDefinition } from '../../ServiceDefinition.ts';
|
|
3
|
+
import { PrismApp } from '../../app/PrismApp.ts';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
export { validateServicesForOpenapi as validateEndpointForOpenapi } from './validateServicesForOpenapi.ts';
|
|
6
|
+
export interface OpenAPIConfig {
|
|
7
|
+
enable: boolean;
|
|
8
|
+
enableSwagger?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export type ParseExpressPathForOpenAPIResult = {
|
|
11
|
+
openApiPath: string;
|
|
12
|
+
pathParams: string[];
|
|
13
|
+
};
|
|
14
|
+
export interface OpenAPIDocumentInfo {
|
|
15
|
+
version: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Transforms path parameters in an Express-style URL path (e.g. :pathParameter) into their OpenAPI equivalents (e.g. {pathParameter})
|
|
21
|
+
* Returns the transformed OpenAPI-style URL path along with any path parameters found.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} expressApiPath - An Express-style URL path
|
|
24
|
+
* @returns {ParseExpressPathForOpenAPIResult} - the transformed OpenAPI-style URL path with any path parameters found
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseExpressPathForOpenAPI(expressApiPath: string): ParseExpressPathForOpenAPIResult;
|
|
27
|
+
/**
|
|
28
|
+
* Generates the Open API Schema for all services.
|
|
29
|
+
*
|
|
30
|
+
* @param {ServiceDefinition[]} services - Array of service definitions to generate OpenAPI schema for
|
|
31
|
+
* @param {OpenAPIDocumentInfo} documentInfo - Metadata for the OpenAPI document
|
|
32
|
+
* @returns {OpenAPIObject} - the Open API schema for all service definitions.
|
|
33
|
+
*/
|
|
34
|
+
export declare function generateOpenAPISchema(services: ServiceDefinition[], documentInfo: OpenAPIDocumentInfo): OpenAPIObject;
|
|
35
|
+
export declare function setupSwaggerUI(app: express.Application | express.Router, openApiJsonPath?: string): void;
|
|
36
|
+
export declare function mountOpenAPIEndpoints(config: OpenAPIConfig, target: express.Application | express.Router, prismApp: PrismApp): void;
|
|
37
|
+
//# sourceMappingURL=OpenAPI.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAPI.d.ts","sourceRoot":"","sources":["../../../src/web/openapi/OpenAPI.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,OAAwC,MAAM,SAAS,CAAC;AAK/D,OAAO,EAAE,0BAA0B,IAAI,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAI3G,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAA;IACf,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,MAAM,gCAAgC,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAKD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,MAAM,GAAG,gCAAgC,CAkBnG;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,YAAY,EAAE,mBAAmB,GAChC,aAAa,CA0Hf;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,eAAe,GAAE,MAAwB,GAAG,IAAI,CAYzH;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CA0BnI"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ServiceDefinition } from "../../ServiceDefinition.ts";
|
|
2
|
+
import type { EndpointDefinition } from "../ExpressEndpointSetup.ts";
|
|
3
|
+
import { type ErrorDetails } from "@facetlayer/streams";
|
|
4
|
+
export interface EndpointValidationResult {
|
|
5
|
+
error: ErrorDetails;
|
|
6
|
+
}
|
|
7
|
+
export interface FailedEndpoint {
|
|
8
|
+
serviceName: string;
|
|
9
|
+
path: string;
|
|
10
|
+
method: string;
|
|
11
|
+
error: ErrorDetails;
|
|
12
|
+
}
|
|
13
|
+
export interface ServicesValidationResult {
|
|
14
|
+
problemEndpoints: FailedEndpoint[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validates a single endpoint for OpenAPI schema generation compatibility.
|
|
18
|
+
*
|
|
19
|
+
* @param serviceName - Name of the service containing the endpoint
|
|
20
|
+
* @param endpoint - The endpoint definition to validate
|
|
21
|
+
* @returns ProblematicEndpoint if validation fails, null if endpoint is valid
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateEndpointForOpenapi(endpoint: EndpointDefinition): EndpointValidationResult;
|
|
24
|
+
/**
|
|
25
|
+
* Finds endpoints that will fail OpenAPI schema generation.
|
|
26
|
+
* Tests each endpoint individually to identify which ones have unsupported Zod types.
|
|
27
|
+
*
|
|
28
|
+
* @param services - Array of service definitions to check
|
|
29
|
+
* @returns ValidationResult containing any problematic endpoints
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateServicesForOpenapi(services: ServiceDefinition[]): ServicesValidationResult;
|
|
32
|
+
//# sourceMappingURL=validateServicesForOpenapi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateServicesForOpenapi.d.ts","sourceRoot":"","sources":["../../../src/web/openapi/validateServicesForOpenapi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,WAAW,wBAAwB;IACrC,KAAK,EAAE,YAAY,CAAA;CACtB;AAED,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACrC,gBAAgB,EAAE,cAAc,EAAE,CAAC;CACtC;AAGD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,kBAAkB,GAC3B,wBAAwB,CAgB1B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,wBAAwB,CAoBhG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestContextMiddleware.d.ts","sourceRoot":"","sources":["../../src/web/requestContextMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAM/D,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAiB9F"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: authorization
|
|
3
|
+
description: Authorization system with resources, auth sources, and permissions
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authorization System
|
|
7
|
+
|
|
8
|
+
Prism Framework includes a flexible authorization system based on resources, auth sources, and permissions.
|
|
9
|
+
|
|
10
|
+
## Core Concepts
|
|
11
|
+
|
|
12
|
+
### Resources
|
|
13
|
+
|
|
14
|
+
Resources represent entities that can be accessed:
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
interface Resource {
|
|
18
|
+
type: 'user' | 'project' | 'session' | 'custom';
|
|
19
|
+
id: string;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Auth Sources
|
|
24
|
+
|
|
25
|
+
Auth sources represent how a user authenticated:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
interface AuthSource {
|
|
29
|
+
type: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface CookieAuthSource extends AuthSource {
|
|
33
|
+
type: 'cookie';
|
|
34
|
+
sessionId: string;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Permissions
|
|
39
|
+
|
|
40
|
+
Permissions are string-based capabilities:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
type Permission = string;
|
|
44
|
+
|
|
45
|
+
interface UserPermissions {
|
|
46
|
+
userId: string;
|
|
47
|
+
permissions: Permission[];
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Authorization Class
|
|
52
|
+
|
|
53
|
+
The `Authorization` class manages resources, auth sources, and permissions:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { Authorization } from '@facetlayer/prism-framework';
|
|
57
|
+
|
|
58
|
+
const auth = new Authorization();
|
|
59
|
+
|
|
60
|
+
// Add resources
|
|
61
|
+
auth.addResource({ type: 'user', id: '123' });
|
|
62
|
+
auth.addResource({ type: 'project', id: 'abc' });
|
|
63
|
+
|
|
64
|
+
// Check resources
|
|
65
|
+
if (auth.hasResource('user')) {
|
|
66
|
+
const user = auth.getResource('user');
|
|
67
|
+
console.log(user.id); // '123'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Add auth sources
|
|
71
|
+
auth.addAuthSource({
|
|
72
|
+
type: 'cookie',
|
|
73
|
+
sessionId: 'session-xyz',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Set permissions
|
|
77
|
+
auth.setUserPermissions({
|
|
78
|
+
userId: '123',
|
|
79
|
+
permissions: ['read:projects', 'write:projects'],
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Check permissions
|
|
83
|
+
if (auth.hasPermission('write:projects')) {
|
|
84
|
+
// User can write projects
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Request Context Integration
|
|
89
|
+
|
|
90
|
+
Every request has an `Authorization` instance in its context:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { getCurrentRequestContext } from '@facetlayer/prism-framework';
|
|
94
|
+
|
|
95
|
+
// In endpoint handler
|
|
96
|
+
const context = getCurrentRequestContext();
|
|
97
|
+
const auth = context.auth;
|
|
98
|
+
|
|
99
|
+
// Check if user is authenticated
|
|
100
|
+
const user = auth.getResource('user');
|
|
101
|
+
if (!user) {
|
|
102
|
+
throw new UnauthorizedError('Authentication required');
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Middleware Example
|
|
107
|
+
|
|
108
|
+
Create middleware to populate authorization:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { MiddlewareDefinition } from '@facetlayer/prism-framework';
|
|
112
|
+
|
|
113
|
+
export const authMiddleware: MiddlewareDefinition = {
|
|
114
|
+
path: '/api/*',
|
|
115
|
+
handler: async (req, res, next) => {
|
|
116
|
+
const context = getCurrentRequestContext();
|
|
117
|
+
|
|
118
|
+
// Check for session cookie
|
|
119
|
+
const sessionId = req.cookies.sessionId;
|
|
120
|
+
if (sessionId) {
|
|
121
|
+
// Look up session
|
|
122
|
+
const session = await getSession(sessionId);
|
|
123
|
+
if (session) {
|
|
124
|
+
// Add auth source
|
|
125
|
+
context.auth.addAuthSource({
|
|
126
|
+
type: 'cookie',
|
|
127
|
+
sessionId,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Add user resource
|
|
131
|
+
context.auth.addResource({
|
|
132
|
+
type: 'user',
|
|
133
|
+
id: session.userId,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Load and set permissions
|
|
137
|
+
const permissions = await getUserPermissions(session.userId);
|
|
138
|
+
context.auth.setUserPermissions({
|
|
139
|
+
userId: session.userId,
|
|
140
|
+
permissions,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
next();
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Endpoint Requirements
|
|
151
|
+
|
|
152
|
+
Use the `requires` array to enforce authentication:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
createEndpoint({
|
|
156
|
+
method: 'GET',
|
|
157
|
+
path: '/api/protected',
|
|
158
|
+
requires: ['authenticated-user'],
|
|
159
|
+
handler: async () => {
|
|
160
|
+
// This handler only runs if user is authenticated
|
|
161
|
+
const context = getCurrentRequestContext();
|
|
162
|
+
const user = context.auth.getResource('user');
|
|
163
|
+
return { userId: user.id };
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The `'authenticated-user'` requirement checks that a user resource exists in the authorization context.
|
|
169
|
+
|
|
170
|
+
## Custom Authorization Checks
|
|
171
|
+
|
|
172
|
+
Implement custom authorization logic in your handlers:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { ForbiddenError } from '@facetlayer/prism-framework';
|
|
176
|
+
|
|
177
|
+
createEndpoint({
|
|
178
|
+
method: 'DELETE',
|
|
179
|
+
path: '/api/projects/:projectId',
|
|
180
|
+
requires: ['authenticated-user'],
|
|
181
|
+
handler: async (input) => {
|
|
182
|
+
const context = getCurrentRequestContext();
|
|
183
|
+
|
|
184
|
+
// Check permission
|
|
185
|
+
if (!context.auth.hasPermission('delete:projects')) {
|
|
186
|
+
throw new ForbiddenError('Insufficient permissions');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Check project ownership
|
|
190
|
+
const project = await getProject(input.projectId);
|
|
191
|
+
const user = context.auth.getResource('user');
|
|
192
|
+
|
|
193
|
+
if (project.ownerId !== user.id) {
|
|
194
|
+
throw new ForbiddenError('Not the project owner');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
await deleteProject(input.projectId);
|
|
198
|
+
return { success: true };
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Permission Patterns
|
|
204
|
+
|
|
205
|
+
Common permission naming patterns:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Resource:Action format
|
|
209
|
+
'read:projects'
|
|
210
|
+
'write:projects'
|
|
211
|
+
'delete:projects'
|
|
212
|
+
'admin:users'
|
|
213
|
+
|
|
214
|
+
// Hierarchical permissions
|
|
215
|
+
'projects:read'
|
|
216
|
+
'projects:write'
|
|
217
|
+
'projects:delete'
|
|
218
|
+
|
|
219
|
+
// Role-based
|
|
220
|
+
'role:admin'
|
|
221
|
+
'role:editor'
|
|
222
|
+
'role:viewer'
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Helper Functions
|
|
226
|
+
|
|
227
|
+
Create helper functions for common authorization checks:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { getCurrentRequestContext, ForbiddenError } from '@facetlayer/prism-framework';
|
|
231
|
+
|
|
232
|
+
export function requirePermission(permission: string) {
|
|
233
|
+
const context = getCurrentRequestContext();
|
|
234
|
+
if (!context?.auth.hasPermission(permission)) {
|
|
235
|
+
throw new ForbiddenError(`Missing permission: ${permission}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function requireProjectAccess(projectId: string) {
|
|
240
|
+
const context = getCurrentRequestContext();
|
|
241
|
+
const user = context?.auth.getResource('user');
|
|
242
|
+
if (!user) {
|
|
243
|
+
throw new UnauthorizedError('Authentication required');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const project = getProject(projectId);
|
|
247
|
+
if (project.ownerId !== user.id) {
|
|
248
|
+
throw new ForbiddenError('Project access denied');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Usage in handlers
|
|
253
|
+
handler: async (input) => {
|
|
254
|
+
requirePermission('write:projects');
|
|
255
|
+
requireProjectAccess(input.projectId);
|
|
256
|
+
|
|
257
|
+
// Proceed with the operation
|
|
258
|
+
await updateProject(input.projectId, input.updates);
|
|
259
|
+
return { success: true };
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Multi-Tenant Support
|
|
264
|
+
|
|
265
|
+
Use resources to track tenant/organization context:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
// Add organization resource
|
|
269
|
+
context.auth.addResource({
|
|
270
|
+
type: 'custom',
|
|
271
|
+
id: 'org:acme-corp',
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Check organization access
|
|
275
|
+
const org = context.auth.getAllResources()
|
|
276
|
+
.find(r => r.id.startsWith('org:'));
|
|
277
|
+
|
|
278
|
+
if (!org) {
|
|
279
|
+
throw new ForbiddenError('No organization context');
|
|
280
|
+
}
|
|
281
|
+
```
|