@objectstack/connector-openapi 15.0.0 → 15.1.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/connector-openapi@15.0.0 build /home/runner/work/framework/framework/packages/connectors/connector-openapi
2
+ > @objectstack/connector-openapi@15.1.1 build /home/runner/work/framework/framework/packages/connectors/connector-openapi
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- ESM dist/index.mjs 7.73 KB
14
- ESM dist/index.mjs.map 23.76 KB
15
- ESM ⚡️ Build success in 83ms
16
- CJS dist/index.js 8.82 KB
17
- CJS dist/index.js.map 25.11 KB
18
- CJS ⚡️ Build success in 100ms
13
+ CJS dist/index.js 13.11 KB
14
+ CJS dist/index.js.map 36.97 KB
15
+ CJS ⚡️ Build success in 99ms
16
+ ESM dist/index.mjs 11.86 KB
17
+ ESM dist/index.mjs.map 35.41 KB
18
+ ESM ⚡️ Build success in 100ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 9287ms
21
- DTS dist/index.d.mts 6.11 KB
22
- DTS dist/index.d.ts 6.11 KB
20
+ DTS ⚡️ Build success in 11860ms
21
+ DTS dist/index.d.mts 9.16 KB
22
+ DTS dist/index.d.ts 9.16 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,123 @@
1
1
  # @objectstack/connector-openapi
2
2
 
3
+ ## 15.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - @objectstack/spec@15.1.1
8
+ - @objectstack/core@15.1.1
9
+
10
+ ## 15.1.0
11
+
12
+ ### Minor Changes
13
+
14
+ - f531a26: feat(connectors): ADR-0096 — provider-bound declarative connector instances materialized at boot (#2977)
15
+
16
+ Declarative `connectors:` stack entries used to be **descriptor-only** (#2612):
17
+ registered as metadata but never dispatchable, the platform's one dead metadata
18
+ surface. An entry may now name a **`provider`** — an installed generic executor
19
+ (`openapi` / `mcp` / `rest`) — and the automation service **materializes** it
20
+ into a live, dispatchable connector at boot. AI can now wire an integration as
21
+ pure metadata and a flow `connector_action` calls it end-to-end.
22
+
23
+ - **Schema (`@objectstack/spec`).** `ConnectorSchema` gains `provider`,
24
+ `providerConfig`, and `auth` (a `credentialRef`-based instance-auth shape —
25
+ `ConnectorInstanceAuthSchema` — that references credentials, never inlines
26
+ them); `authentication` now defaults to `{ type: 'none' }` so a provider-bound
27
+ instance need not author it (loosening — existing connectors are unaffected).
28
+ `DeclarativeConnectorEntrySchema` (used by `stack.zod.ts`) rejects inline
29
+ secrets, orphan `providerConfig`/`auth`, and authored `actions`/`triggers` on a
30
+ provider-bound entry. A new `integration/connector-provider.ts` defines the
31
+ provider-factory contract as pure types.
32
+
33
+ - **Engine + boot (`@objectstack/service-automation`).** The engine adds a
34
+ connector-provider registry (`registerConnectorProvider`/`getConnectorProvider`)
35
+ and origin-tags registered connectors. At boot the service resolves each
36
+ provider-bound entry — looking up the factory, resolving `auth.credentialRef`
37
+ via a pluggable `CredentialResolver` (open-tier default: environment
38
+ variables), and registering the materialized connector. Boot **fails loudly**
39
+ for an unknown provider, invalid `providerConfig`, an unresolvable
40
+ `credentialRef`, or a name conflict with a plugin-registered connector (no
41
+ silent precedence).
42
+
43
+ - **Providers (`connector-rest` / `connector-openapi` / `connector-mcp`).** Each
44
+ plugin registers a provider factory in `init()` reusing its existing
45
+ generator/adapter API. Plugin options are now **optional**: with none the
46
+ plugin contributes only its provider factory; with instance options it also
47
+ registers a hand-wired connector (back-compat). `connector-openapi` adds a
48
+ `ConnectorOpenApiPlugin`.
49
+
50
+ Open tier: static auth (`none`/`api-key`/`basic`/`bearer`) with `credentialRef`
51
+ resolved from env vars. Managed vaulting, OAuth2 refresh, and per-tenant
52
+ connection lifecycle remain the enterprise tier (ADR-0015) — an enterprise host
53
+ injects a vault-backed `CredentialResolver` with no change to the materialization
54
+ path.
55
+
56
+ - f531a26: feat(connector-openapi): resolve `providerConfig.spec` from a package-relative file path (#3016, ADR-0096 follow-up)
57
+
58
+ ADR-0096's canonical example authors an OpenAPI-backed instance as
59
+ `providerConfig: { spec: './billing-openapi.json' }`, but the landed `openapi`
60
+ provider factory only accepted an inline document object or an http(s) URL.
61
+ The spec union is now complete: **inline object | file path | remote URL**.
62
+
63
+ - **`@objectstack/spec`.** `ConnectorProviderContext` gains an optional
64
+ host-injected `loadPackageFile(relativePath)` capability (pure type): reads a
65
+ UTF-8 file resolved against the declaring stack/package root, confined to
66
+ that root. `undefined` on hosts without a filesystem.
67
+
68
+ - **`@objectstack/service-automation`.** New `packageRoot` plugin option (the
69
+ base for relative file refs; defaults to `process.cwd()`) and an exported
70
+ `createPackageFileLoader(packageRoot)` that implements the confinement
71
+ guard — absolute paths and `..`-escaping paths are rejected — with lazy
72
+ `node:fs`/`node:path` imports so non-Node hosts only fail if a file ref is
73
+ actually dereferenced. The materializer injects the capability into every
74
+ provider factory's context. Failures follow the existing reconcile policy:
75
+ **fatal at boot, entry skipped on reload**.
76
+
77
+ - **`@objectstack/connector-openapi`.** A string `providerConfig.spec` that is
78
+ not an http(s) URL is now read via `ctx.loadPackageFile` and parsed as an
79
+ OpenAPI JSON document (clear errors for missing/unreadable files, unparseable
80
+ JSON, and hosts without package file access).
81
+
82
+ - **`@objectstack/cli`.** `serve`/`dev` pass the project folder (the
83
+ `objectstack.config.ts` directory) as the automation service's `packageRoot`,
84
+ mirroring how the standalone sqlite default is anchored.
85
+
86
+ ### Patch Changes
87
+
88
+ - Updated dependencies [f531a26]
89
+ - Updated dependencies [f531a26]
90
+ - Updated dependencies [f531a26]
91
+ - Updated dependencies [f531a26]
92
+ - Updated dependencies [f531a26]
93
+ - Updated dependencies [f531a26]
94
+ - Updated dependencies [3fe9df1]
95
+ - Updated dependencies [f531a26]
96
+ - Updated dependencies [f531a26]
97
+ - Updated dependencies [f531a26]
98
+ - Updated dependencies [f531a26]
99
+ - Updated dependencies [f531a26]
100
+ - Updated dependencies [f531a26]
101
+ - Updated dependencies [f531a26]
102
+ - Updated dependencies [f531a26]
103
+ - Updated dependencies [f531a26]
104
+ - Updated dependencies [f531a26]
105
+ - Updated dependencies [f531a26]
106
+ - Updated dependencies [f531a26]
107
+ - Updated dependencies [4109153]
108
+ - Updated dependencies [f531a26]
109
+ - Updated dependencies [f531a26]
110
+ - Updated dependencies [f531a26]
111
+ - Updated dependencies [f531a26]
112
+ - Updated dependencies [f531a26]
113
+ - Updated dependencies [f531a26]
114
+ - Updated dependencies [627f225]
115
+ - Updated dependencies [f531a26]
116
+ - Updated dependencies [f531a26]
117
+ - Updated dependencies [f531a26]
118
+ - @objectstack/spec@15.1.0
119
+ - @objectstack/core@15.1.0
120
+
3
121
  ## 15.0.0
4
122
 
5
123
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { Connector } from '@objectstack/spec/integration';
1
+ import { Connector, ConnectorProviderFactory } from '@objectstack/spec/integration';
2
+ import { Plugin, PluginContext } from '@objectstack/core';
2
3
 
3
4
  /**
4
5
  * OpenAPI connector generator — turns a declarative OpenAPI 3.x document into a
@@ -128,14 +129,15 @@ interface OpenApiConnectorConfig {
128
129
  declare function createOpenApiConnector(config: OpenApiConnectorConfig): OpenApiConnectorBundle;
129
130
 
130
131
  /**
131
- * Minimal surface of the automation engine this helper depends on — the
132
- * connector registry from ADR-0018 §Addendum. Kept structural so callers need
133
- * no runtime dependency on `@objectstack/service-automation` (mirrors
134
- * connector-rest / connector-mcp).
132
+ * Minimal surface of the automation engine this package depends on — the
133
+ * connector registry (ADR-0018 §Addendum) plus the provider registry (ADR-0097).
134
+ * Kept structural so callers need no runtime dependency on
135
+ * `@objectstack/service-automation` (mirrors connector-rest / connector-mcp).
135
136
  */
136
137
  interface ConnectorRegistrySurface {
137
138
  registerConnector(def: Connector, handlers: Record<string, (input: Record<string, unknown>, ctx: unknown) => Promise<Record<string, unknown>>>): void;
138
139
  unregisterConnector(name: string): void;
140
+ registerConnectorProvider(providerKey: string, factory: ConnectorProviderFactory): void;
139
141
  }
140
142
  /**
141
143
  * Generate an OpenAPI-backed connector and register it on the engine's connector
@@ -143,5 +145,65 @@ interface ConnectorRegistrySurface {
143
145
  * actions (ADR-0023). Returns the registered connector name.
144
146
  */
145
147
  declare function registerOpenApiConnector(registry: ConnectorRegistrySurface, config: OpenApiConnectorConfig): string;
148
+ /**
149
+ * Options for {@link ConnectorOpenApiPlugin}. All optional (ADR-0097): with no
150
+ * `document` the plugin contributes only the `openapi` provider factory — so a
151
+ * stack can declare `provider: 'openapi'` instances as pure metadata. Supply a
152
+ * `document` (+ config) to ALSO register one hand-wired OpenAPI connector.
153
+ */
154
+ interface ConnectorOpenApiPluginOptions extends Partial<OpenApiConnectorConfig> {
155
+ /** Injected fetch implementation forwarded to the `openapi` provider factory (tests). */
156
+ fetchImpl?: typeof fetch;
157
+ }
158
+ /**
159
+ * ConnectorOpenApiPlugin — contributes the OpenAPI generic executor (ADR-0023) in
160
+ * two forms:
161
+ *
162
+ * 1. **Provider factory** (`openapi`, ADR-0097): registered at `init()` so the
163
+ * automation service can materialize declarative `provider: 'openapi'`
164
+ * `connectors:` entries — loading the OpenAPI document and mapping each
165
+ * operation to a connector action — at boot.
166
+ * 2. **Hand-wired instance** (optional, back-compat): when constructed with a
167
+ * `document`, it also registers one concrete OpenAPI connector at `start()`.
168
+ *
169
+ * If no automation engine is present the plugin logs and skips.
170
+ */
171
+ declare class ConnectorOpenApiPlugin implements Plugin {
172
+ name: string;
173
+ version: string;
174
+ type: "standard";
175
+ dependencies: string[];
176
+ private readonly options;
177
+ private connectorName?;
178
+ private automation?;
179
+ constructor(options?: ConnectorOpenApiPluginOptions);
180
+ init(ctx: PluginContext): Promise<void>;
181
+ start(ctx: PluginContext): Promise<void>;
182
+ stop(_ctx: PluginContext): Promise<void>;
183
+ private tryGetAutomation;
184
+ }
185
+
186
+ /**
187
+ * The provider key this package contributes (ADR-0097). A declarative
188
+ * `connectors:` entry with `provider: 'openapi'` is materialized by this factory.
189
+ */
190
+ declare const OPENAPI_PROVIDER_KEY = "openapi";
191
+ /** Injectable dependencies for {@link createOpenApiProviderFactory} (tests). */
192
+ interface OpenApiProviderDeps {
193
+ /** Injected fetch implementation (spec fetch + request transport); defaults to global `fetch`. */
194
+ fetchImpl?: typeof fetch;
195
+ }
196
+ /**
197
+ * Build the `openapi` {@link ConnectorProviderFactory} (ADR-0097 / ADR-0023). At
198
+ * boot the automation service invokes it for each `provider: 'openapi'`
199
+ * declarative instance: it loads the OpenAPI document from `providerConfig.spec`,
200
+ * then produces the same `{ def, handlers }` bundle {@link createOpenApiConnector}
201
+ * generates for a hand-wired OpenAPI connector — one action per operation over a
202
+ * static-auth HTTP transport, with the resolved `auth` applied.
203
+ *
204
+ * Hard-fails on invalid config (missing/unfetchable spec, no base URL), so a
205
+ * misconfigured instance fails boot loudly.
206
+ */
207
+ declare function createOpenApiProviderFactory(deps?: OpenApiProviderDeps): ConnectorProviderFactory;
146
208
 
147
- export { type ConnectorRegistrySurface, type JsonSchema, type OpenApiConnectorBundle, type OpenApiConnectorConfig, type OpenApiDocument, type OpenApiOperation, type OpenApiParameter, type OpenApiPathItem, type OpenApiRequestBody, type OpenApiResponse, type OpenApiSecurityScheme, type OperationInfo, type RestAuth, createOpenApiConnector, registerOpenApiConnector };
209
+ export { ConnectorOpenApiPlugin, type ConnectorOpenApiPluginOptions, type ConnectorRegistrySurface, type JsonSchema, OPENAPI_PROVIDER_KEY, type OpenApiConnectorBundle, type OpenApiConnectorConfig, type OpenApiDocument, type OpenApiOperation, type OpenApiParameter, type OpenApiPathItem, type OpenApiProviderDeps, type OpenApiRequestBody, type OpenApiResponse, type OpenApiSecurityScheme, type OperationInfo, type RestAuth, createOpenApiConnector, createOpenApiProviderFactory, registerOpenApiConnector };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Connector } from '@objectstack/spec/integration';
1
+ import { Connector, ConnectorProviderFactory } from '@objectstack/spec/integration';
2
+ import { Plugin, PluginContext } from '@objectstack/core';
2
3
 
3
4
  /**
4
5
  * OpenAPI connector generator — turns a declarative OpenAPI 3.x document into a
@@ -128,14 +129,15 @@ interface OpenApiConnectorConfig {
128
129
  declare function createOpenApiConnector(config: OpenApiConnectorConfig): OpenApiConnectorBundle;
129
130
 
130
131
  /**
131
- * Minimal surface of the automation engine this helper depends on — the
132
- * connector registry from ADR-0018 §Addendum. Kept structural so callers need
133
- * no runtime dependency on `@objectstack/service-automation` (mirrors
134
- * connector-rest / connector-mcp).
132
+ * Minimal surface of the automation engine this package depends on — the
133
+ * connector registry (ADR-0018 §Addendum) plus the provider registry (ADR-0097).
134
+ * Kept structural so callers need no runtime dependency on
135
+ * `@objectstack/service-automation` (mirrors connector-rest / connector-mcp).
135
136
  */
136
137
  interface ConnectorRegistrySurface {
137
138
  registerConnector(def: Connector, handlers: Record<string, (input: Record<string, unknown>, ctx: unknown) => Promise<Record<string, unknown>>>): void;
138
139
  unregisterConnector(name: string): void;
140
+ registerConnectorProvider(providerKey: string, factory: ConnectorProviderFactory): void;
139
141
  }
140
142
  /**
141
143
  * Generate an OpenAPI-backed connector and register it on the engine's connector
@@ -143,5 +145,65 @@ interface ConnectorRegistrySurface {
143
145
  * actions (ADR-0023). Returns the registered connector name.
144
146
  */
145
147
  declare function registerOpenApiConnector(registry: ConnectorRegistrySurface, config: OpenApiConnectorConfig): string;
148
+ /**
149
+ * Options for {@link ConnectorOpenApiPlugin}. All optional (ADR-0097): with no
150
+ * `document` the plugin contributes only the `openapi` provider factory — so a
151
+ * stack can declare `provider: 'openapi'` instances as pure metadata. Supply a
152
+ * `document` (+ config) to ALSO register one hand-wired OpenAPI connector.
153
+ */
154
+ interface ConnectorOpenApiPluginOptions extends Partial<OpenApiConnectorConfig> {
155
+ /** Injected fetch implementation forwarded to the `openapi` provider factory (tests). */
156
+ fetchImpl?: typeof fetch;
157
+ }
158
+ /**
159
+ * ConnectorOpenApiPlugin — contributes the OpenAPI generic executor (ADR-0023) in
160
+ * two forms:
161
+ *
162
+ * 1. **Provider factory** (`openapi`, ADR-0097): registered at `init()` so the
163
+ * automation service can materialize declarative `provider: 'openapi'`
164
+ * `connectors:` entries — loading the OpenAPI document and mapping each
165
+ * operation to a connector action — at boot.
166
+ * 2. **Hand-wired instance** (optional, back-compat): when constructed with a
167
+ * `document`, it also registers one concrete OpenAPI connector at `start()`.
168
+ *
169
+ * If no automation engine is present the plugin logs and skips.
170
+ */
171
+ declare class ConnectorOpenApiPlugin implements Plugin {
172
+ name: string;
173
+ version: string;
174
+ type: "standard";
175
+ dependencies: string[];
176
+ private readonly options;
177
+ private connectorName?;
178
+ private automation?;
179
+ constructor(options?: ConnectorOpenApiPluginOptions);
180
+ init(ctx: PluginContext): Promise<void>;
181
+ start(ctx: PluginContext): Promise<void>;
182
+ stop(_ctx: PluginContext): Promise<void>;
183
+ private tryGetAutomation;
184
+ }
185
+
186
+ /**
187
+ * The provider key this package contributes (ADR-0097). A declarative
188
+ * `connectors:` entry with `provider: 'openapi'` is materialized by this factory.
189
+ */
190
+ declare const OPENAPI_PROVIDER_KEY = "openapi";
191
+ /** Injectable dependencies for {@link createOpenApiProviderFactory} (tests). */
192
+ interface OpenApiProviderDeps {
193
+ /** Injected fetch implementation (spec fetch + request transport); defaults to global `fetch`. */
194
+ fetchImpl?: typeof fetch;
195
+ }
196
+ /**
197
+ * Build the `openapi` {@link ConnectorProviderFactory} (ADR-0097 / ADR-0023). At
198
+ * boot the automation service invokes it for each `provider: 'openapi'`
199
+ * declarative instance: it loads the OpenAPI document from `providerConfig.spec`,
200
+ * then produces the same `{ def, handlers }` bundle {@link createOpenApiConnector}
201
+ * generates for a hand-wired OpenAPI connector — one action per operation over a
202
+ * static-auth HTTP transport, with the resolved `auth` applied.
203
+ *
204
+ * Hard-fails on invalid config (missing/unfetchable spec, no base URL), so a
205
+ * misconfigured instance fails boot loudly.
206
+ */
207
+ declare function createOpenApiProviderFactory(deps?: OpenApiProviderDeps): ConnectorProviderFactory;
146
208
 
147
- export { type ConnectorRegistrySurface, type JsonSchema, type OpenApiConnectorBundle, type OpenApiConnectorConfig, type OpenApiDocument, type OpenApiOperation, type OpenApiParameter, type OpenApiPathItem, type OpenApiRequestBody, type OpenApiResponse, type OpenApiSecurityScheme, type OperationInfo, type RestAuth, createOpenApiConnector, registerOpenApiConnector };
209
+ export { ConnectorOpenApiPlugin, type ConnectorOpenApiPluginOptions, type ConnectorRegistrySurface, type JsonSchema, OPENAPI_PROVIDER_KEY, type OpenApiConnectorBundle, type OpenApiConnectorConfig, type OpenApiDocument, type OpenApiOperation, type OpenApiParameter, type OpenApiPathItem, type OpenApiProviderDeps, type OpenApiRequestBody, type OpenApiResponse, type OpenApiSecurityScheme, type OperationInfo, type RestAuth, createOpenApiConnector, createOpenApiProviderFactory, registerOpenApiConnector };
package/dist/index.js CHANGED
@@ -20,7 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ ConnectorOpenApiPlugin: () => ConnectorOpenApiPlugin,
24
+ OPENAPI_PROVIDER_KEY: () => OPENAPI_PROVIDER_KEY,
23
25
  createOpenApiConnector: () => createOpenApiConnector,
26
+ createOpenApiProviderFactory: () => createOpenApiProviderFactory,
24
27
  registerOpenApiConnector: () => registerOpenApiConnector
25
28
  });
26
29
  module.exports = __toCommonJS(index_exports);
@@ -238,15 +241,133 @@ function titleize(name) {
238
241
  return name.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
239
242
  }
240
243
 
244
+ // src/openapi-provider.ts
245
+ var OPENAPI_PROVIDER_KEY = "openapi";
246
+ async function loadOpenApiDocument(spec, fetchImpl, ctx) {
247
+ const connectorName = ctx.name;
248
+ if (spec && typeof spec === "object" && !Array.isArray(spec)) {
249
+ return spec;
250
+ }
251
+ if (typeof spec === "string" && spec.length > 0) {
252
+ if (/^https?:\/\//i.test(spec)) {
253
+ const doFetch = fetchImpl ?? fetch;
254
+ const res = await doFetch(spec);
255
+ if (!res.ok) {
256
+ throw new Error(
257
+ `connector-openapi provider: connector '${connectorName}' failed to fetch spec '${spec}' (HTTP ${res.status}).`
258
+ );
259
+ }
260
+ return await res.json();
261
+ }
262
+ if (!ctx.loadPackageFile) {
263
+ throw new Error(
264
+ `connector-openapi provider: connector '${connectorName}' providerConfig.spec '${spec}' is a file path, but this host provides no package file access \u2014 inline the OpenAPI document or use an http(s) URL.`
265
+ );
266
+ }
267
+ let text;
268
+ try {
269
+ text = await ctx.loadPackageFile(spec);
270
+ } catch (err) {
271
+ throw new Error(
272
+ `connector-openapi provider: connector '${connectorName}' failed to read providerConfig.spec '${spec}': ${err.message}`
273
+ );
274
+ }
275
+ try {
276
+ const parsed = JSON.parse(text);
277
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
278
+ throw new Error("not a JSON object");
279
+ }
280
+ return parsed;
281
+ } catch (err) {
282
+ throw new Error(
283
+ `connector-openapi provider: connector '${connectorName}' providerConfig.spec '${spec}' is not a parseable OpenAPI JSON document: ${err.message}`
284
+ );
285
+ }
286
+ }
287
+ throw new Error(
288
+ `connector-openapi provider: connector '${connectorName}' requires providerConfig.spec \u2014 an inline OpenAPI 3.x document object, an http(s) URL, or a package-relative file path.`
289
+ );
290
+ }
291
+ function createOpenApiProviderFactory(deps = {}) {
292
+ return async (ctx) => {
293
+ const cfg = ctx.providerConfig ?? {};
294
+ if (cfg.baseUrl !== void 0 && typeof cfg.baseUrl !== "string") {
295
+ throw new Error(
296
+ `connector-openapi provider: connector '${ctx.name}' providerConfig.baseUrl must be a string when set.`
297
+ );
298
+ }
299
+ const document = await loadOpenApiDocument(cfg.spec, deps.fetchImpl, ctx);
300
+ const auth = ctx.auth;
301
+ return createOpenApiConnector({
302
+ name: ctx.name,
303
+ label: ctx.label,
304
+ description: ctx.description,
305
+ document,
306
+ baseUrl: typeof cfg.baseUrl === "string" ? cfg.baseUrl : void 0,
307
+ auth,
308
+ fetchImpl: deps.fetchImpl
309
+ });
310
+ };
311
+ }
312
+
241
313
  // src/connector-openapi-plugin.ts
242
314
  function registerOpenApiConnector(registry, config) {
243
315
  const { def, handlers } = createOpenApiConnector(config);
244
316
  registry.registerConnector(def, handlers);
245
317
  return def.name;
246
318
  }
319
+ var ConnectorOpenApiPlugin = class {
320
+ constructor(options = {}) {
321
+ this.name = "com.objectstack.connector.openapi";
322
+ this.version = "1.0.0";
323
+ this.type = "standard";
324
+ // Ensure the automation engine (and its connector/provider registries) exist first.
325
+ this.dependencies = ["com.objectstack.service-automation"];
326
+ this.options = options;
327
+ }
328
+ async init(ctx) {
329
+ const automation = this.tryGetAutomation(ctx);
330
+ if (automation && typeof automation.registerConnectorProvider === "function") {
331
+ automation.registerConnectorProvider(
332
+ OPENAPI_PROVIDER_KEY,
333
+ createOpenApiProviderFactory({ fetchImpl: this.options.fetchImpl })
334
+ );
335
+ ctx.logger.info("ConnectorOpenApiPlugin: registered 'openapi' connector provider");
336
+ }
337
+ }
338
+ async start(ctx) {
339
+ if (!this.options.document) return;
340
+ const automation = this.tryGetAutomation(ctx);
341
+ if (!automation || typeof automation.registerConnector !== "function") {
342
+ ctx.logger.info("ConnectorOpenApiPlugin: no automation engine \u2014 OpenAPI connector not registered");
343
+ return;
344
+ }
345
+ this.connectorName = registerOpenApiConnector(automation, this.options);
346
+ this.automation = automation;
347
+ ctx.logger.info(`ConnectorOpenApiPlugin: OpenAPI connector '${this.connectorName}' registered`);
348
+ }
349
+ async stop(_ctx) {
350
+ if (this.automation && this.connectorName) {
351
+ try {
352
+ this.automation.unregisterConnector(this.connectorName);
353
+ } catch {
354
+ }
355
+ }
356
+ }
357
+ tryGetAutomation(ctx) {
358
+ try {
359
+ return ctx.getService("automation");
360
+ } catch {
361
+ return void 0;
362
+ }
363
+ }
364
+ };
247
365
  // Annotate the CommonJS export names for ESM import in node:
248
366
  0 && (module.exports = {
367
+ ConnectorOpenApiPlugin,
368
+ OPENAPI_PROVIDER_KEY,
249
369
  createOpenApiConnector,
370
+ createOpenApiProviderFactory,
250
371
  registerOpenApiConnector
251
372
  });
252
373
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/openapi-connector.ts","../src/connector-openapi-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/connector-openapi\n *\n * Generate an ObjectStack {@link Connector} from a declarative OpenAPI 3.x\n * document (ADR-0023). One operation becomes one connector action; a single\n * generic handler drives a self-contained static-auth HTTP transport (mirroring\n * `@objectstack/connector-rest`). The generated connector is an ordinary\n * `type: 'api'` connector — registered via `engine.registerConnector` with no\n * new engine surface.\n *\n * Open-source scope: static auth only (`none` / `api-key` / `basic` / `bearer`),\n * credentials supplied by the caller. Managed OAuth2, credential vaulting, and\n * per-tenant lifecycle are the enterprise tier (ADR-0015 / 0022).\n */\n\nexport {\n createOpenApiConnector,\n type OpenApiConnectorBundle,\n type OpenApiConnectorConfig,\n type OpenApiDocument,\n type OpenApiPathItem,\n type OpenApiOperation,\n type OpenApiParameter,\n type OpenApiRequestBody,\n type OpenApiResponse,\n type OpenApiSecurityScheme,\n type OperationInfo,\n type RestAuth,\n type JsonSchema,\n} from './openapi-connector.js';\nexport {\n registerOpenApiConnector,\n type ConnectorRegistrySurface,\n} from './connector-openapi-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Connector } from '@objectstack/spec/integration';\n\n/**\n * OpenAPI connector generator — turns a declarative OpenAPI 3.x document into a\n * {@link Connector} definition + handler map (ADR-0023).\n *\n * Each OpenAPI operation maps to one connector action; a single generic handler\n * (closing over the operation's method + path template) drives one shared HTTP\n * request implementation. That transport mirrors `@objectstack/connector-rest`\n * (build URL from base+path+query, apply static auth, JSON-encode the body,\n * normalise the response to `{ status, ok, body }`) — kept inline so this package\n * stays self-contained, depending only on `@objectstack/core` + `@objectstack/spec`\n * like its sibling connectors. The output is an ordinary `type: 'api'` connector,\n * registered via `engine.registerConnector(def, handlers)` exactly like a\n * hand-written one — the registry, the `connector_action` node, the discovery\n * route, and the Studio palette never know it came from OpenAPI.\n *\n * Open-source scope: **static** auth only (`none` / `api-key` / `basic` /\n * `bearer`), with credentials supplied by the caller. Managed OAuth2, credential\n * vaulting, and per-tenant lifecycle are the enterprise tier (ADR-0015 / 0022).\n */\n\n/** Static auth understood by the generated connector (the open-source subset). */\nexport type RestAuth = Extract<Connector['authentication'], { type: 'none' | 'api-key' | 'basic' | 'bearer' }>;\n\n/** An action on a Connector definition (derived to avoid guessing export names). */\ntype ConnectorAction = NonNullable<Connector['actions']>[number];\n\n/** Handler signature accepted by the connector registry (ADR-0018 §Addendum). */\ntype ConnectorHandler = (input: Record<string, unknown>, ctx: unknown) => Promise<Record<string, unknown>>;\n\n/** A connector definition paired with its action handlers, ready for registerConnector(). */\nexport interface OpenApiConnectorBundle {\n def: Connector;\n handlers: Record<string, ConnectorHandler>;\n}\n\n/** A free-form JSON Schema fragment (matches ConnectorAction input/outputSchema). */\nexport type JsonSchema = Record<string, unknown>;\n\n/** Minimal subset of an OpenAPI 3.x document consumed by the generator.\n * The caller is responsible for loading and de-referencing ($ref) the doc. */\nexport interface OpenApiDocument {\n openapi?: string;\n info?: { title?: string; description?: string; version?: string };\n servers?: { url: string }[];\n paths?: Record<string, OpenApiPathItem>;\n components?: { securitySchemes?: Record<string, OpenApiSecurityScheme> };\n}\n\nexport interface OpenApiPathItem {\n [method: string]: OpenApiOperation | unknown;\n}\n\nexport interface OpenApiOperation {\n operationId?: string;\n summary?: string;\n description?: string;\n tags?: string[];\n parameters?: OpenApiParameter[];\n requestBody?: OpenApiRequestBody;\n responses?: Record<string, OpenApiResponse>;\n}\n\nexport interface OpenApiParameter {\n name: string;\n in: 'path' | 'query' | 'header' | 'cookie';\n required?: boolean;\n description?: string;\n schema?: JsonSchema;\n}\n\nexport interface OpenApiRequestBody {\n required?: boolean;\n description?: string;\n content?: Record<string, { schema?: JsonSchema }>;\n}\n\nexport interface OpenApiResponse {\n description?: string;\n content?: Record<string, { schema?: JsonSchema }>;\n}\n\nexport interface OpenApiSecurityScheme {\n type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';\n name?: string;\n in?: 'header' | 'query' | 'cookie';\n scheme?: string;\n}\n\n/** Flattened view of a single operation, passed to the `include` predicate. */\nexport interface OperationInfo {\n operationId?: string;\n method: string;\n path: string;\n tags?: string[];\n summary?: string;\n description?: string;\n}\n\n/** Configuration for {@link createOpenApiConnector}. */\nexport interface OpenApiConnectorConfig {\n /** Connector machine name (snake_case). Defaults to a slug of info.title. */\n name?: string;\n /** Human-friendly label. Defaults to info.title (then name). */\n label?: string;\n /** Description. Defaults to info.description. */\n description?: string;\n /** Icon identifier for the Studio palette. Defaults to `globe`. */\n icon?: string;\n /** The parsed OpenAPI 3.x document (caller loads/derefs it). */\n document: OpenApiDocument;\n /** Override the base URL (else servers[0].url). */\n baseUrl?: string;\n /** Static auth with credentials. Defaults to `{ type: 'none' }`. */\n auth?: RestAuth;\n /** Headers merged into every request (request-level headers win). */\n defaultHeaders?: Record<string, string>;\n /** Only include operations for which this predicate returns true (allowlist). */\n include?: (op: OperationInfo) => boolean;\n /** Injected fetch implementation (defaults to global `fetch`). */\n fetchImpl?: typeof fetch;\n}\n\n/** OpenAPI HTTP method keys, in a deterministic order. */\nconst HTTP_METHODS = ['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace'] as const;\n\n/** Input passed to the shared request transport. */\ninterface RequestInput {\n method: string;\n path: string;\n headers?: Record<string, string>;\n query?: Record<string, string>;\n body?: unknown;\n}\n\n/**\n * Build an OpenAPI connector definition and its handler map.\n *\n * @returns the `Connector` definition (`def`) and a `handlers` record keyed by\n * action key, suitable for `engine.registerConnector(def, handlers)`.\n */\nexport function createOpenApiConnector(config: OpenApiConnectorConfig): OpenApiConnectorBundle {\n const { document, include } = config;\n const auth: RestAuth = config.auth ?? { type: 'none' };\n const doFetch = config.fetchImpl ?? fetch;\n const name = config.name ?? slug(document.info?.title ?? 'openapi_connector');\n const label = config.label ?? document.info?.title ?? titleize(name);\n const description = config.description ?? document.info?.description;\n const baseUrl = config.baseUrl ?? document.servers?.[0]?.url;\n if (!baseUrl) {\n throw new Error('createOpenApiConnector: no base URL — provide config.baseUrl or document.servers[0].url');\n }\n\n // One shared transport (mirrors connector-rest) reused by every action handler.\n async function request(input: RequestInput): Promise<Record<string, unknown>> {\n const method = input.method.toUpperCase();\n const headers: Record<string, string> = { ...config.defaultHeaders, ...input.headers };\n const query: Record<string, string> = { ...input.query };\n applyAuth(auth, headers, query);\n\n const url = buildUrl(baseUrl as string, input.path, query);\n const hasBody = input.body !== undefined && method !== 'GET' && method !== 'HEAD';\n if (hasBody && headers['Content-Type'] === undefined && headers['content-type'] === undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const response = await doFetch(url, {\n method,\n headers,\n body: hasBody ? JSON.stringify(input.body) : undefined,\n });\n\n const contentType = response.headers.get('content-type') ?? '';\n const parsed = contentType.includes('application/json') ? await response.json() : await response.text();\n return { status: response.status, ok: response.ok, body: parsed };\n }\n\n const actions: ConnectorAction[] = [];\n const handlers: Record<string, ConnectorHandler> = {};\n const seenKeys = new Set<string>();\n\n for (const op of collectOperations(document)) {\n if (include && !include(toInfo(op))) continue;\n const key = uniqueKey(op.operationId ?? slug(`${op.method}_${op.path}`), seenKeys);\n\n actions.push({\n key,\n label: op.summary ?? titleize(key),\n description: op.description,\n inputSchema: buildInputSchema(op),\n outputSchema: buildOutputSchema(op),\n });\n\n handlers[key] = async (input: Record<string, unknown>) => {\n const req = input as { path?: unknown; query?: unknown; header?: unknown; body?: unknown };\n return request({\n method: op.method,\n path: interpolatePath(op.path, asRecord(req.path)),\n query: stringifyValues(asRecord(req.query)),\n headers: stringifyValues(asRecord(req.header)),\n body: req.body,\n });\n };\n }\n\n const def: Connector = {\n name,\n label,\n type: 'api',\n description,\n icon: config.icon ?? 'globe',\n authentication: auth,\n // Defaulted by ConnectorSchema; set explicitly so the literal satisfies\n // the (post-parse) Connector output type (mirrors connector-rest/mcp).\n status: 'active',\n enabled: true,\n connectionTimeoutMs: 30000,\n requestTimeoutMs: 30000,\n actions,\n };\n\n return { def, handlers };\n}\n\ninterface Op extends OpenApiOperation {\n method: string;\n path: string;\n}\n\n/** Flatten paths × methods into a deterministic list of operations. */\nfunction collectOperations(doc: OpenApiDocument): Op[] {\n const ops: Op[] = [];\n for (const [path, item] of Object.entries(doc.paths ?? {})) {\n if (!item || typeof item !== 'object') continue;\n const record = item as Record<string, unknown>;\n for (const method of HTTP_METHODS) {\n const operation = record[method] as OpenApiOperation | undefined;\n if (!operation || typeof operation !== 'object') continue;\n ops.push({ ...operation, method, path });\n }\n }\n return ops;\n}\n\nfunction toInfo(op: Op): OperationInfo {\n return {\n operationId: op.operationId,\n method: op.method,\n path: op.path,\n tags: op.tags,\n summary: op.summary,\n description: op.description,\n };\n}\n\n/**\n * Assemble the action inputSchema from an operation's parameters + requestBody.\n * Produces { type: 'object', properties: { path, query, header, body }, required }\n * where only non-empty sections are emitted.\n */\nfunction buildInputSchema(op: OpenApiOperation): JsonSchema | undefined {\n const sections: Record<'path' | 'query' | 'header', { props: Record<string, JsonSchema>; required: string[] }> = {\n path: { props: {}, required: [] },\n query: { props: {}, required: [] },\n header: { props: {}, required: [] },\n };\n\n for (const p of op.parameters ?? []) {\n if (!p || typeof p !== 'object' || '$ref' in p) continue;\n if (p.in !== 'path' && p.in !== 'query' && p.in !== 'header') continue;\n const sec = sections[p.in];\n sec.props[p.name] = p.schema ?? (p.description ? { type: 'string', description: p.description } : { type: 'string' });\n if (p.required) sec.required.push(p.name);\n }\n\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n for (const where of ['path', 'query', 'header'] as const) {\n const sec = sections[where];\n if (Object.keys(sec.props).length === 0) continue;\n const schema: JsonSchema = { type: 'object', properties: sec.props };\n if (sec.required.length) schema.required = sec.required;\n properties[where] = schema;\n // Path params are always required when present; others only if any are.\n if (where === 'path' || sec.required.length) required.push(where);\n }\n\n const bodySchema = extractRequestBodySchema(op.requestBody);\n if (bodySchema) {\n properties.body = bodySchema;\n if (op.requestBody && !('$ref' in op.requestBody) && op.requestBody.required) required.push('body');\n }\n\n if (Object.keys(properties).length === 0) return undefined;\n const schema: JsonSchema = { type: 'object', properties };\n if (required.length) schema.required = required;\n return schema;\n}\n\n/** Pick the success response's JSON schema (200 → first 2xx → default). */\nfunction buildOutputSchema(op: OpenApiOperation): JsonSchema | undefined {\n const responses = op.responses;\n if (!responses) return undefined;\n let code: string | undefined;\n if (responses['200']) code = '200';\n else code = Object.keys(responses).find((c) => /^2\\d\\d$/.test(c));\n if (!code && responses['default']) code = 'default';\n if (!code) return undefined;\n const resp = responses[code];\n if (!resp || typeof resp !== 'object' || '$ref' in resp) return undefined;\n return pickJsonSchema(resp.content);\n}\n\n/** Extract the requestBody JSON schema (prefers application/json). */\nfunction extractRequestBodySchema(rb: OpenApiRequestBody | undefined): JsonSchema | undefined {\n if (!rb || typeof rb !== 'object' || '$ref' in rb) return undefined;\n return pickJsonSchema(rb.content);\n}\n\n/** Choose the application/json schema, falling back to the first content type. */\nfunction pickJsonSchema(content: Record<string, { schema?: JsonSchema }> | undefined): JsonSchema | undefined {\n if (!content) return undefined;\n const chosen = content['application/json'] ?? Object.values(content)[0];\n return chosen?.schema;\n}\n\n/** Build the request URL from base + path + query, encoding query params. */\nfunction buildUrl(baseUrl: string, path: string, query: Record<string, string>): string {\n const base = baseUrl.replace(/\\/+$/, '');\n const suffix = path ? (path.startsWith('/') ? path : `/${path}`) : '';\n const url = new URL(base + suffix);\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined && value !== null) url.searchParams.set(key, String(value));\n }\n return url.toString();\n}\n\n/** Apply static auth to the outgoing headers / query (mirrors connector-rest). */\nfunction applyAuth(auth: RestAuth, headers: Record<string, string>, query: Record<string, string>): void {\n switch (auth.type) {\n case 'none':\n return;\n case 'bearer':\n headers['Authorization'] = `Bearer ${auth.token}`;\n return;\n case 'basic': {\n const encoded = Buffer.from(`${auth.username}:${auth.password}`).toString('base64');\n headers['Authorization'] = `Basic ${encoded}`;\n return;\n }\n case 'api-key':\n if (auth.paramName) query[auth.paramName] = auth.key;\n else headers[auth.headerName ?? 'X-API-Key'] = auth.key;\n return;\n }\n}\n\n/** Interpolate {name} path templates with encoded values from the input. */\nfunction interpolatePath(template: string, pathParams: Record<string, unknown>): string {\n return template.replace(/\\{([^}]+)\\}/g, (_match, key: string) => {\n const value = pathParams[key];\n return value === undefined || value === null ? `{${key}}` : encodeURIComponent(String(value));\n });\n}\n\n/** Coerce a record of mixed values into string values, dropping null/undefined. */\nfunction stringifyValues(rec: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(rec)) {\n if (v === undefined || v === null) continue;\n out[k] = String(v);\n }\n return out;\n}\n\n/** Return v if it is a plain object, else an empty record. */\nfunction asRecord(v: unknown): Record<string, unknown> {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : {};\n}\n\n/** Ensure a deterministically unique action key within the connector. */\nfunction uniqueKey(base: string, seen: Set<string>): string {\n let candidate = base;\n if (seen.has(candidate)) {\n let i = 2;\n while (seen.has(`${base}_${i}`)) i++;\n candidate = `${base}_${i}`;\n }\n seen.add(candidate);\n return candidate;\n}\n\n/** Slugify a string into a snake_case machine name (`/^[a-z_][a-z0-9_]*$/`). */\nfunction slug(s: string): string {\n const out = s\n .normalize('NFKD')\n .replace(/[^a-zA-Z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .toLowerCase();\n if (!out) return 'connector';\n return /^[a-z_]/.test(out) ? out : `op_${out}`;\n}\n\n/** Title-case a snake_case key for a default label (`get_pets` → `Get Pets`). */\nfunction titleize(name: string): string {\n return name\n .split('_')\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Connector } from '@objectstack/spec/integration';\nimport { createOpenApiConnector, type OpenApiConnectorConfig } from './openapi-connector.js';\n\n/**\n * Minimal surface of the automation engine this helper depends on — the\n * connector registry from ADR-0018 §Addendum. Kept structural so callers need\n * no runtime dependency on `@objectstack/service-automation` (mirrors\n * connector-rest / connector-mcp).\n */\nexport interface ConnectorRegistrySurface {\n registerConnector(\n def: Connector,\n handlers: Record<\n string,\n (input: Record<string, unknown>, ctx: unknown) => Promise<Record<string, unknown>>\n >,\n ): void;\n unregisterConnector(name: string): void;\n}\n\n/**\n * Generate an OpenAPI-backed connector and register it on the engine's connector\n * registry so the baseline `connector_action` node can dispatch to the generated\n * actions (ADR-0023). Returns the registered connector name.\n */\nexport function registerOpenApiConnector(registry: ConnectorRegistrySurface, config: OpenApiConnectorConfig): string {\n const { def, handlers } = createOpenApiConnector(config);\n registry.registerConnector(def, handlers);\n return def.name;\n}\n"],"mappings":"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","names":["schema"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/openapi-connector.ts","../src/openapi-provider.ts","../src/connector-openapi-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/connector-openapi\n *\n * Generate an ObjectStack {@link Connector} from a declarative OpenAPI 3.x\n * document (ADR-0023). One operation becomes one connector action; a single\n * generic handler drives a self-contained static-auth HTTP transport (mirroring\n * `@objectstack/connector-rest`). The generated connector is an ordinary\n * `type: 'api'` connector — registered via `engine.registerConnector` with no\n * new engine surface.\n *\n * Open-source scope: static auth only (`none` / `api-key` / `basic` / `bearer`),\n * credentials supplied by the caller. Managed OAuth2, credential vaulting, and\n * per-tenant lifecycle are the enterprise tier (ADR-0015 / 0022).\n */\n\nexport {\n createOpenApiConnector,\n type OpenApiConnectorBundle,\n type OpenApiConnectorConfig,\n type OpenApiDocument,\n type OpenApiPathItem,\n type OpenApiOperation,\n type OpenApiParameter,\n type OpenApiRequestBody,\n type OpenApiResponse,\n type OpenApiSecurityScheme,\n type OperationInfo,\n type RestAuth,\n type JsonSchema,\n} from './openapi-connector.js';\nexport {\n registerOpenApiConnector,\n ConnectorOpenApiPlugin,\n type ConnectorOpenApiPluginOptions,\n type ConnectorRegistrySurface,\n} from './connector-openapi-plugin.js';\nexport {\n createOpenApiProviderFactory,\n OPENAPI_PROVIDER_KEY,\n type OpenApiProviderDeps,\n} from './openapi-provider.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Connector } from '@objectstack/spec/integration';\n\n/**\n * OpenAPI connector generator — turns a declarative OpenAPI 3.x document into a\n * {@link Connector} definition + handler map (ADR-0023).\n *\n * Each OpenAPI operation maps to one connector action; a single generic handler\n * (closing over the operation's method + path template) drives one shared HTTP\n * request implementation. That transport mirrors `@objectstack/connector-rest`\n * (build URL from base+path+query, apply static auth, JSON-encode the body,\n * normalise the response to `{ status, ok, body }`) — kept inline so this package\n * stays self-contained, depending only on `@objectstack/core` + `@objectstack/spec`\n * like its sibling connectors. The output is an ordinary `type: 'api'` connector,\n * registered via `engine.registerConnector(def, handlers)` exactly like a\n * hand-written one — the registry, the `connector_action` node, the discovery\n * route, and the Studio palette never know it came from OpenAPI.\n *\n * Open-source scope: **static** auth only (`none` / `api-key` / `basic` /\n * `bearer`), with credentials supplied by the caller. Managed OAuth2, credential\n * vaulting, and per-tenant lifecycle are the enterprise tier (ADR-0015 / 0022).\n */\n\n/** Static auth understood by the generated connector (the open-source subset). */\nexport type RestAuth = Extract<Connector['authentication'], { type: 'none' | 'api-key' | 'basic' | 'bearer' }>;\n\n/** An action on a Connector definition (derived to avoid guessing export names). */\ntype ConnectorAction = NonNullable<Connector['actions']>[number];\n\n/** Handler signature accepted by the connector registry (ADR-0018 §Addendum). */\ntype ConnectorHandler = (input: Record<string, unknown>, ctx: unknown) => Promise<Record<string, unknown>>;\n\n/** A connector definition paired with its action handlers, ready for registerConnector(). */\nexport interface OpenApiConnectorBundle {\n def: Connector;\n handlers: Record<string, ConnectorHandler>;\n}\n\n/** A free-form JSON Schema fragment (matches ConnectorAction input/outputSchema). */\nexport type JsonSchema = Record<string, unknown>;\n\n/** Minimal subset of an OpenAPI 3.x document consumed by the generator.\n * The caller is responsible for loading and de-referencing ($ref) the doc. */\nexport interface OpenApiDocument {\n openapi?: string;\n info?: { title?: string; description?: string; version?: string };\n servers?: { url: string }[];\n paths?: Record<string, OpenApiPathItem>;\n components?: { securitySchemes?: Record<string, OpenApiSecurityScheme> };\n}\n\nexport interface OpenApiPathItem {\n [method: string]: OpenApiOperation | unknown;\n}\n\nexport interface OpenApiOperation {\n operationId?: string;\n summary?: string;\n description?: string;\n tags?: string[];\n parameters?: OpenApiParameter[];\n requestBody?: OpenApiRequestBody;\n responses?: Record<string, OpenApiResponse>;\n}\n\nexport interface OpenApiParameter {\n name: string;\n in: 'path' | 'query' | 'header' | 'cookie';\n required?: boolean;\n description?: string;\n schema?: JsonSchema;\n}\n\nexport interface OpenApiRequestBody {\n required?: boolean;\n description?: string;\n content?: Record<string, { schema?: JsonSchema }>;\n}\n\nexport interface OpenApiResponse {\n description?: string;\n content?: Record<string, { schema?: JsonSchema }>;\n}\n\nexport interface OpenApiSecurityScheme {\n type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';\n name?: string;\n in?: 'header' | 'query' | 'cookie';\n scheme?: string;\n}\n\n/** Flattened view of a single operation, passed to the `include` predicate. */\nexport interface OperationInfo {\n operationId?: string;\n method: string;\n path: string;\n tags?: string[];\n summary?: string;\n description?: string;\n}\n\n/** Configuration for {@link createOpenApiConnector}. */\nexport interface OpenApiConnectorConfig {\n /** Connector machine name (snake_case). Defaults to a slug of info.title. */\n name?: string;\n /** Human-friendly label. Defaults to info.title (then name). */\n label?: string;\n /** Description. Defaults to info.description. */\n description?: string;\n /** Icon identifier for the Studio palette. Defaults to `globe`. */\n icon?: string;\n /** The parsed OpenAPI 3.x document (caller loads/derefs it). */\n document: OpenApiDocument;\n /** Override the base URL (else servers[0].url). */\n baseUrl?: string;\n /** Static auth with credentials. Defaults to `{ type: 'none' }`. */\n auth?: RestAuth;\n /** Headers merged into every request (request-level headers win). */\n defaultHeaders?: Record<string, string>;\n /** Only include operations for which this predicate returns true (allowlist). */\n include?: (op: OperationInfo) => boolean;\n /** Injected fetch implementation (defaults to global `fetch`). */\n fetchImpl?: typeof fetch;\n}\n\n/** OpenAPI HTTP method keys, in a deterministic order. */\nconst HTTP_METHODS = ['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace'] as const;\n\n/** Input passed to the shared request transport. */\ninterface RequestInput {\n method: string;\n path: string;\n headers?: Record<string, string>;\n query?: Record<string, string>;\n body?: unknown;\n}\n\n/**\n * Build an OpenAPI connector definition and its handler map.\n *\n * @returns the `Connector` definition (`def`) and a `handlers` record keyed by\n * action key, suitable for `engine.registerConnector(def, handlers)`.\n */\nexport function createOpenApiConnector(config: OpenApiConnectorConfig): OpenApiConnectorBundle {\n const { document, include } = config;\n const auth: RestAuth = config.auth ?? { type: 'none' };\n const doFetch = config.fetchImpl ?? fetch;\n const name = config.name ?? slug(document.info?.title ?? 'openapi_connector');\n const label = config.label ?? document.info?.title ?? titleize(name);\n const description = config.description ?? document.info?.description;\n const baseUrl = config.baseUrl ?? document.servers?.[0]?.url;\n if (!baseUrl) {\n throw new Error('createOpenApiConnector: no base URL — provide config.baseUrl or document.servers[0].url');\n }\n\n // One shared transport (mirrors connector-rest) reused by every action handler.\n async function request(input: RequestInput): Promise<Record<string, unknown>> {\n const method = input.method.toUpperCase();\n const headers: Record<string, string> = { ...config.defaultHeaders, ...input.headers };\n const query: Record<string, string> = { ...input.query };\n applyAuth(auth, headers, query);\n\n const url = buildUrl(baseUrl as string, input.path, query);\n const hasBody = input.body !== undefined && method !== 'GET' && method !== 'HEAD';\n if (hasBody && headers['Content-Type'] === undefined && headers['content-type'] === undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const response = await doFetch(url, {\n method,\n headers,\n body: hasBody ? JSON.stringify(input.body) : undefined,\n });\n\n const contentType = response.headers.get('content-type') ?? '';\n const parsed = contentType.includes('application/json') ? await response.json() : await response.text();\n return { status: response.status, ok: response.ok, body: parsed };\n }\n\n const actions: ConnectorAction[] = [];\n const handlers: Record<string, ConnectorHandler> = {};\n const seenKeys = new Set<string>();\n\n for (const op of collectOperations(document)) {\n if (include && !include(toInfo(op))) continue;\n const key = uniqueKey(op.operationId ?? slug(`${op.method}_${op.path}`), seenKeys);\n\n actions.push({\n key,\n label: op.summary ?? titleize(key),\n description: op.description,\n inputSchema: buildInputSchema(op),\n outputSchema: buildOutputSchema(op),\n });\n\n handlers[key] = async (input: Record<string, unknown>) => {\n const req = input as { path?: unknown; query?: unknown; header?: unknown; body?: unknown };\n return request({\n method: op.method,\n path: interpolatePath(op.path, asRecord(req.path)),\n query: stringifyValues(asRecord(req.query)),\n headers: stringifyValues(asRecord(req.header)),\n body: req.body,\n });\n };\n }\n\n const def: Connector = {\n name,\n label,\n type: 'api',\n description,\n icon: config.icon ?? 'globe',\n authentication: auth,\n // Defaulted by ConnectorSchema; set explicitly so the literal satisfies\n // the (post-parse) Connector output type (mirrors connector-rest/mcp).\n status: 'active',\n enabled: true,\n connectionTimeoutMs: 30000,\n requestTimeoutMs: 30000,\n actions,\n };\n\n return { def, handlers };\n}\n\ninterface Op extends OpenApiOperation {\n method: string;\n path: string;\n}\n\n/** Flatten paths × methods into a deterministic list of operations. */\nfunction collectOperations(doc: OpenApiDocument): Op[] {\n const ops: Op[] = [];\n for (const [path, item] of Object.entries(doc.paths ?? {})) {\n if (!item || typeof item !== 'object') continue;\n const record = item as Record<string, unknown>;\n for (const method of HTTP_METHODS) {\n const operation = record[method] as OpenApiOperation | undefined;\n if (!operation || typeof operation !== 'object') continue;\n ops.push({ ...operation, method, path });\n }\n }\n return ops;\n}\n\nfunction toInfo(op: Op): OperationInfo {\n return {\n operationId: op.operationId,\n method: op.method,\n path: op.path,\n tags: op.tags,\n summary: op.summary,\n description: op.description,\n };\n}\n\n/**\n * Assemble the action inputSchema from an operation's parameters + requestBody.\n * Produces { type: 'object', properties: { path, query, header, body }, required }\n * where only non-empty sections are emitted.\n */\nfunction buildInputSchema(op: OpenApiOperation): JsonSchema | undefined {\n const sections: Record<'path' | 'query' | 'header', { props: Record<string, JsonSchema>; required: string[] }> = {\n path: { props: {}, required: [] },\n query: { props: {}, required: [] },\n header: { props: {}, required: [] },\n };\n\n for (const p of op.parameters ?? []) {\n if (!p || typeof p !== 'object' || '$ref' in p) continue;\n if (p.in !== 'path' && p.in !== 'query' && p.in !== 'header') continue;\n const sec = sections[p.in];\n sec.props[p.name] = p.schema ?? (p.description ? { type: 'string', description: p.description } : { type: 'string' });\n if (p.required) sec.required.push(p.name);\n }\n\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n for (const where of ['path', 'query', 'header'] as const) {\n const sec = sections[where];\n if (Object.keys(sec.props).length === 0) continue;\n const schema: JsonSchema = { type: 'object', properties: sec.props };\n if (sec.required.length) schema.required = sec.required;\n properties[where] = schema;\n // Path params are always required when present; others only if any are.\n if (where === 'path' || sec.required.length) required.push(where);\n }\n\n const bodySchema = extractRequestBodySchema(op.requestBody);\n if (bodySchema) {\n properties.body = bodySchema;\n if (op.requestBody && !('$ref' in op.requestBody) && op.requestBody.required) required.push('body');\n }\n\n if (Object.keys(properties).length === 0) return undefined;\n const schema: JsonSchema = { type: 'object', properties };\n if (required.length) schema.required = required;\n return schema;\n}\n\n/** Pick the success response's JSON schema (200 → first 2xx → default). */\nfunction buildOutputSchema(op: OpenApiOperation): JsonSchema | undefined {\n const responses = op.responses;\n if (!responses) return undefined;\n let code: string | undefined;\n if (responses['200']) code = '200';\n else code = Object.keys(responses).find((c) => /^2\\d\\d$/.test(c));\n if (!code && responses['default']) code = 'default';\n if (!code) return undefined;\n const resp = responses[code];\n if (!resp || typeof resp !== 'object' || '$ref' in resp) return undefined;\n return pickJsonSchema(resp.content);\n}\n\n/** Extract the requestBody JSON schema (prefers application/json). */\nfunction extractRequestBodySchema(rb: OpenApiRequestBody | undefined): JsonSchema | undefined {\n if (!rb || typeof rb !== 'object' || '$ref' in rb) return undefined;\n return pickJsonSchema(rb.content);\n}\n\n/** Choose the application/json schema, falling back to the first content type. */\nfunction pickJsonSchema(content: Record<string, { schema?: JsonSchema }> | undefined): JsonSchema | undefined {\n if (!content) return undefined;\n const chosen = content['application/json'] ?? Object.values(content)[0];\n return chosen?.schema;\n}\n\n/** Build the request URL from base + path + query, encoding query params. */\nfunction buildUrl(baseUrl: string, path: string, query: Record<string, string>): string {\n const base = baseUrl.replace(/\\/+$/, '');\n const suffix = path ? (path.startsWith('/') ? path : `/${path}`) : '';\n const url = new URL(base + suffix);\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined && value !== null) url.searchParams.set(key, String(value));\n }\n return url.toString();\n}\n\n/** Apply static auth to the outgoing headers / query (mirrors connector-rest). */\nfunction applyAuth(auth: RestAuth, headers: Record<string, string>, query: Record<string, string>): void {\n switch (auth.type) {\n case 'none':\n return;\n case 'bearer':\n headers['Authorization'] = `Bearer ${auth.token}`;\n return;\n case 'basic': {\n const encoded = Buffer.from(`${auth.username}:${auth.password}`).toString('base64');\n headers['Authorization'] = `Basic ${encoded}`;\n return;\n }\n case 'api-key':\n if (auth.paramName) query[auth.paramName] = auth.key;\n else headers[auth.headerName ?? 'X-API-Key'] = auth.key;\n return;\n }\n}\n\n/** Interpolate {name} path templates with encoded values from the input. */\nfunction interpolatePath(template: string, pathParams: Record<string, unknown>): string {\n return template.replace(/\\{([^}]+)\\}/g, (_match, key: string) => {\n const value = pathParams[key];\n return value === undefined || value === null ? `{${key}}` : encodeURIComponent(String(value));\n });\n}\n\n/** Coerce a record of mixed values into string values, dropping null/undefined. */\nfunction stringifyValues(rec: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(rec)) {\n if (v === undefined || v === null) continue;\n out[k] = String(v);\n }\n return out;\n}\n\n/** Return v if it is a plain object, else an empty record. */\nfunction asRecord(v: unknown): Record<string, unknown> {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : {};\n}\n\n/** Ensure a deterministically unique action key within the connector. */\nfunction uniqueKey(base: string, seen: Set<string>): string {\n let candidate = base;\n if (seen.has(candidate)) {\n let i = 2;\n while (seen.has(`${base}_${i}`)) i++;\n candidate = `${base}_${i}`;\n }\n seen.add(candidate);\n return candidate;\n}\n\n/** Slugify a string into a snake_case machine name (`/^[a-z_][a-z0-9_]*$/`). */\nfunction slug(s: string): string {\n const out = s\n .normalize('NFKD')\n .replace(/[^a-zA-Z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .toLowerCase();\n if (!out) return 'connector';\n return /^[a-z_]/.test(out) ? out : `op_${out}`;\n}\n\n/** Title-case a snake_case key for a default label (`get_pets` → `Get Pets`). */\nfunction titleize(name: string): string {\n return name\n .split('_')\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n ConnectorProviderContext,\n ConnectorProviderFactory,\n ResolvedConnectorAuth,\n} from '@objectstack/spec/integration';\nimport {\n createOpenApiConnector,\n type OpenApiDocument,\n type RestAuth,\n} from './openapi-connector.js';\n\n/**\n * The provider key this package contributes (ADR-0097). A declarative\n * `connectors:` entry with `provider: 'openapi'` is materialized by this factory.\n */\nexport const OPENAPI_PROVIDER_KEY = 'openapi';\n\n/** Injectable dependencies for {@link createOpenApiProviderFactory} (tests). */\nexport interface OpenApiProviderDeps {\n /** Injected fetch implementation (spec fetch + request transport); defaults to global `fetch`. */\n fetchImpl?: typeof fetch;\n}\n\n/** Shape of `providerConfig` for a `provider: 'openapi'` declarative instance. */\ninterface OpenApiProviderConfig {\n /**\n * The OpenAPI 3.x document: an inline object, an http(s) URL to fetch at\n * boot, or a file path resolved relative to the declaring stack/package root\n * (`'./billing-openapi.json'`, #3016).\n */\n spec?: unknown;\n /** Override the base URL (else the document's `servers[0].url`). */\n baseUrl?: unknown;\n}\n\n/**\n * Resolve `providerConfig.spec` into a parsed OpenAPI document (ADR-0097;\n * union per #3016): an inline document object (the reliable, no-I/O-at-boot\n * form used by the showcase), an http(s) URL fetched at materialization, or a\n * **file path** read through the host's `ctx.loadPackageFile` — which resolves\n * it relative to the declaring stack/package root and confines the read to\n * that root (absolute / `..`-escaping paths are rejected there). Every failure\n * throws, so the materializer's reconcile policy applies: fatal at boot, the\n * entry is skipped on reload.\n */\nasync function loadOpenApiDocument(\n spec: unknown,\n fetchImpl: typeof fetch | undefined,\n ctx: ConnectorProviderContext,\n): Promise<OpenApiDocument> {\n const connectorName = ctx.name;\n if (spec && typeof spec === 'object' && !Array.isArray(spec)) {\n return spec as OpenApiDocument;\n }\n if (typeof spec === 'string' && spec.length > 0) {\n if (/^https?:\\/\\//i.test(spec)) {\n const doFetch = fetchImpl ?? fetch;\n const res = await doFetch(spec);\n if (!res.ok) {\n throw new Error(\n `connector-openapi provider: connector '${connectorName}' failed to fetch spec '${spec}' (HTTP ${res.status}).`,\n );\n }\n return (await res.json()) as OpenApiDocument;\n }\n // File path — dereferenced through the host capability so resolution stays\n // anchored to (and confined within) the declaring stack/package root.\n if (!ctx.loadPackageFile) {\n throw new Error(\n `connector-openapi provider: connector '${connectorName}' providerConfig.spec '${spec}' is a file path, ` +\n `but this host provides no package file access — inline the OpenAPI document or use an http(s) URL.`,\n );\n }\n let text: string;\n try {\n text = await ctx.loadPackageFile(spec);\n } catch (err) {\n throw new Error(\n `connector-openapi provider: connector '${connectorName}' failed to read providerConfig.spec '${spec}': ` +\n `${(err as Error).message}`,\n );\n }\n try {\n const parsed: unknown = JSON.parse(text);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('not a JSON object');\n }\n return parsed as OpenApiDocument;\n } catch (err) {\n throw new Error(\n `connector-openapi provider: connector '${connectorName}' providerConfig.spec '${spec}' is not a parseable ` +\n `OpenAPI JSON document: ${(err as Error).message}`,\n );\n }\n }\n throw new Error(\n `connector-openapi provider: connector '${connectorName}' requires providerConfig.spec — an inline OpenAPI 3.x ` +\n `document object, an http(s) URL, or a package-relative file path.`,\n );\n}\n\n/**\n * Build the `openapi` {@link ConnectorProviderFactory} (ADR-0097 / ADR-0023). At\n * boot the automation service invokes it for each `provider: 'openapi'`\n * declarative instance: it loads the OpenAPI document from `providerConfig.spec`,\n * then produces the same `{ def, handlers }` bundle {@link createOpenApiConnector}\n * generates for a hand-wired OpenAPI connector — one action per operation over a\n * static-auth HTTP transport, with the resolved `auth` applied.\n *\n * Hard-fails on invalid config (missing/unfetchable spec, no base URL), so a\n * misconfigured instance fails boot loudly.\n */\nexport function createOpenApiProviderFactory(deps: OpenApiProviderDeps = {}): ConnectorProviderFactory {\n return async (ctx) => {\n const cfg = (ctx.providerConfig ?? {}) as OpenApiProviderConfig;\n if (cfg.baseUrl !== undefined && typeof cfg.baseUrl !== 'string') {\n throw new Error(\n `connector-openapi provider: connector '${ctx.name}' providerConfig.baseUrl must be a string when set.`,\n );\n }\n const document = await loadOpenApiDocument(cfg.spec, deps.fetchImpl, ctx);\n const auth = ctx.auth as ResolvedConnectorAuth | undefined as RestAuth | undefined;\n return createOpenApiConnector({\n name: ctx.name,\n label: ctx.label,\n description: ctx.description,\n document,\n baseUrl: typeof cfg.baseUrl === 'string' ? cfg.baseUrl : undefined,\n auth,\n fetchImpl: deps.fetchImpl,\n });\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { Connector, ConnectorProviderFactory } from '@objectstack/spec/integration';\nimport { createOpenApiConnector, type OpenApiConnectorConfig } from './openapi-connector.js';\nimport { createOpenApiProviderFactory, OPENAPI_PROVIDER_KEY } from './openapi-provider.js';\n\n/**\n * Minimal surface of the automation engine this package depends on — the\n * connector registry (ADR-0018 §Addendum) plus the provider registry (ADR-0097).\n * Kept structural so callers need no runtime dependency on\n * `@objectstack/service-automation` (mirrors connector-rest / connector-mcp).\n */\nexport interface ConnectorRegistrySurface {\n registerConnector(\n def: Connector,\n handlers: Record<\n string,\n (input: Record<string, unknown>, ctx: unknown) => Promise<Record<string, unknown>>\n >,\n ): void;\n unregisterConnector(name: string): void;\n registerConnectorProvider(providerKey: string, factory: ConnectorProviderFactory): void;\n}\n\n/**\n * Generate an OpenAPI-backed connector and register it on the engine's connector\n * registry so the baseline `connector_action` node can dispatch to the generated\n * actions (ADR-0023). Returns the registered connector name.\n */\nexport function registerOpenApiConnector(registry: ConnectorRegistrySurface, config: OpenApiConnectorConfig): string {\n const { def, handlers } = createOpenApiConnector(config);\n registry.registerConnector(def, handlers);\n return def.name;\n}\n\n/**\n * Options for {@link ConnectorOpenApiPlugin}. All optional (ADR-0097): with no\n * `document` the plugin contributes only the `openapi` provider factory — so a\n * stack can declare `provider: 'openapi'` instances as pure metadata. Supply a\n * `document` (+ config) to ALSO register one hand-wired OpenAPI connector.\n */\nexport interface ConnectorOpenApiPluginOptions extends Partial<OpenApiConnectorConfig> {\n /** Injected fetch implementation forwarded to the `openapi` provider factory (tests). */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * ConnectorOpenApiPlugin — contributes the OpenAPI generic executor (ADR-0023) in\n * two forms:\n *\n * 1. **Provider factory** (`openapi`, ADR-0097): registered at `init()` so the\n * automation service can materialize declarative `provider: 'openapi'`\n * `connectors:` entries — loading the OpenAPI document and mapping each\n * operation to a connector action — at boot.\n * 2. **Hand-wired instance** (optional, back-compat): when constructed with a\n * `document`, it also registers one concrete OpenAPI connector at `start()`.\n *\n * If no automation engine is present the plugin logs and skips.\n */\nexport class ConnectorOpenApiPlugin implements Plugin {\n name = 'com.objectstack.connector.openapi';\n version = '1.0.0';\n type = 'standard' as const;\n // Ensure the automation engine (and its connector/provider registries) exist first.\n dependencies = ['com.objectstack.service-automation'];\n\n private readonly options: ConnectorOpenApiPluginOptions;\n private connectorName?: string;\n private automation?: ConnectorRegistrySurface;\n\n constructor(options: ConnectorOpenApiPluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n // Contribute the `openapi` provider factory (ADR-0097) before the\n // automation service materializes declarative instances during its start().\n const automation = this.tryGetAutomation(ctx);\n if (automation && typeof automation.registerConnectorProvider === 'function') {\n automation.registerConnectorProvider(\n OPENAPI_PROVIDER_KEY,\n createOpenApiProviderFactory({ fetchImpl: this.options.fetchImpl }),\n );\n ctx.logger.info(\"ConnectorOpenApiPlugin: registered 'openapi' connector provider\");\n }\n }\n\n async start(ctx: PluginContext): Promise<void> {\n // Provider-only usage (no document) contributes just the factory in init().\n if (!this.options.document) return;\n\n const automation = this.tryGetAutomation(ctx);\n if (!automation || typeof automation.registerConnector !== 'function') {\n ctx.logger.info('ConnectorOpenApiPlugin: no automation engine — OpenAPI connector not registered');\n return;\n }\n\n this.connectorName = registerOpenApiConnector(automation, this.options as OpenApiConnectorConfig);\n this.automation = automation;\n ctx.logger.info(`ConnectorOpenApiPlugin: OpenAPI connector '${this.connectorName}' registered`);\n }\n\n async stop(_ctx: PluginContext): Promise<void> {\n if (this.automation && this.connectorName) {\n try { this.automation.unregisterConnector(this.connectorName); } catch { /* ignore */ }\n }\n }\n\n private tryGetAutomation(ctx: PluginContext): ConnectorRegistrySurface | undefined {\n try {\n return ctx.getService<ConnectorRegistrySurface>('automation');\n } catch {\n return undefined;\n }\n }\n}\n"],"mappings":"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","names":["schema"]}