@constructive-io/express-context 0.27.0 → 0.29.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/esm/index.js CHANGED
@@ -45,6 +45,6 @@ export { BoundedCounter, CounterFlusher } from './usage-counter';
45
45
  // Request ID middleware
46
46
  export { requestIdMiddleware } from './request-id';
47
47
  export { buildContext, createContextMiddleware } from './context';
48
- export { agentChatLoader, authSettingsLoader, authSurfaceLoader, billingLoader, computeLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, identityProvidersLoader, inferenceLogLoader, llmLoader, pubkeyLoader, requestProtectionLoader, requireDatabaseId, requireIdentityProvider, rlsLoader, webauthnLoader, } from './loaders';
48
+ export { agentChatLoader, authSettingsLoader, authSurfaceLoader, billingLoader, computeLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, eventsLoader, identityProvidersLoader, inferenceLogLoader, llmLoader, pubkeyLoader, requestProtectionLoader, requireDatabaseId, requireIdentityProvider, rlsLoader, webauthnLoader, } from './loaders';
49
49
  // Side-effect: Express type augmentation
50
50
  import './types';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Events Module Loader
3
+ *
4
+ * Resolves the tenant's app-scoped events module from
5
+ * metaschema_modules_public.events_module: the private schema and the name of
6
+ * its `record_event` function, so the server can record events without
7
+ * hard-coding the generated schema or function names.
8
+ */
9
+ import { createModuleLoader } from './create-loader';
10
+ // ─── SQL ────────────────────────────────────────────────────────────────────
11
+ const EVENTS_MODULE_SQL = `
12
+ SELECT
13
+ s.schema_name AS private_schema_name,
14
+ em.record_event
15
+ FROM metaschema_modules_public.events_module em
16
+ JOIN metaschema_public.schema s ON s.id = em.private_schema_id
17
+ WHERE em.database_id = $1
18
+ AND em.scope = 'app'
19
+ LIMIT 1
20
+ `;
21
+ // ─── Loader ─────────────────────────────────────────────────────────────────
22
+ export const eventsLoader = createModuleLoader({
23
+ name: 'events',
24
+ ttlMs: 60_000,
25
+ async resolve(ctx) {
26
+ const { tenantPool, databaseId } = ctx;
27
+ const result = await tenantPool.query(EVENTS_MODULE_SQL, [databaseId]);
28
+ const row = result.rows[0];
29
+ if (!row?.private_schema_name || !row.record_event)
30
+ return undefined;
31
+ return {
32
+ privateSchemaName: row.private_schema_name,
33
+ recordEvent: row.record_event
34
+ };
35
+ }
36
+ });
@@ -14,6 +14,7 @@
14
14
  * - webauthnSettings(routing-plane webauthn_settings)
15
15
  * - authSettings (metaschema_modules_public.sessions_module → tenant DB)
16
16
  * - authSurface (identity/connected-accounts/emails modules → tenant DB)
17
+ * - events (metaschema_modules_public.events_module, app scope → tenant DB)
17
18
  *
18
19
  * Opt-in (not in the default registry, register it explicitly):
19
20
  * - identityProviders (three round trips, decrypts client secrets)
@@ -41,6 +42,7 @@ export { billingLoader } from './billing';
41
42
  export { computeLoader } from './compute';
42
43
  export { corsLoader } from './cors';
43
44
  export { databaseSettingsLoader } from './database-settings';
45
+ export { eventsLoader } from './events';
44
46
  export { identityProvidersLoader, requireIdentityProvider } from './identity-providers';
45
47
  export { inferenceLogLoader } from './inference-log';
46
48
  export { llmLoader } from './llm';
@@ -58,6 +60,7 @@ import { billingLoader } from './billing';
58
60
  import { computeLoader } from './compute';
59
61
  import { corsLoader } from './cors';
60
62
  import { databaseSettingsLoader } from './database-settings';
63
+ import { eventsLoader } from './events';
61
64
  import { inferenceLogLoader } from './inference-log';
62
65
  import { llmLoader } from './llm';
63
66
  import { pubkeyLoader } from './pubkey';
@@ -79,6 +82,7 @@ export function createDefaultRegistry() {
79
82
  registry.register(agentChatLoader);
80
83
  registry.register(llmLoader);
81
84
  registry.register(computeLoader);
85
+ registry.register(eventsLoader);
82
86
  registry.register(requestProtectionLoader);
83
87
  return registry;
84
88
  }
@@ -30,6 +30,17 @@ export function buildPgSettings(input) {
30
30
  if (token?.session_id) {
31
31
  settings['jwt.claims.session_id'] = token.session_id;
32
32
  }
33
+ // Session lineage (token exchange chains)
34
+ if (token?.root_session_id) {
35
+ settings['jwt.claims.root_session_id'] = token.root_session_id;
36
+ }
37
+ if (token?.parent_session_id) {
38
+ settings['jwt.claims.parent_session_id'] = token.parent_session_id;
39
+ }
40
+ // Declared purpose of the credential
41
+ if (token?.intent) {
42
+ settings['jwt.claims.intent'] = token.intent;
43
+ }
33
44
  // Principal identity (service accounts / bots)
34
45
  if (token?.principal_id) {
35
46
  settings['jwt.claims.principal_id'] = token.principal_id;
package/index.d.ts CHANGED
@@ -34,7 +34,7 @@
34
34
  * });
35
35
  * ```
36
36
  */
37
- export type { AgentChatConfig, ApiConfigResult, ApiError, ApiStructure, AuthSettings, AuthSurface, BillingConfig, BuiltinModuleMap, ComputeConfig, ComputeModuleConfig, ConstructiveAPIToken, ConstructiveContext, DatabaseSettings, IdentityProviderConfig, IdentityProvidersModule, InferenceLogConfig, LlmConfig, PubkeyChallengeSettings, RlsModule, WebauthnSettings, WithPgClient, } from './types';
37
+ export type { AgentChatConfig, ApiConfigResult, ApiError, ApiStructure, AuthSettings, AuthSurface, BillingConfig, BuiltinModuleMap, ComputeConfig, ComputeModuleConfig, ConstructiveAPIToken, ConstructiveContext, DatabaseSettings, EventsConfig, IdentityProviderConfig, IdentityProvidersModule, InferenceLogConfig, LlmConfig, PubkeyChallengeSettings, RlsModule, WebauthnSettings, WithPgClient, } from './types';
38
38
  export type { BillingClient, InferenceLogEntry } from './billing-client';
39
39
  export { createBillingClient } from './billing-client';
40
40
  export type { PgSettingsInput } from './pg-settings';
@@ -52,5 +52,5 @@ export { requestIdMiddleware } from './request-id';
52
52
  export type { ContextMiddlewareOptions } from './context';
53
53
  export { buildContext, createContextMiddleware } from './context';
54
54
  export type { CreateLoaderOptions, LoaderContext, LoaderRegistry, ModuleLoader, } from './loaders';
55
- export { agentChatLoader, authSettingsLoader, authSurfaceLoader, billingLoader, computeLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, identityProvidersLoader, inferenceLogLoader, llmLoader, pubkeyLoader, requestProtectionLoader, requireDatabaseId, requireIdentityProvider, rlsLoader, webauthnLoader, } from './loaders';
55
+ export { agentChatLoader, authSettingsLoader, authSurfaceLoader, billingLoader, computeLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, eventsLoader, identityProvidersLoader, inferenceLogLoader, llmLoader, pubkeyLoader, requestProtectionLoader, requireDatabaseId, requireIdentityProvider, rlsLoader, webauthnLoader, } from './loaders';
56
56
  import './types';
package/index.js CHANGED
@@ -36,7 +36,8 @@
36
36
  * ```
37
37
  */
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.webauthnLoader = exports.rlsLoader = exports.requireIdentityProvider = exports.requireDatabaseId = exports.requestProtectionLoader = exports.pubkeyLoader = exports.llmLoader = exports.inferenceLogLoader = exports.identityProvidersLoader = exports.databaseSettingsLoader = exports.createModuleLoader = exports.createLoaderRegistry = exports.createDefaultRegistry = exports.corsLoader = exports.computeLoader = exports.billingLoader = exports.authSurfaceLoader = exports.authSettingsLoader = exports.agentChatLoader = exports.createContextMiddleware = exports.buildContext = exports.requestIdMiddleware = exports.CounterFlusher = exports.BoundedCounter = exports.UNKNOWN_SOURCE = exports.sourceBucket = exports.refusalRows = exports.RefusalRecorder = exports.refusalOverflowKey = exports.refusalKeyOf = exports.REFUSAL_REASONS = exports.OVERFLOW_SOURCE = exports.OVERFLOW_ROUTE = exports.DEFAULT_REFUSAL_MAX_KEYS = exports.DEFAULT_REFUSAL_FLUSH_JITTER_MS = exports.DEFAULT_REFUSAL_FLUSH_INTERVAL_MS = exports.createRecordRefusalsSink = exports.trustedProxyHops = exports.RateWindow = exports.ConcurrencyLimiter = exports.clientIpFrom = exports.resolveRequestProtection = exports.protectionPgSettings = exports.PROTECTION_BOUNDS = exports.INTROSPECTION_BOUND = exports.DEFAULT_REQUEST_PROTECTION = exports.ASSUMED_PAGE_SIZE = exports.withPgClient = exports.buildPgSettings = exports.createBillingClient = void 0;
39
+ exports.rlsLoader = exports.requireIdentityProvider = exports.requireDatabaseId = exports.requestProtectionLoader = exports.pubkeyLoader = exports.llmLoader = exports.inferenceLogLoader = exports.identityProvidersLoader = exports.eventsLoader = exports.databaseSettingsLoader = exports.createModuleLoader = exports.createLoaderRegistry = exports.createDefaultRegistry = exports.corsLoader = exports.computeLoader = exports.billingLoader = exports.authSurfaceLoader = exports.authSettingsLoader = exports.agentChatLoader = exports.createContextMiddleware = exports.buildContext = exports.requestIdMiddleware = exports.CounterFlusher = exports.BoundedCounter = exports.UNKNOWN_SOURCE = exports.sourceBucket = exports.refusalRows = exports.RefusalRecorder = exports.refusalOverflowKey = exports.refusalKeyOf = exports.REFUSAL_REASONS = exports.OVERFLOW_SOURCE = exports.OVERFLOW_ROUTE = exports.DEFAULT_REFUSAL_MAX_KEYS = exports.DEFAULT_REFUSAL_FLUSH_JITTER_MS = exports.DEFAULT_REFUSAL_FLUSH_INTERVAL_MS = exports.createRecordRefusalsSink = exports.trustedProxyHops = exports.RateWindow = exports.ConcurrencyLimiter = exports.clientIpFrom = exports.resolveRequestProtection = exports.protectionPgSettings = exports.PROTECTION_BOUNDS = exports.INTROSPECTION_BOUND = exports.DEFAULT_REQUEST_PROTECTION = exports.ASSUMED_PAGE_SIZE = exports.withPgClient = exports.buildPgSettings = exports.createBillingClient = void 0;
40
+ exports.webauthnLoader = void 0;
40
41
  var billing_client_1 = require("./billing-client");
41
42
  Object.defineProperty(exports, "createBillingClient", { enumerable: true, get: function () { return billing_client_1.createBillingClient; } });
42
43
  var pg_settings_1 = require("./pg-settings");
@@ -90,6 +91,7 @@ Object.defineProperty(exports, "createDefaultRegistry", { enumerable: true, get:
90
91
  Object.defineProperty(exports, "createLoaderRegistry", { enumerable: true, get: function () { return loaders_1.createLoaderRegistry; } });
91
92
  Object.defineProperty(exports, "createModuleLoader", { enumerable: true, get: function () { return loaders_1.createModuleLoader; } });
92
93
  Object.defineProperty(exports, "databaseSettingsLoader", { enumerable: true, get: function () { return loaders_1.databaseSettingsLoader; } });
94
+ Object.defineProperty(exports, "eventsLoader", { enumerable: true, get: function () { return loaders_1.eventsLoader; } });
93
95
  Object.defineProperty(exports, "identityProvidersLoader", { enumerable: true, get: function () { return loaders_1.identityProvidersLoader; } });
94
96
  Object.defineProperty(exports, "inferenceLogLoader", { enumerable: true, get: function () { return loaders_1.inferenceLogLoader; } });
95
97
  Object.defineProperty(exports, "llmLoader", { enumerable: true, get: function () { return loaders_1.llmLoader; } });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Events Module Loader
3
+ *
4
+ * Resolves the tenant's app-scoped events module from
5
+ * metaschema_modules_public.events_module: the private schema and the name of
6
+ * its `record_event` function, so the server can record events without
7
+ * hard-coding the generated schema or function names.
8
+ */
9
+ import type { EventsConfig } from '../types';
10
+ import type { ModuleLoader } from './types';
11
+ export declare const eventsLoader: ModuleLoader<EventsConfig>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Events Module Loader
4
+ *
5
+ * Resolves the tenant's app-scoped events module from
6
+ * metaschema_modules_public.events_module: the private schema and the name of
7
+ * its `record_event` function, so the server can record events without
8
+ * hard-coding the generated schema or function names.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.eventsLoader = void 0;
12
+ const create_loader_1 = require("./create-loader");
13
+ // ─── SQL ────────────────────────────────────────────────────────────────────
14
+ const EVENTS_MODULE_SQL = `
15
+ SELECT
16
+ s.schema_name AS private_schema_name,
17
+ em.record_event
18
+ FROM metaschema_modules_public.events_module em
19
+ JOIN metaschema_public.schema s ON s.id = em.private_schema_id
20
+ WHERE em.database_id = $1
21
+ AND em.scope = 'app'
22
+ LIMIT 1
23
+ `;
24
+ // ─── Loader ─────────────────────────────────────────────────────────────────
25
+ exports.eventsLoader = (0, create_loader_1.createModuleLoader)({
26
+ name: 'events',
27
+ ttlMs: 60_000,
28
+ async resolve(ctx) {
29
+ const { tenantPool, databaseId } = ctx;
30
+ const result = await tenantPool.query(EVENTS_MODULE_SQL, [databaseId]);
31
+ const row = result.rows[0];
32
+ if (!row?.private_schema_name || !row.record_event)
33
+ return undefined;
34
+ return {
35
+ privateSchemaName: row.private_schema_name,
36
+ recordEvent: row.record_event
37
+ };
38
+ }
39
+ });
@@ -14,6 +14,7 @@
14
14
  * - webauthnSettings(routing-plane webauthn_settings)
15
15
  * - authSettings (metaschema_modules_public.sessions_module → tenant DB)
16
16
  * - authSurface (identity/connected-accounts/emails modules → tenant DB)
17
+ * - events (metaschema_modules_public.events_module, app scope → tenant DB)
17
18
  *
18
19
  * Opt-in (not in the default registry, register it explicitly):
19
20
  * - identityProviders (three round trips, decrypts client secrets)
@@ -43,6 +44,7 @@ export { billingLoader } from './billing';
43
44
  export { computeLoader } from './compute';
44
45
  export { corsLoader } from './cors';
45
46
  export { databaseSettingsLoader } from './database-settings';
47
+ export { eventsLoader } from './events';
46
48
  export { identityProvidersLoader, requireIdentityProvider } from './identity-providers';
47
49
  export { inferenceLogLoader } from './inference-log';
48
50
  export { llmLoader } from './llm';
package/loaders/index.js CHANGED
@@ -15,6 +15,7 @@
15
15
  * - webauthnSettings(routing-plane webauthn_settings)
16
16
  * - authSettings (metaschema_modules_public.sessions_module → tenant DB)
17
17
  * - authSurface (identity/connected-accounts/emails modules → tenant DB)
18
+ * - events (metaschema_modules_public.events_module, app scope → tenant DB)
18
19
  *
19
20
  * Opt-in (not in the default registry, register it explicitly):
20
21
  * - identityProviders (three round trips, decrypts client secrets)
@@ -32,7 +33,7 @@
32
33
  * registry.register(myLoader);
33
34
  */
34
35
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.webauthnLoader = exports.rlsLoader = exports.requestProtectionLoader = exports.pubkeyLoader = exports.llmLoader = exports.inferenceLogLoader = exports.requireIdentityProvider = exports.identityProvidersLoader = exports.databaseSettingsLoader = exports.corsLoader = exports.computeLoader = exports.billingLoader = exports.authSurfaceLoader = exports.authSettingsLoader = exports.agentChatLoader = exports.createLoaderRegistry = exports.createModuleLoader = exports.requireDatabaseId = void 0;
36
+ exports.webauthnLoader = exports.rlsLoader = exports.requestProtectionLoader = exports.pubkeyLoader = exports.llmLoader = exports.inferenceLogLoader = exports.requireIdentityProvider = exports.identityProvidersLoader = exports.eventsLoader = exports.databaseSettingsLoader = exports.corsLoader = exports.computeLoader = exports.billingLoader = exports.authSurfaceLoader = exports.authSettingsLoader = exports.agentChatLoader = exports.createLoaderRegistry = exports.createModuleLoader = exports.requireDatabaseId = void 0;
36
37
  exports.createDefaultRegistry = createDefaultRegistry;
37
38
  var types_1 = require("./types");
38
39
  Object.defineProperty(exports, "requireDatabaseId", { enumerable: true, get: function () { return types_1.requireDatabaseId; } });
@@ -55,6 +56,8 @@ var cors_1 = require("./cors");
55
56
  Object.defineProperty(exports, "corsLoader", { enumerable: true, get: function () { return cors_1.corsLoader; } });
56
57
  var database_settings_1 = require("./database-settings");
57
58
  Object.defineProperty(exports, "databaseSettingsLoader", { enumerable: true, get: function () { return database_settings_1.databaseSettingsLoader; } });
59
+ var events_1 = require("./events");
60
+ Object.defineProperty(exports, "eventsLoader", { enumerable: true, get: function () { return events_1.eventsLoader; } });
58
61
  var identity_providers_1 = require("./identity-providers");
59
62
  Object.defineProperty(exports, "identityProvidersLoader", { enumerable: true, get: function () { return identity_providers_1.identityProvidersLoader; } });
60
63
  Object.defineProperty(exports, "requireIdentityProvider", { enumerable: true, get: function () { return identity_providers_1.requireIdentityProvider; } });
@@ -80,6 +83,7 @@ const billing_2 = require("./billing");
80
83
  const compute_2 = require("./compute");
81
84
  const cors_2 = require("./cors");
82
85
  const database_settings_2 = require("./database-settings");
86
+ const events_2 = require("./events");
83
87
  const inference_log_2 = require("./inference-log");
84
88
  const llm_2 = require("./llm");
85
89
  const pubkey_2 = require("./pubkey");
@@ -101,6 +105,7 @@ function createDefaultRegistry() {
101
105
  registry.register(agent_chat_2.agentChatLoader);
102
106
  registry.register(llm_2.llmLoader);
103
107
  registry.register(compute_2.computeLoader);
108
+ registry.register(events_2.eventsLoader);
104
109
  registry.register(request_protection_2.requestProtectionLoader);
105
110
  return registry;
106
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/express-context",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Extractable Express middleware for Constructive tenant context — domain resolution, JWT auth, pgSettings, and withPgClient",
5
5
  "author": "Constructive <developers@constructive.io>",
6
6
  "homepage": "https://github.com/constructive-io/constructive",
@@ -30,13 +30,13 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@constructive-io/url-domains": "^2.30.1",
33
- "@pgpmjs/env": "^2.43.3",
33
+ "@pgpmjs/env": "^2.43.4",
34
34
  "@pgpmjs/logger": "^2.25.1",
35
- "@pgpmjs/server-utils": "^3.27.3",
36
- "@pgpmjs/types": "^2.53.1",
35
+ "@pgpmjs/server-utils": "^3.27.4",
36
+ "@pgpmjs/types": "^2.53.2",
37
37
  "lru-cache": "^11.2.7",
38
38
  "pg": "^8.21.0",
39
- "pg-cache": "^3.27.3",
39
+ "pg-cache": "^3.27.4",
40
40
  "pg-query-context": "^2.30.1"
41
41
  },
42
42
  "devDependencies": {
@@ -59,5 +59,5 @@
59
59
  "jwt",
60
60
  "pgSettings"
61
61
  ],
62
- "gitHead": "6e2d71a9eb4e1f49f0de0b2b594b2f811de97442"
62
+ "gitHead": "86896399e80595be179738d459106cfbfbada659"
63
63
  }
package/pg-settings.js CHANGED
@@ -33,6 +33,17 @@ function buildPgSettings(input) {
33
33
  if (token?.session_id) {
34
34
  settings['jwt.claims.session_id'] = token.session_id;
35
35
  }
36
+ // Session lineage (token exchange chains)
37
+ if (token?.root_session_id) {
38
+ settings['jwt.claims.root_session_id'] = token.root_session_id;
39
+ }
40
+ if (token?.parent_session_id) {
41
+ settings['jwt.claims.parent_session_id'] = token.parent_session_id;
42
+ }
43
+ // Declared purpose of the credential
44
+ if (token?.intent) {
45
+ settings['jwt.claims.intent'] = token.intent;
46
+ }
36
47
  // Principal identity (service accounts / bots)
37
48
  if (token?.principal_id) {
38
49
  settings['jwt.claims.principal_id'] = token.principal_id;
package/types.d.ts CHANGED
@@ -152,6 +152,12 @@ export type ConstructiveAPIToken = {
152
152
  session_id?: string;
153
153
  access_level?: string;
154
154
  kind?: string;
155
+ /** Session lineage: the top of this session's exchange chain. */
156
+ root_session_id?: string;
157
+ /** Session lineage: the session this one was exchanged from, if any. */
158
+ parent_session_id?: string;
159
+ /** Caller-declared purpose recorded on the credential at exchange time. */
160
+ intent?: string;
155
161
  [key: string]: unknown;
156
162
  };
157
163
  export interface BillingConfig {
@@ -193,6 +199,11 @@ export interface ComputeModuleConfig {
193
199
  export interface ComputeConfig {
194
200
  modules: ComputeModuleConfig[];
195
201
  }
202
+ /** The tenant's app-scoped events module: where `record_event` lives. */
203
+ export interface EventsConfig {
204
+ privateSchemaName: string;
205
+ recordEvent: string;
206
+ }
196
207
  export interface LlmConfig {
197
208
  embeddingProvider: string;
198
209
  embeddingModel: string;
@@ -227,6 +238,7 @@ export interface BuiltinModuleMap {
227
238
  agentChat: AgentChatConfig;
228
239
  llm: LlmConfig;
229
240
  compute: ComputeConfig;
241
+ events: EventsConfig;
230
242
  requestProtection: RequestProtection;
231
243
  }
232
244
  /**