@lssm/lib.contracts 0.0.0-canary-20251217062943 → 0.0.0-canary-20251217072406
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/dist/app-config/app-config.feature.js +53 -1
- package/dist/app-config/contracts.d.ts +50 -50
- package/dist/app-config/contracts.js +396 -1
- package/dist/app-config/docs/app-config.docblock.js +22 -220
- package/dist/app-config/events.js +168 -1
- package/dist/app-config/index.js +8 -1
- package/dist/app-config/lifecycle-contracts.js +441 -1
- package/dist/app-config/runtime.js +617 -1
- package/dist/app-config/spec.js +36 -1
- package/dist/app-config/validation.js +538 -1
- package/dist/capabilities/docs/capabilities.docblock.js +22 -1
- package/dist/capabilities/openbanking.js +92 -1
- package/dist/capabilities.js +50 -1
- package/dist/client/index.js +9 -1
- package/dist/client/react/drivers/rn-reusables.js +21 -1
- package/dist/client/react/drivers/shadcn.js +11 -1
- package/dist/client/react/feature-render.js +43 -1
- package/dist/client/react/form-render.js +298 -1
- package/dist/client/react/index.js +8 -1
- package/dist/contract-registry/index.js +3 -1
- package/dist/contract-registry/schemas.js +61 -1
- package/dist/contracts-adapter-hydration.js +41 -1
- package/dist/contracts-adapter-input.js +77 -1
- package/dist/data-views/docs/data-views.docblock.js +22 -1
- package/dist/data-views/query-generator.js +48 -1
- package/dist/data-views/runtime.js +39 -1
- package/dist/data-views.js +35 -1
- package/dist/docs/PUBLISHING.docblock.js +17 -76
- package/dist/docs/accessibility_wcag_compliance_specs.docblock.js +17 -350
- package/dist/docs/index.js +33 -1
- package/dist/docs/meta.docs.js +15 -2
- package/dist/docs/presentations.js +77 -1
- package/dist/docs/registry.js +51 -1
- package/dist/docs/tech/PHASE_1_QUICKSTART.docblock.js +17 -383
- package/dist/docs/tech/PHASE_2_AI_NATIVE_OPERATIONS.docblock.js +17 -68
- package/dist/docs/tech/PHASE_3_AUTO_EVOLUTION.docblock.js +17 -140
- package/dist/docs/tech/PHASE_4_PERSONALIZATION_ENGINE.docblock.js +17 -86
- package/dist/docs/tech/PHASE_5_ZERO_TOUCH_OPERATIONS.docblock.js +17 -1
- package/dist/docs/tech/auth/better-auth-nextjs.docblock.js +25 -2
- package/dist/docs/tech/contracts/README.docblock.js +21 -1
- package/dist/docs/tech/contracts/create-subscription.docblock.js +21 -1
- package/dist/docs/tech/contracts/graphql-typed-outputs.docblock.js +21 -180
- package/dist/docs/tech/contracts/migrations.docblock.js +21 -1
- package/dist/docs/tech/contracts/openapi-export.docblock.js +22 -2
- package/dist/docs/tech/contracts/ops-to-presentation-linking.docblock.js +19 -60
- package/dist/docs/tech/contracts/overlays.docblock.js +21 -68
- package/dist/docs/tech/contracts/tests.docblock.js +21 -132
- package/dist/docs/tech/contracts/themes.docblock.js +21 -1
- package/dist/docs/tech/contracts/vertical-pocket-family-office.docblock.js +21 -106
- package/dist/docs/tech/lifecycle-stage-system.docblock.js +17 -213
- package/dist/docs/tech/llm/llm-integration.docblock.js +74 -5
- package/dist/docs/tech/mcp-endpoints.docblock.js +38 -1
- package/dist/docs/tech/presentation-runtime.docblock.js +17 -1
- package/dist/docs/tech/schema/README.docblock.js +21 -262
- package/dist/docs/tech/studio/learning-events.docblock.js +49 -1
- package/dist/docs/tech/studio/learning-journeys.docblock.js +25 -2
- package/dist/docs/tech/studio/platform-admin-panel.docblock.js +24 -2
- package/dist/docs/tech/studio/project-access-teams.docblock.js +26 -16
- package/dist/docs/tech/studio/project-routing.docblock.js +68 -1
- package/dist/docs/tech/studio/sandbox-unlogged.docblock.js +23 -2
- package/dist/docs/tech/studio/team-invitations.docblock.js +41 -36
- package/dist/docs/tech/studio/workspace-ops.docblock.js +48 -1
- package/dist/docs/tech/studio/workspaces.docblock.js +24 -2
- package/dist/docs/tech/telemetry-ingest.docblock.js +37 -3
- package/dist/docs/tech/templates/runtime.docblock.js +21 -1
- package/dist/docs/tech/vscode-extension.docblock.js +37 -3
- package/dist/docs/tech/workflows/overview.docblock.js +21 -1
- package/dist/docs/tech-contracts.docs.js +19 -2
- package/dist/events.js +12 -1
- package/dist/experiments/docs/experiments.docblock.js +22 -128
- package/dist/experiments/evaluator.js +101 -1
- package/dist/experiments/spec.js +33 -1
- package/dist/features.js +68 -1
- package/dist/forms/docs/forms.docblock.js +22 -1
- package/dist/forms.js +119 -1
- package/dist/index.js +107 -1
- package/dist/install.js +40 -1
- package/dist/integrations/contracts.d.ts +102 -102
- package/dist/integrations/contracts.js +388 -1
- package/dist/integrations/docs/integrations.docblock.js +95 -1
- package/dist/integrations/health.js +69 -1
- package/dist/integrations/index.js +23 -1
- package/dist/integrations/openbanking/contracts/accounts.d.ts +66 -66
- package/dist/integrations/openbanking/contracts/accounts.js +237 -1
- package/dist/integrations/openbanking/contracts/balances.d.ts +34 -34
- package/dist/integrations/openbanking/contracts/balances.js +167 -1
- package/dist/integrations/openbanking/contracts/index.js +12 -1
- package/dist/integrations/openbanking/contracts/transactions.d.ts +48 -48
- package/dist/integrations/openbanking/contracts/transactions.js +218 -1
- package/dist/integrations/openbanking/guards.js +32 -1
- package/dist/integrations/openbanking/models.d.ts +55 -55
- package/dist/integrations/openbanking/models.js +242 -1
- package/dist/integrations/openbanking/openbanking.feature.js +68 -1
- package/dist/integrations/openbanking/telemetry.js +39 -1
- package/dist/integrations/providers/elevenlabs.js +56 -1
- package/dist/integrations/providers/gcs-storage.js +79 -1
- package/dist/integrations/providers/gmail.js +91 -1
- package/dist/integrations/providers/google-calendar.js +70 -1
- package/dist/integrations/providers/impls/elevenlabs-voice.js +95 -1
- package/dist/integrations/providers/impls/gcs-storage.js +88 -1
- package/dist/integrations/providers/impls/gmail-inbound.js +200 -1
- package/dist/integrations/providers/impls/gmail-outbound.js +104 -5
- package/dist/integrations/providers/impls/google-calendar.js +154 -1
- package/dist/integrations/providers/impls/index.js +16 -1
- package/dist/integrations/providers/impls/mistral-embedding.js +41 -1
- package/dist/integrations/providers/impls/mistral-llm.js +247 -1
- package/dist/integrations/providers/impls/postmark-email.js +55 -1
- package/dist/integrations/providers/impls/powens-client.js +171 -1
- package/dist/integrations/providers/impls/powens-openbanking.js +218 -1
- package/dist/integrations/providers/impls/provider-factory.js +142 -1
- package/dist/integrations/providers/impls/qdrant-vector.js +69 -1
- package/dist/integrations/providers/impls/stripe-payments.js +202 -1
- package/dist/integrations/providers/impls/twilio-sms.js +58 -1
- package/dist/integrations/providers/index.js +13 -1
- package/dist/integrations/providers/mistral.js +72 -1
- package/dist/integrations/providers/postmark.js +72 -1
- package/dist/integrations/providers/powens.js +120 -1
- package/dist/integrations/providers/qdrant.js +77 -1
- package/dist/integrations/providers/registry.js +34 -1
- package/dist/integrations/providers/stripe.js +87 -1
- package/dist/integrations/providers/twilio-sms.js +65 -1
- package/dist/integrations/runtime.js +186 -1
- package/dist/integrations/secrets/aws-secret-manager.js +231 -1
- package/dist/integrations/secrets/env-secret-provider.js +81 -1
- package/dist/integrations/secrets/gcp-secret-manager.js +229 -1
- package/dist/integrations/secrets/index.js +8 -1
- package/dist/integrations/secrets/manager.js +103 -1
- package/dist/integrations/secrets/provider.js +58 -1
- package/dist/integrations/secrets/scaleway-secret-manager.js +247 -1
- package/dist/integrations/spec.js +39 -1
- package/dist/jobs/define-job.js +16 -1
- package/dist/jobs/gcp-cloud-tasks.js +53 -1
- package/dist/jobs/gcp-pubsub.js +39 -1
- package/dist/jobs/handlers/gmail-sync-handler.js +9 -1
- package/dist/jobs/handlers/index.js +12 -1
- package/dist/jobs/handlers/ping-handler.js +15 -1
- package/dist/jobs/handlers/storage-document-handler.js +14 -1
- package/dist/jobs/index.js +4 -1
- package/dist/jobs/memory-queue.js +71 -1
- package/dist/jobs/queue.js +33 -1
- package/dist/jobs/scaleway-sqs-queue.js +153 -1
- package/dist/jsonschema.d.ts +3 -3
- package/dist/jsonschema.js +32 -1
- package/dist/knowledge/contracts.d.ts +66 -66
- package/dist/knowledge/contracts.js +317 -1
- package/dist/knowledge/docs/knowledge.docblock.js +22 -138
- package/dist/knowledge/index.js +10 -1
- package/dist/knowledge/ingestion/document-processor.js +54 -1
- package/dist/knowledge/ingestion/embedding-service.js +25 -1
- package/dist/knowledge/ingestion/gmail-adapter.js +50 -5
- package/dist/knowledge/ingestion/index.js +7 -1
- package/dist/knowledge/ingestion/storage-adapter.js +26 -1
- package/dist/knowledge/ingestion/vector-indexer.js +32 -1
- package/dist/knowledge/query/index.js +3 -1
- package/dist/knowledge/query/service.js +64 -2
- package/dist/knowledge/runtime.js +49 -1
- package/dist/knowledge/spaces/email-threads.js +38 -1
- package/dist/knowledge/spaces/financial-docs.js +38 -1
- package/dist/knowledge/spaces/financial-overview.js +42 -1
- package/dist/knowledge/spaces/index.js +8 -1
- package/dist/knowledge/spaces/product-canon.js +38 -1
- package/dist/knowledge/spaces/support-faq.js +41 -1
- package/dist/knowledge/spaces/uploaded-docs.js +38 -1
- package/dist/knowledge/spec.js +39 -1
- package/dist/llm/exporters.js +541 -8
- package/dist/llm/index.js +4 -1
- package/dist/llm/prompts.js +246 -56
- package/dist/markdown.js +116 -3
- package/dist/migrations.js +33 -1
- package/dist/onboarding-base.d.ts +29 -29
- package/dist/onboarding-base.js +196 -1
- package/dist/openapi.js +75 -1
- package/dist/openbanking/docs/openbanking.docblock.js +22 -109
- package/dist/ownership.js +40 -1
- package/dist/policy/docs/policy.docblock.js +22 -1
- package/dist/policy/engine.js +223 -1
- package/dist/policy/opa-adapter.js +71 -1
- package/dist/policy/spec.js +33 -1
- package/dist/presentations/docs/presentations-conventions.docblock.js +21 -7
- package/dist/presentations.backcompat.js +47 -1
- package/dist/presentations.d.ts +3 -3
- package/dist/presentations.js +66 -1
- package/dist/presentations.v2.js +278 -6
- package/dist/prompt.js +10 -1
- package/dist/promptRegistry.js +34 -1
- package/dist/regenerator/docs/regenerator.docblock.js +22 -184
- package/dist/regenerator/executor.js +86 -1
- package/dist/regenerator/index.js +6 -1
- package/dist/regenerator/service.js +92 -1
- package/dist/regenerator/sinks.js +32 -1
- package/dist/regenerator/utils.js +51 -1
- package/dist/registry.js +208 -1
- package/dist/resources.js +47 -1
- package/dist/schema/dist/EnumType.js +2 -1
- package/dist/schema/dist/FieldType.js +49 -1
- package/dist/schema/dist/ScalarTypeEnum.js +236 -1
- package/dist/schema/dist/SchemaModel.js +39 -1
- package/dist/schema/dist/entity/defineEntity.js +1 -1
- package/dist/schema/dist/entity/index.js +2 -1
- package/dist/schema/dist/entity/types.js +1 -1
- package/dist/schema/dist/index.js +6 -1
- package/dist/schema-to-markdown.js +214 -10
- package/dist/server/graphql-pothos.js +128 -1
- package/dist/server/index.js +10 -1
- package/dist/server/mcp/createMcpServer.js +28 -1
- package/dist/server/mcp/registerPresentations.js +151 -1
- package/dist/server/mcp/registerPrompts.js +36 -2
- package/dist/server/mcp/registerResources.js +35 -1
- package/dist/server/mcp/registerTools.js +22 -1
- package/dist/server/provider-mcp.js +3 -1
- package/dist/server/rest-elysia.js +20 -1
- package/dist/server/rest-express.js +39 -1
- package/dist/server/rest-generic.js +125 -1
- package/dist/server/rest-next-app.js +38 -1
- package/dist/server/rest-next-mcp.js +45 -1
- package/dist/server/rest-next-pages.js +25 -1
- package/dist/spec.js +35 -1
- package/dist/telemetry/anomaly.js +48 -1
- package/dist/telemetry/docs/telemetry.docblock.js +22 -139
- package/dist/telemetry/index.js +5 -1
- package/dist/telemetry/spec.js +69 -1
- package/dist/telemetry/tracker.js +76 -1
- package/dist/tests/index.js +4 -1
- package/dist/tests/runner.js +150 -1
- package/dist/tests/spec.js +33 -1
- package/dist/themes.js +39 -1
- package/dist/workflow/adapters/db-adapter.js +83 -1
- package/dist/workflow/adapters/file-adapter.js +11 -1
- package/dist/workflow/adapters/index.js +5 -1
- package/dist/workflow/adapters/memory-store.js +58 -1
- package/dist/workflow/expression.js +98 -1
- package/dist/workflow/index.js +9 -1
- package/dist/workflow/runner.js +337 -1
- package/dist/workflow/sla-monitor.js +47 -1
- package/dist/workflow/spec.js +32 -1
- package/dist/workflow/validation.js +175 -1
- package/package.json +11 -4
|
@@ -1 +1,186 @@
|
|
|
1
|
-
import{performance
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
|
|
3
|
+
//#region src/integrations/runtime.ts
|
|
4
|
+
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
5
|
+
const DEFAULT_BACKOFF_MS = 250;
|
|
6
|
+
var IntegrationCallGuard = class {
|
|
7
|
+
telemetry;
|
|
8
|
+
maxAttempts;
|
|
9
|
+
backoffMs;
|
|
10
|
+
shouldRetry;
|
|
11
|
+
sleep;
|
|
12
|
+
now;
|
|
13
|
+
constructor(secretProvider, options = {}) {
|
|
14
|
+
this.secretProvider = secretProvider;
|
|
15
|
+
this.telemetry = options.telemetry;
|
|
16
|
+
this.maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);
|
|
17
|
+
this.backoffMs = options.backoffMs ?? DEFAULT_BACKOFF_MS;
|
|
18
|
+
this.shouldRetry = options.shouldRetry ?? ((error) => typeof error === "object" && error !== null && "retryable" in error && Boolean(error.retryable));
|
|
19
|
+
this.sleep = options.sleep ?? ((ms) => ms <= 0 ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, ms)));
|
|
20
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
21
|
+
}
|
|
22
|
+
async executeWithGuards(slotId, operation, input, resolvedConfig, executor) {
|
|
23
|
+
const integration = this.findIntegration(slotId, resolvedConfig);
|
|
24
|
+
if (!integration) return this.failure({
|
|
25
|
+
tenantId: resolvedConfig.tenantId,
|
|
26
|
+
appId: resolvedConfig.appId,
|
|
27
|
+
environment: resolvedConfig.environment,
|
|
28
|
+
blueprintName: resolvedConfig.blueprintName,
|
|
29
|
+
blueprintVersion: resolvedConfig.blueprintVersion,
|
|
30
|
+
configVersion: resolvedConfig.configVersion,
|
|
31
|
+
slotId,
|
|
32
|
+
operation
|
|
33
|
+
}, void 0, {
|
|
34
|
+
code: "SLOT_NOT_BOUND",
|
|
35
|
+
message: `Integration slot "${slotId}" is not bound for tenant "${resolvedConfig.tenantId}".`,
|
|
36
|
+
retryable: false
|
|
37
|
+
}, 0);
|
|
38
|
+
const status = integration.connection.status;
|
|
39
|
+
if (status === "disconnected" || status === "error") return this.failure(this.makeContext(slotId, operation, resolvedConfig), integration, {
|
|
40
|
+
code: "CONNECTION_NOT_READY",
|
|
41
|
+
message: `Integration connection "${integration.connection.meta.label}" is in status "${status}".`,
|
|
42
|
+
retryable: false
|
|
43
|
+
}, 0);
|
|
44
|
+
const secrets = await this.fetchSecrets(integration.connection);
|
|
45
|
+
let attempt = 0;
|
|
46
|
+
const started = performance.now();
|
|
47
|
+
while (attempt < this.maxAttempts) {
|
|
48
|
+
attempt += 1;
|
|
49
|
+
try {
|
|
50
|
+
const data = await executor(integration.connection, secrets);
|
|
51
|
+
const duration = performance.now() - started;
|
|
52
|
+
this.emitTelemetry(this.makeContext(slotId, operation, resolvedConfig), integration, "success", duration);
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
data,
|
|
56
|
+
metadata: {
|
|
57
|
+
latencyMs: duration,
|
|
58
|
+
connectionId: integration.connection.meta.id,
|
|
59
|
+
ownershipMode: integration.connection.ownershipMode,
|
|
60
|
+
attempts: attempt
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
const duration = performance.now() - started;
|
|
65
|
+
this.emitTelemetry(this.makeContext(slotId, operation, resolvedConfig), integration, "error", duration, this.errorCodeFor(error), error instanceof Error ? error.message : String(error));
|
|
66
|
+
const retryable = this.shouldRetry(error, attempt);
|
|
67
|
+
if (!retryable || attempt >= this.maxAttempts) return {
|
|
68
|
+
success: false,
|
|
69
|
+
error: {
|
|
70
|
+
code: this.errorCodeFor(error),
|
|
71
|
+
message: error instanceof Error ? error.message : String(error),
|
|
72
|
+
retryable,
|
|
73
|
+
cause: error
|
|
74
|
+
},
|
|
75
|
+
metadata: {
|
|
76
|
+
latencyMs: duration,
|
|
77
|
+
connectionId: integration.connection.meta.id,
|
|
78
|
+
ownershipMode: integration.connection.ownershipMode,
|
|
79
|
+
attempts: attempt
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
await this.sleep(this.backoffMs);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: {
|
|
88
|
+
code: "UNKNOWN_ERROR",
|
|
89
|
+
message: "Integration call failed after retries.",
|
|
90
|
+
retryable: false
|
|
91
|
+
},
|
|
92
|
+
metadata: {
|
|
93
|
+
latencyMs: performance.now() - started,
|
|
94
|
+
connectionId: integration.connection.meta.id,
|
|
95
|
+
ownershipMode: integration.connection.ownershipMode,
|
|
96
|
+
attempts: this.maxAttempts
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
findIntegration(slotId, config) {
|
|
101
|
+
return config.integrations.find((integration) => integration.slot.slotId === slotId);
|
|
102
|
+
}
|
|
103
|
+
async fetchSecrets(connection) {
|
|
104
|
+
if (!this.secretProvider.canHandle(connection.secretRef)) throw new Error(`Secret provider "${this.secretProvider.id}" cannot handle reference "${connection.secretRef}".`);
|
|
105
|
+
const secret = await this.secretProvider.getSecret(connection.secretRef);
|
|
106
|
+
return this.parseSecret(secret);
|
|
107
|
+
}
|
|
108
|
+
parseSecret(secret) {
|
|
109
|
+
const text = new TextDecoder().decode(secret.data);
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(text);
|
|
112
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
113
|
+
const entries = Object.entries(parsed).filter(([, value]) => typeof value === "string" || typeof value === "number" || typeof value === "boolean");
|
|
114
|
+
return Object.fromEntries(entries.map(([key, value]) => [key, String(value)]));
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
117
|
+
return { secret: text };
|
|
118
|
+
}
|
|
119
|
+
emitTelemetry(context, integration, status, durationMs, errorCode, errorMessage) {
|
|
120
|
+
if (!this.telemetry || !integration) return;
|
|
121
|
+
this.telemetry.record({
|
|
122
|
+
tenantId: context.tenantId,
|
|
123
|
+
appId: context.appId,
|
|
124
|
+
environment: context.environment,
|
|
125
|
+
slotId: context.slotId,
|
|
126
|
+
integrationKey: integration.connection.meta.integrationKey,
|
|
127
|
+
integrationVersion: integration.connection.meta.integrationVersion,
|
|
128
|
+
connectionId: integration.connection.meta.id,
|
|
129
|
+
status,
|
|
130
|
+
durationMs,
|
|
131
|
+
errorCode,
|
|
132
|
+
errorMessage,
|
|
133
|
+
occurredAt: this.now(),
|
|
134
|
+
metadata: {
|
|
135
|
+
blueprint: `${context.blueprintName}.v${context.blueprintVersion}`,
|
|
136
|
+
configVersion: context.configVersion,
|
|
137
|
+
operation: context.operation
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
failure(context, integration, error, attempts) {
|
|
142
|
+
if (integration) this.emitTelemetry(context, integration, "error", 0, error.code, error.message);
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
error,
|
|
146
|
+
metadata: {
|
|
147
|
+
latencyMs: 0,
|
|
148
|
+
connectionId: integration?.connection.meta.id ?? "unknown",
|
|
149
|
+
ownershipMode: integration?.connection.ownershipMode ?? "managed",
|
|
150
|
+
attempts
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
makeContext(slotId, operation, config) {
|
|
155
|
+
return {
|
|
156
|
+
tenantId: config.tenantId,
|
|
157
|
+
appId: config.appId,
|
|
158
|
+
environment: config.environment,
|
|
159
|
+
blueprintName: config.blueprintName,
|
|
160
|
+
blueprintVersion: config.blueprintVersion,
|
|
161
|
+
configVersion: config.configVersion,
|
|
162
|
+
slotId,
|
|
163
|
+
operation
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
errorCodeFor(error) {
|
|
167
|
+
if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") return error.code;
|
|
168
|
+
return "PROVIDER_ERROR";
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
function ensureConnectionReady(integration) {
|
|
172
|
+
const status = integration.connection.status;
|
|
173
|
+
if (status === "disconnected" || status === "error") throw new Error(`Integration connection "${integration.connection.meta.label}" is in status "${status}".`);
|
|
174
|
+
}
|
|
175
|
+
function connectionStatusLabel(status) {
|
|
176
|
+
switch (status) {
|
|
177
|
+
case "connected": return "connected";
|
|
178
|
+
case "disconnected": return "disconnected";
|
|
179
|
+
case "error": return "error";
|
|
180
|
+
case "unknown":
|
|
181
|
+
default: return "unknown";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
//#endregion
|
|
186
|
+
export { IntegrationCallGuard, connectionStatusLabel, ensureConnectionReady };
|
|
@@ -1 +1,231 @@
|
|
|
1
|
-
import{SecretProviderError
|
|
1
|
+
import { SecretProviderError, normalizeSecretPayload, parseSecretUri } from "./provider.js";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import { CreateSecretCommand, DeleteSecretCommand, GetSecretValueCommand, PutSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
|
|
4
|
+
|
|
5
|
+
//#region src/integrations/secrets/aws-secret-manager.ts
|
|
6
|
+
const DEFAULT_DELETE_RECOVERY_DAYS = 7;
|
|
7
|
+
var AwsSecretsManagerProvider = class {
|
|
8
|
+
id = "aws-secrets-manager";
|
|
9
|
+
explicitRegion;
|
|
10
|
+
injectedClient;
|
|
11
|
+
clientConfig;
|
|
12
|
+
clientsByRegion = /* @__PURE__ */ new Map();
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.explicitRegion = options.region;
|
|
15
|
+
this.injectedClient = options.client;
|
|
16
|
+
this.clientConfig = options.clientConfig;
|
|
17
|
+
}
|
|
18
|
+
canHandle(reference) {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = parseSecretUri(reference);
|
|
21
|
+
return parsed.provider === "aws" && (parsed.path === "secretsmanager" || parsed.path.startsWith("secretsmanager/"));
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async getSecret(reference, options) {
|
|
27
|
+
const location = this.parseReference(reference);
|
|
28
|
+
const client = this.getClient(location.region);
|
|
29
|
+
const requestedVersion = options?.version ?? location.stage ?? location.version;
|
|
30
|
+
const input = {
|
|
31
|
+
SecretId: location.secretId,
|
|
32
|
+
...this.buildVersionSelector(requestedVersion)
|
|
33
|
+
};
|
|
34
|
+
try {
|
|
35
|
+
const result = await client.send(new GetSecretValueCommand(input));
|
|
36
|
+
return {
|
|
37
|
+
data: extractAwsSecretBytes(result, reference, this.id),
|
|
38
|
+
version: typeof result.VersionId === "string" && result.VersionId ? result.VersionId : requestedVersion,
|
|
39
|
+
metadata: {
|
|
40
|
+
region: location.region,
|
|
41
|
+
secretId: location.secretId,
|
|
42
|
+
...requestedVersion ? { requestedVersion } : {}
|
|
43
|
+
},
|
|
44
|
+
retrievedAt: /* @__PURE__ */ new Date()
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw toAwsSecretProviderError({
|
|
48
|
+
error,
|
|
49
|
+
provider: this.id,
|
|
50
|
+
reference,
|
|
51
|
+
operation: "getSecret"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async setSecret(reference, payload) {
|
|
56
|
+
const location = this.parseReference(reference);
|
|
57
|
+
const client = this.getClient(location.region);
|
|
58
|
+
const bytes = normalizeSecretPayload(payload);
|
|
59
|
+
try {
|
|
60
|
+
const result = await client.send(new PutSecretValueCommand({
|
|
61
|
+
SecretId: location.secretId,
|
|
62
|
+
SecretBinary: bytes
|
|
63
|
+
}));
|
|
64
|
+
const versionId = typeof result.VersionId === "string" && result.VersionId ? result.VersionId : "latest";
|
|
65
|
+
return {
|
|
66
|
+
reference: this.buildReference(location.region, location.secretId, { version: versionId }),
|
|
67
|
+
version: versionId
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (!isAwsNotFound(error)) throw toAwsSecretProviderError({
|
|
71
|
+
error,
|
|
72
|
+
provider: this.id,
|
|
73
|
+
reference,
|
|
74
|
+
operation: "putSecretValue"
|
|
75
|
+
});
|
|
76
|
+
if (looksLikeAwsArn(location.secretId)) throw new SecretProviderError({
|
|
77
|
+
message: `Secret not found: ${location.secretId}`,
|
|
78
|
+
provider: this.id,
|
|
79
|
+
reference,
|
|
80
|
+
code: "NOT_FOUND",
|
|
81
|
+
cause: error
|
|
82
|
+
});
|
|
83
|
+
try {
|
|
84
|
+
const created = await client.send(new CreateSecretCommand({
|
|
85
|
+
Name: location.secretId,
|
|
86
|
+
SecretBinary: bytes
|
|
87
|
+
}));
|
|
88
|
+
const versionId = typeof created.VersionId === "string" && created.VersionId ? created.VersionId : "latest";
|
|
89
|
+
return {
|
|
90
|
+
reference: this.buildReference(location.region, location.secretId, { version: versionId }),
|
|
91
|
+
version: versionId
|
|
92
|
+
};
|
|
93
|
+
} catch (creationError) {
|
|
94
|
+
throw toAwsSecretProviderError({
|
|
95
|
+
error: creationError,
|
|
96
|
+
provider: this.id,
|
|
97
|
+
reference,
|
|
98
|
+
operation: "createSecret"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async rotateSecret(reference, payload) {
|
|
104
|
+
return this.setSecret(reference, payload);
|
|
105
|
+
}
|
|
106
|
+
async deleteSecret(reference) {
|
|
107
|
+
const location = this.parseReference(reference);
|
|
108
|
+
const client = this.getClient(location.region);
|
|
109
|
+
try {
|
|
110
|
+
await client.send(new DeleteSecretCommand({
|
|
111
|
+
SecretId: location.secretId,
|
|
112
|
+
RecoveryWindowInDays: DEFAULT_DELETE_RECOVERY_DAYS
|
|
113
|
+
}));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw toAwsSecretProviderError({
|
|
116
|
+
error,
|
|
117
|
+
provider: this.id,
|
|
118
|
+
reference,
|
|
119
|
+
operation: "deleteSecret"
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
getClient(region) {
|
|
124
|
+
if (this.injectedClient) return this.injectedClient;
|
|
125
|
+
const cached = this.clientsByRegion.get(region);
|
|
126
|
+
if (cached) return cached;
|
|
127
|
+
const client = new SecretsManagerClient({
|
|
128
|
+
...this.clientConfig ?? {},
|
|
129
|
+
region
|
|
130
|
+
});
|
|
131
|
+
this.clientsByRegion.set(region, client);
|
|
132
|
+
return client;
|
|
133
|
+
}
|
|
134
|
+
parseReference(reference) {
|
|
135
|
+
const parsed = parseSecretUri(reference);
|
|
136
|
+
if (parsed.provider !== "aws") throw new SecretProviderError({
|
|
137
|
+
message: `Unsupported secret provider: ${parsed.provider}`,
|
|
138
|
+
provider: this.id,
|
|
139
|
+
reference,
|
|
140
|
+
code: "INVALID"
|
|
141
|
+
});
|
|
142
|
+
const segments = parsed.path.split("/").filter(Boolean);
|
|
143
|
+
if (segments.length < 3 || segments[0] !== "secretsmanager") throw new SecretProviderError({
|
|
144
|
+
message: "Expected secret reference format aws://secretsmanager/{region}/{secretIdOrArn}[?version=...]",
|
|
145
|
+
provider: this.id,
|
|
146
|
+
reference,
|
|
147
|
+
code: "INVALID"
|
|
148
|
+
});
|
|
149
|
+
const regionCandidate = segments[1];
|
|
150
|
+
const region = this.resolveRegion(regionCandidate);
|
|
151
|
+
const secretId = segments.slice(2).join("/");
|
|
152
|
+
if (!secretId) throw new SecretProviderError({
|
|
153
|
+
message: `Unable to resolve secret id from reference "${parsed.path}"`,
|
|
154
|
+
provider: this.id,
|
|
155
|
+
reference,
|
|
156
|
+
code: "INVALID"
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
region,
|
|
160
|
+
secretId,
|
|
161
|
+
version: parsed.extras?.version,
|
|
162
|
+
stage: parsed.extras?.stage
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
resolveRegion(regionCandidate) {
|
|
166
|
+
const region = regionCandidate ?? this.explicitRegion ?? process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION;
|
|
167
|
+
if (!region) throw new SecretProviderError({
|
|
168
|
+
message: "AWS region must be provided either in reference (aws://secretsmanager/{region}/...) or via AWS_REGION/AWS_DEFAULT_REGION.",
|
|
169
|
+
provider: this.id,
|
|
170
|
+
reference: "aws://secretsmanager//",
|
|
171
|
+
code: "INVALID"
|
|
172
|
+
});
|
|
173
|
+
return region;
|
|
174
|
+
}
|
|
175
|
+
buildVersionSelector(version) {
|
|
176
|
+
if (!version) return {};
|
|
177
|
+
if (version === "latest" || version === "current") return { VersionStage: "AWSCURRENT" };
|
|
178
|
+
if (version.startsWith("AWS")) return { VersionStage: version };
|
|
179
|
+
return { VersionId: version };
|
|
180
|
+
}
|
|
181
|
+
buildReference(region, secretId, extras) {
|
|
182
|
+
const base = `aws://secretsmanager/${region}/${secretId}`;
|
|
183
|
+
const query = extras ? Object.entries(extras).filter(([, value]) => Boolean(value)).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&") : "";
|
|
184
|
+
return query ? `${base}?${query}` : base;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
function extractAwsSecretBytes(result, reference, provider) {
|
|
188
|
+
if (!result || typeof result !== "object") throw new SecretProviderError({
|
|
189
|
+
message: "Invalid AWS Secrets Manager response",
|
|
190
|
+
provider,
|
|
191
|
+
reference,
|
|
192
|
+
code: "UNKNOWN",
|
|
193
|
+
cause: result
|
|
194
|
+
});
|
|
195
|
+
const record = result;
|
|
196
|
+
if (record.SecretBinary instanceof Uint8Array) return record.SecretBinary;
|
|
197
|
+
if (typeof record.SecretBinary === "string") return Buffer.from(record.SecretBinary, "base64");
|
|
198
|
+
if (typeof record.SecretString === "string") return Buffer.from(record.SecretString, "utf-8");
|
|
199
|
+
throw new SecretProviderError({
|
|
200
|
+
message: "AWS secret value is empty",
|
|
201
|
+
provider,
|
|
202
|
+
reference,
|
|
203
|
+
code: "NOT_FOUND",
|
|
204
|
+
cause: result
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function looksLikeAwsArn(secretId) {
|
|
208
|
+
return secretId.startsWith("arn:aws:secretsmanager:");
|
|
209
|
+
}
|
|
210
|
+
function isAwsNotFound(error) {
|
|
211
|
+
if (!error || typeof error !== "object") return false;
|
|
212
|
+
const err = error;
|
|
213
|
+
if (typeof err.$metadata?.httpStatusCode === "number") return err.$metadata.httpStatusCode === 404;
|
|
214
|
+
return err.name === "ResourceNotFoundException";
|
|
215
|
+
}
|
|
216
|
+
function toAwsSecretProviderError(params) {
|
|
217
|
+
const { error, provider, reference, operation } = params;
|
|
218
|
+
if (error instanceof SecretProviderError) return error;
|
|
219
|
+
const httpStatusCode = typeof error === "object" && error !== null && "$metadata" in error && typeof error.$metadata === "object" && error.$metadata?.httpStatusCode;
|
|
220
|
+
const code = httpStatusCode === 404 ? "NOT_FOUND" : httpStatusCode === 401 || httpStatusCode === 403 ? "FORBIDDEN" : httpStatusCode === 400 ? "INVALID" : "UNKNOWN";
|
|
221
|
+
return new SecretProviderError({
|
|
222
|
+
message: error instanceof Error ? error.message : `Unknown error during ${operation}`,
|
|
223
|
+
provider,
|
|
224
|
+
reference,
|
|
225
|
+
code,
|
|
226
|
+
cause: error
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
//#endregion
|
|
231
|
+
export { AwsSecretsManagerProvider };
|
|
@@ -1 +1,81 @@
|
|
|
1
|
-
import{SecretProviderError
|
|
1
|
+
import { SecretProviderError, parseSecretUri } from "./provider.js";
|
|
2
|
+
|
|
3
|
+
//#region src/integrations/secrets/env-secret-provider.ts
|
|
4
|
+
/**
|
|
5
|
+
* Environment-variable backed secret provider. Read-only by design.
|
|
6
|
+
* Allows overriding other secret providers by deriving environment variable
|
|
7
|
+
* names from secret references (or by using explicit aliases).
|
|
8
|
+
*/
|
|
9
|
+
var EnvSecretProvider = class {
|
|
10
|
+
id = "env";
|
|
11
|
+
aliases;
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
this.aliases = options.aliases ?? {};
|
|
14
|
+
}
|
|
15
|
+
canHandle(reference) {
|
|
16
|
+
const envKey = this.resolveEnvKey(reference);
|
|
17
|
+
return envKey !== void 0 && process.env[envKey] !== void 0;
|
|
18
|
+
}
|
|
19
|
+
async getSecret(reference) {
|
|
20
|
+
const envKey = this.resolveEnvKey(reference);
|
|
21
|
+
if (!envKey) throw new SecretProviderError({
|
|
22
|
+
message: `Unable to resolve environment variable for reference "${reference}".`,
|
|
23
|
+
provider: this.id,
|
|
24
|
+
reference,
|
|
25
|
+
code: "INVALID"
|
|
26
|
+
});
|
|
27
|
+
const value = process.env[envKey];
|
|
28
|
+
if (value === void 0) throw new SecretProviderError({
|
|
29
|
+
message: `Environment variable "${envKey}" not found for reference "${reference}".`,
|
|
30
|
+
provider: this.id,
|
|
31
|
+
reference,
|
|
32
|
+
code: "NOT_FOUND"
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
data: Buffer.from(value, "utf-8"),
|
|
36
|
+
version: "current",
|
|
37
|
+
metadata: {
|
|
38
|
+
source: "env",
|
|
39
|
+
envKey
|
|
40
|
+
},
|
|
41
|
+
retrievedAt: /* @__PURE__ */ new Date()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async setSecret(reference, _payload) {
|
|
45
|
+
throw this.forbiddenError("setSecret", reference);
|
|
46
|
+
}
|
|
47
|
+
async rotateSecret(reference, _payload) {
|
|
48
|
+
throw this.forbiddenError("rotateSecret", reference);
|
|
49
|
+
}
|
|
50
|
+
async deleteSecret(reference) {
|
|
51
|
+
throw this.forbiddenError("deleteSecret", reference);
|
|
52
|
+
}
|
|
53
|
+
resolveEnvKey(reference) {
|
|
54
|
+
if (!reference) return;
|
|
55
|
+
if (this.aliases[reference]) return this.aliases[reference];
|
|
56
|
+
if (!reference.includes("://")) return reference;
|
|
57
|
+
try {
|
|
58
|
+
const parsed = parseSecretUri(reference);
|
|
59
|
+
if (parsed.provider === "env") return parsed.path;
|
|
60
|
+
if (parsed.extras?.env) return parsed.extras.env;
|
|
61
|
+
return this.deriveEnvKey(parsed.path);
|
|
62
|
+
} catch {
|
|
63
|
+
return reference;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
deriveEnvKey(path) {
|
|
67
|
+
if (!path) return void 0;
|
|
68
|
+
return path.split(/[\/:\-\.]/).filter(Boolean).map((segment) => segment.replace(/[^a-zA-Z0-9]/g, "_").replace(/_{2,}/g, "_").toUpperCase()).join("_");
|
|
69
|
+
}
|
|
70
|
+
forbiddenError(operation, reference) {
|
|
71
|
+
return new SecretProviderError({
|
|
72
|
+
message: `EnvSecretProvider is read-only. "${operation}" is not allowed for ${reference}.`,
|
|
73
|
+
provider: this.id,
|
|
74
|
+
reference,
|
|
75
|
+
code: "FORBIDDEN"
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { EnvSecretProvider };
|