@stackbone/sdk 0.1.0-alpha.1 → 0.1.0-alpha.3
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/CHANGELOG.md +317 -0
- package/README.md +40 -105
- package/db/testing/index.cjs +12 -43
- package/db/testing/index.cjs.map +1 -1
- package/db/testing/index.d.cts +12 -21
- package/db/testing/index.d.ts +12 -21
- package/db/testing/index.js +12 -43
- package/db/testing/index.js.map +1 -1
- package/index.cjs +3849 -19245
- package/index.cjs.map +1 -1
- package/index.d.cts +1539 -894
- package/index.d.ts +1539 -894
- package/index.js +3837 -19238
- package/index.js.map +1 -1
- package/observability/index.cjs +596 -0
- package/observability/index.cjs.map +1 -0
- package/observability/index.d.cts +175 -0
- package/observability/index.d.ts +175 -0
- package/observability/index.js +587 -0
- package/observability/index.js.map +1 -0
- package/package.json +14 -2
- package/rag/migrations/index.cjs +1 -1
- package/rag/migrations/index.cjs.map +1 -1
- package/rag/migrations/index.js +1 -1
- package/rag/migrations/index.js.map +1 -1
- package/rag/schema.cjs +1 -1
- package/rag/schema.cjs.map +1 -1
- package/rag/schema.js +1 -1
- package/rag/schema.js.map +1 -1
- package/stackbone-sdk-0.1.0-alpha.3.tgz +0 -0
- package/stackbone-sdk-0.1.0-alpha.1.tgz +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,323 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- `ResolvedConfig` is now an opaque, typed snapshot — the live `env`
|
|
13
|
+
channel is gone. Every consumer used to read environment variables with
|
|
14
|
+
the pattern `resolved.config.fieldX ?? resolved.env['FIELDX']`, which
|
|
15
|
+
spread two pieces of knowledge across ~20 call sites: the exact env-var
|
|
16
|
+
name and the precedence rule. The resolver now owns both. After this
|
|
17
|
+
change, modules read typed fields like `resolved.stackboneApiUrl`,
|
|
18
|
+
`resolved.s3Bucket`, `resolved.databaseUrl`, `resolved.requireContract`,
|
|
19
|
+
etc. — none of them touch `process.env` directly.
|
|
20
|
+
|
|
21
|
+
The resolver is the **single** seam that reads `process.env` in the
|
|
22
|
+
library. New env-driven settings go in two places: a typed field on
|
|
23
|
+
`ResolvedConfig` and the matching `config.xxx ?? env['XXX']` (or parsing
|
|
24
|
+
logic) inside `resolveConfig`. The compiler then flags every consumer
|
|
25
|
+
that needs the new field.
|
|
26
|
+
|
|
27
|
+
Runtime knobs previously read inline from `contract/contract-handshake.ts`
|
|
28
|
+
(`STACKBONE_REQUIRE_CONTRACT`, `STACKBONE_CONTRACT_TTL_MS`,
|
|
29
|
+
`STACKBONE_DEBUG`) are now resolved up-front and threaded into the per-
|
|
30
|
+
client `ContractStore`. The database singleton in
|
|
31
|
+
`surfaces/agent-local/database/shared-handle.ts` takes an explicit
|
|
32
|
+
`connectionString` parameter that `DatabaseModule` pulls off
|
|
33
|
+
`resolved.databaseUrl`; the only remaining read of
|
|
34
|
+
`STACKBONE_POSTGRES_URL` is inside `resolveConfig`.
|
|
35
|
+
|
|
36
|
+
Booleans (`STACKBONE_DEBUG=1`) and numbers (`STACKBONE_CONTRACT_TTL_MS=30000`)
|
|
37
|
+
get parsed at resolve time, so consumers receive `boolean` / `number`,
|
|
38
|
+
not raw strings — one parse, one place, no drift.
|
|
39
|
+
|
|
40
|
+
Behavioural change worth flagging: `ResolvedConfig` is a snapshot, not
|
|
41
|
+
a live view. Tests that mutated `process.env` AFTER `createClient()` to
|
|
42
|
+
exercise late-binding behaviour now need to rebuild the client (or pass
|
|
43
|
+
the new value through the config object). The new `src/config.spec.ts`
|
|
44
|
+
pins both the snapshot semantics and the deletion contract — `.env` is
|
|
45
|
+
no longer a property on `ResolvedConfig`, and reading it is both a type
|
|
46
|
+
error and a runtime `undefined`. Two integration / handshake tests
|
|
47
|
+
(`STACKBONE_REQUIRE_CONTRACT=0`, `STACKBONE_CONTRACT_TTL_MS`,
|
|
48
|
+
`STACKBONE_DEBUG=1`) now construct a fresh `ContractStore` per env-var
|
|
49
|
+
variation to keep the resolve-once contract explicit.
|
|
50
|
+
|
|
51
|
+
- `SdkError.code` is now a typed catalog (`SdkErrorCode`) declared in one
|
|
52
|
+
place — `src/errors/codes.ts`. Every literal `code` value that ships on a
|
|
53
|
+
`Result<T>` envelope (and the `errorMapping.prefix` slot on the transport)
|
|
54
|
+
is now compiler-checked against the catalog, so a future PR that adds
|
|
55
|
+
`secrets_not_foud` (typo) or `ai_provdier_down` (typo) fails to build
|
|
56
|
+
instead of silently shipping past the README contract. The catalog also
|
|
57
|
+
ties `HttpClient`'s `<prefix>_<reason>` projection to `SdkErrorPrefix`, so
|
|
58
|
+
a surface that wires `errorMapping: { prefix: '<unknown>' }` no longer
|
|
59
|
+
compiles. The wire-level shape of every code is unchanged: this is a
|
|
60
|
+
type-level constraint, not a rename.
|
|
61
|
+
|
|
62
|
+
Adding a new code is a single-file edit in `src/errors/codes.ts` (either
|
|
63
|
+
a new suffix under an existing prefix, or a freestanding code under
|
|
64
|
+
`SDK_ERROR_CODES_STANDALONE`). The compiler then flags every call site
|
|
65
|
+
that needs the new literal — and the new exhaustiveness test in
|
|
66
|
+
`src/lib/http-client.spec.ts` asserts every `<prefix>_<reason>` the
|
|
67
|
+
transport can emit is still a member of the catalog so README/code drift
|
|
68
|
+
fails loudly.
|
|
69
|
+
|
|
70
|
+
Two new symbols on the public barrel (`@stackbone/sdk`):
|
|
71
|
+
- `SdkErrorCode` — the union projection of the catalog. Use it as the
|
|
72
|
+
return type when you write a helper that produces an `SdkError`, or
|
|
73
|
+
as a discriminant in a `switch` over `result.error.code`.
|
|
74
|
+
- `isSdkErrorCode(raw: string)` — runtime narrowing for wire strings
|
|
75
|
+
that come from a decoded body. Returns `true` only when `raw` is a
|
|
76
|
+
catalog member; lets the SDK widen upstream errors safely.
|
|
77
|
+
|
|
78
|
+
RAG's `toRagError(cause, message, fallbackCode)` helper now constrains
|
|
79
|
+
`fallbackCode` to `SdkErrorCode` and checks `cause.code` against the
|
|
80
|
+
catalog before reusing it. The catalog and the helper are not folded
|
|
81
|
+
into one file — the catalog is the inventory, `toRagError` stays in the
|
|
82
|
+
RAG folder as a Postgres-tagged-error classifier.
|
|
83
|
+
|
|
84
|
+
- Contract gating membership is now an explicit rule, and the handshake
|
|
85
|
+
cache no longer lives in a process-wide static. Two things changed:
|
|
86
|
+
1. **Membership rule (Option B from the audit)** — a surface gates iff
|
|
87
|
+
`MODULE_CAPABILITIES` (in `@stackbone/validators`) advertises a row
|
|
88
|
+
for its module id. `MODULE_CAPABILITIES` is now the single source of
|
|
89
|
+
truth: adding a surface to gating means adding a `Capability` and a
|
|
90
|
+
`<module>: '<capability>'` row first, then wiring
|
|
91
|
+
`createModuleGate('<module>', resolved, store)` in the surface
|
|
92
|
+
constructor. The new docstring at the top of
|
|
93
|
+
`contract/capability-registry.ts` spells out the rule and the steps.
|
|
94
|
+
Today's gated set stays the same (`database`, `rag`, `queues`,
|
|
95
|
+
`events`, `secrets`, `config`, `approval`, `storage`, `ai`). The
|
|
96
|
+
four ungated surfaces (`memory`, `connections`, `prompts`,
|
|
97
|
+
`observability`) now carry a one-paragraph comment on their class
|
|
98
|
+
header documenting **why** they are not gated — either the runtime
|
|
99
|
+
does not yet exist with a defined capability (`memory`,
|
|
100
|
+
`connections`, `prompts`) or the surface stays local / talks to
|
|
101
|
+
OpenTelemetry, not the Stackbone Agent Protocol (`observability`).
|
|
102
|
+
|
|
103
|
+
2. **Per-client handshake store** — the `ContractHandshake` static
|
|
104
|
+
namespace is gone. In its place: `createContractStore()` returns a
|
|
105
|
+
`ContractStore` with three methods (`get`, `peek`, `gatingEnabled`)
|
|
106
|
+
that owns its own cache, single-flight map, TTL eviction and debug
|
|
107
|
+
bookkeeping. `StackboneClient` instantiates exactly one store per
|
|
108
|
+
instance and threads it through every gated surface via
|
|
109
|
+
`createModuleGate(module, resolved, store)`. Two clients
|
|
110
|
+
constructed in the same process no longer share handshake cache or
|
|
111
|
+
suppression-warning state — including `client.contract`, which now
|
|
112
|
+
reads from the per-client store's `peek()`.
|
|
113
|
+
|
|
114
|
+
Removed test globals (the visible sign that the cache had escaped its
|
|
115
|
+
owner): `ContractHandshake.__resetForTests` and
|
|
116
|
+
`__resetModuleGateWarningsForTests`. Specs that previously called them
|
|
117
|
+
in `beforeEach`/`afterEach` now simply construct a fresh
|
|
118
|
+
`createContractStore()` (or, for client-level coverage, a fresh
|
|
119
|
+
`createClient(...)`); state isolation is intrinsic to construction.
|
|
120
|
+
`module-gate.spec.ts` gained two coverage tests for the new shape:
|
|
121
|
+
"two gates pinned to the same store share suppression-warning state"
|
|
122
|
+
and "two gates pinned to different stores warn independently". The E2E
|
|
123
|
+
suite (`integration/handshake.spec.ts`) loses its global-reset
|
|
124
|
+
bookkeeping entirely.
|
|
125
|
+
|
|
126
|
+
Wire protocol is unchanged: `GET /api/contract` returns the same JSON
|
|
127
|
+
shape, `client.contract` exposes the same read-only `ContractResponse |
|
|
128
|
+
null` getter, gate-blocked calls still return the same `Result`
|
|
129
|
+
envelope with `capability_unavailable` / `contract_version_unsupported`
|
|
130
|
+
/ `contract_unreachable` codes, and the `STACKBONE_REQUIRE_CONTRACT=0`
|
|
131
|
+
/ `STACKBONE_CONTRACT_TTL_MS` / `STACKBONE_DEBUG=1` escape hatches
|
|
132
|
+
behave identically. Surface constructors keep the same `gate?:
|
|
133
|
+
ModuleGate` test seam — only the internal default (now backed by a
|
|
134
|
+
per-client store instead of a static singleton) changed.
|
|
135
|
+
|
|
136
|
+
- The cross-surface shared-handle seam is now an explicit, documented
|
|
137
|
+
pattern on the public surfaces. Two surfaces produce a reusable
|
|
138
|
+
resource and expose it through a canonical accessor:
|
|
139
|
+
- `client.database.shared(): DrizzleClient` — process-wide Drizzle
|
|
140
|
+
handle bound to `STACKBONE_POSTGRES_URL`. Same input → same
|
|
141
|
+
instance. `client.database.raw()` stays as the creator-facing
|
|
142
|
+
escape hatch and is now documented as an alias of `shared()`.
|
|
143
|
+
- `client.ai.shared(): Result<OpenAI>` — process-wide OpenRouter
|
|
144
|
+
`OpenAI` client used by every namespace under `client.ai`. Same
|
|
145
|
+
input → same instance; reflects the `clientOverride` injected at
|
|
146
|
+
construction time. The previous private `client()` accessor folded
|
|
147
|
+
into this public name.
|
|
148
|
+
|
|
149
|
+
These accessors are the **only** way cross-surface SDK consumers
|
|
150
|
+
should reach the shared pool / OpenAI client. RAG already consumed
|
|
151
|
+
the database pool via a module-private file (`database/internal.ts`)
|
|
152
|
+
— RAG now reaches it through `client.database.shared().$client`
|
|
153
|
+
instead. The renamed `database/shared-handle.ts` keeps the singleton
|
|
154
|
+
implementation but has zero sibling-relative importers outside its
|
|
155
|
+
own folder and its own spec; cross-surface friction (memory and
|
|
156
|
+
queues will need the same handles per the consolidation ADRs) now
|
|
157
|
+
has one named extension point instead of a per-consumer escape
|
|
158
|
+
hatch.
|
|
159
|
+
|
|
160
|
+
Public API of `client.database`, `client.ai`, and `client.rag` is
|
|
161
|
+
unchanged — method signatures and `Result` envelopes match the
|
|
162
|
+
previous release. `RagModule` constructor adds a
|
|
163
|
+
`() => DatabaseModule` accessor alongside the existing
|
|
164
|
+
`() => AiModule`, mirroring the wiring `client.ts` produces; tests
|
|
165
|
+
pass both verbatim.
|
|
166
|
+
|
|
167
|
+
- **BREAKING** — Control-plane facades (`client.secrets`, `client.config`,
|
|
168
|
+
`client.approval`) now surface HTTP failures with surface-specific error
|
|
169
|
+
codes instead of the generic `http_*` family. The transport (`HttpClient`)
|
|
170
|
+
absorbs response validation, status→domain remapping, and querystring
|
|
171
|
+
serialisation, so a facade method now reads as "POST to /path with body X,
|
|
172
|
+
validate against schema Y, error prefix Z" instead of a 30–80 line
|
|
173
|
+
hand-rolled pipeline per surface. Migration is mechanical: pattern-match
|
|
174
|
+
the surface prefix instead of `http_*`.
|
|
175
|
+
- 401 → `<prefix>_unauthorized` (was `http_unauthorized`).
|
|
176
|
+
- 403 → `<prefix>_forbidden` (was `http_forbidden`).
|
|
177
|
+
- 404 → `<prefix>_not_found` (was `http_not_found`).
|
|
178
|
+
- 408 → `<prefix>_timeout` (was `http_timeout`).
|
|
179
|
+
- 429 → `<prefix>_rate_limited` (was `http_rate_limited`).
|
|
180
|
+
- 5xx → `<prefix>_unavailable` (was `http_server_error`).
|
|
181
|
+
- other 4xx → `<prefix>_invalid_request` (was `http_client_error`).
|
|
182
|
+
- schema-drift on a 2xx body → `<prefix>_invalid_response`.
|
|
183
|
+
|
|
184
|
+
The raw HTTP status stays accessible at `result.error.meta.status` for
|
|
185
|
+
callers that still want to pattern-match on the wire. Endpoint-specific
|
|
186
|
+
overrides (e.g. a POST whose `409` means "already exists") are wired by
|
|
187
|
+
facades via the new `errorOverrides` option on the transport request.
|
|
188
|
+
|
|
189
|
+
Transport-level errors (`http_timeout`, `http_aborted`,
|
|
190
|
+
`http_network_error`, `http_parse_error`, `http_request_failed`,
|
|
191
|
+
`http_invalid_response`, `http_invalid_request`) keep the `http_*`
|
|
192
|
+
prefix the README documents — they are not surface-specific.
|
|
193
|
+
|
|
194
|
+
Internal cleanup: `src/lib/facade-helpers.ts` is gone (the two utilities
|
|
195
|
+
it held — `remapHttpNotFound` and `validateStringArray` — were either
|
|
196
|
+
absorbed by the transport or relocated to `lib/http-client.ts` as a
|
|
197
|
+
named export). The per-method bodies on the control-plane facades
|
|
198
|
+
shrank substantially: `secrets.get` and `config.get` lost their
|
|
199
|
+
hand-rolled `value` type-checks and remap blocks, batch endpoints
|
|
200
|
+
pushed array validation + comma-join down to the transport, and the
|
|
201
|
+
approval list endpoint dropped its `pickDefinedAsStrings` helper for
|
|
202
|
+
the transport's built-in scalar coercion. File-level line counts barely
|
|
203
|
+
move because the public type definitions dominate each facade (notably
|
|
204
|
+
on `approval.ts` where 80+ lines are interface exports), but the
|
|
205
|
+
per-method logic now reads like the design target: "POST to /path,
|
|
206
|
+
validate against schema, error prefix is X".
|
|
207
|
+
|
|
208
|
+
- **BREAKING** — Platform-side observability primitives moved off the main
|
|
209
|
+
`@stackbone/sdk` barrel and into a dedicated subpath entrypoint,
|
|
210
|
+
`@stackbone/sdk/observability`. The main barrel previously exposed both the
|
|
211
|
+
handler-scoped `createStructuredLogger` (the per-invocation `Logger` the
|
|
212
|
+
runtime injects into creator handlers) and the platform/run-scoped
|
|
213
|
+
`createPlatformLogger` factory side-by-side, with no signal about which
|
|
214
|
+
belonged to which audience. Creators only ever consume the run-scoped
|
|
215
|
+
surface through `client.observability.*` (the live accessors stay on
|
|
216
|
+
`StackboneClient` unchanged); only platform/runtime tooling and integration
|
|
217
|
+
tests need to construct the primitives directly. The symbols that moved:
|
|
218
|
+
- Per-run logger — `createPlatformLogger`, `LOG_LEVELS`, `defaultRunsDir`,
|
|
219
|
+
plus the types `LogLevelName`, `LogLevelNumber`, `LogRecord`, `PinoLike`,
|
|
220
|
+
`PlatformLogger`, `PlatformLoggerMode`, `PlatformLoggerOptions`.
|
|
221
|
+
- OTel span processor — `RunStepsSpanProcessor`, `RUN_STEP_TYPES`,
|
|
222
|
+
`RUN_ID_ATTRIBUTE`, `STEP_TYPE_ATTRIBUTE`, plus the types
|
|
223
|
+
`ReadableSpanLike`, `RunStepType`, `RunStepsSpanProcessorOptions`,
|
|
224
|
+
`SpanContextLike`, `PostgresLike`.
|
|
225
|
+
- Run-cost aggregator — `aggregateRunCost`, plus the types
|
|
226
|
+
`AggregateRunCostOptions`, `AggregateRunCostResult`.
|
|
227
|
+
|
|
228
|
+
Migration is mechanical:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// Before
|
|
232
|
+
import { createPlatformLogger, RunStepsSpanProcessor } from '@stackbone/sdk';
|
|
233
|
+
// After
|
|
234
|
+
import { createPlatformLogger, RunStepsSpanProcessor } from '@stackbone/sdk/observability';
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The handler-scoped `createStructuredLogger`, `Logger`, `LoggerBindings`,
|
|
238
|
+
`LogSink` and `StructuredLoggerOptions` stay on the main `@stackbone/sdk`
|
|
239
|
+
barrel: those are the surface the runtime wrapper hands to creator
|
|
240
|
+
handlers via `InvokeContext.logger`. JSONL output shape, OTLP/HTTP/JSON
|
|
241
|
+
serialisation and `~/.stackbone/dev/runs/<runId>.jsonl` paths are
|
|
242
|
+
unchanged — only the import path moved.
|
|
243
|
+
|
|
244
|
+
- **BREAKING** — SDK source tree reorganised under `src/surfaces/` by **where
|
|
245
|
+
each surface's state lives** instead of "wraps SDK vs makes HTTP fetch". The
|
|
246
|
+
old `src/modules/` and `src/facades/` directories are gone. New layout:
|
|
247
|
+
- `src/surfaces/agent-local/` — state in the agent's own Postgres
|
|
248
|
+
(`database`, `rag`).
|
|
249
|
+
- `src/surfaces/external/` — managed partner SDKs the SDK wraps directly
|
|
250
|
+
(`ai`, `storage`, `observability`).
|
|
251
|
+
- `src/surfaces/control-plane/` — thin HTTP calls to the Stackbone control
|
|
252
|
+
plane (`approval`, `secrets`, `config`).
|
|
253
|
+
- `src/surfaces/pending/` — surface types are exported but the runtime is
|
|
254
|
+
not built yet (`queues`, `memory`, `prompts`, `connections`, `events`).
|
|
255
|
+
|
|
256
|
+
Subpath entrypoints (`@stackbone/sdk/db`, `@stackbone/sdk/db/testing`,
|
|
257
|
+
`@stackbone/sdk/queues/types`, `@stackbone/sdk/rag/migrations`,
|
|
258
|
+
`@stackbone/sdk/rag/schema`) keep working — only the underlying file
|
|
259
|
+
paths moved.
|
|
260
|
+
|
|
261
|
+
- The five pending surfaces (`queues`, `memory`, `prompts`, `connections`,
|
|
262
|
+
`events`) now live under `src/surfaces/pending/` and are exposed as live
|
|
263
|
+
`client.X` accessors that return `not_implemented` on every method. The
|
|
264
|
+
earlier draft of this refactor dropped the accessors entirely, but MVP
|
|
265
|
+
agent code needs to be authored against the eventual contract today and
|
|
266
|
+
switch over the day the backing runtime ships — keeping the getters as
|
|
267
|
+
placeholders makes that one-line change instead of a refactor. Public
|
|
268
|
+
types continue to ship from `@stackbone/sdk` (`PublishRequest`,
|
|
269
|
+
`MemoryItem`, `MemoryHit`, `Prompt`, `CreatePromptRequest`, …) so
|
|
270
|
+
creators can keep modelling integrations. `queues` and `events` go
|
|
271
|
+
through the contract gate (they are listed in `MODULE_CAPABILITIES`);
|
|
272
|
+
`memory`, `prompts` and `connections` are intentionally ungated.
|
|
273
|
+
|
|
274
|
+
- **BREAKING** — `@stackbone/sdk/db/testing` `createTestDatabase` now spins up
|
|
275
|
+
a real Postgres container via `@testcontainers/postgresql` instead of
|
|
276
|
+
carving a random schema out of a shared dev DB. Callers no longer pass a
|
|
277
|
+
`connectionString`; pass an optional `image` (defaults to
|
|
278
|
+
`pgvector/pgvector:pg17`) if you need extensions beyond pgvector. Tests are
|
|
279
|
+
self-contained: no `docker compose up` ceremony, no `STACKBONE_*_POSTGRES_URL`
|
|
280
|
+
env, no schema bookkeeping. Aligns the SDK helper with the rest of the
|
|
281
|
+
monorepo (see `apps/api/src/db/__tests__/setup-postgres.ts`). Docker must
|
|
282
|
+
be running on the host.
|
|
283
|
+
|
|
284
|
+
## [0.1.0-alpha.2] - 2026-05-18
|
|
285
|
+
|
|
286
|
+
### Fixed
|
|
287
|
+
|
|
288
|
+
- `client.rag.ingest` (batched mode, e.g. when an `onProgress` callback or a
|
|
289
|
+
`jobWriter` is wired): every chunk batch was wrapping its INSERT in a
|
|
290
|
+
transaction that first ran `DELETE FROM rag_chunks WHERE namespace=? AND
|
|
291
|
+
doc_id=?`. With N batches per document, only the rows inserted by the LAST
|
|
292
|
+
batch survived — every earlier slice was wiped by the next batch's DELETE.
|
|
293
|
+
Split the DELETE into a single `clearDocument` call before the loop;
|
|
294
|
+
`ingestDocument` now only INSERTs its slice.
|
|
295
|
+
- `client.rag` error mapping: `42P01 relation does not exist` was only
|
|
296
|
+
translated to `SdkError('rag_schema_missing')` from the pipeline path.
|
|
297
|
+
`reset()`, the database-handle resolver, and `client.rag.ingestAsync`'s job
|
|
298
|
+
writer used a divergent local copy of `toRagError` that surfaced the raw
|
|
299
|
+
Postgres code instead. Consolidated all three call sites onto a shared
|
|
300
|
+
`modules/rag/errors.ts` so the schema-missing hint is consistent.
|
|
301
|
+
- `RunStepsSpanProcessor.stepIdBySpanId`: spans without a `stackbone.run.id`
|
|
302
|
+
attribute leaked their reservation in the map — `buildRow` returned early
|
|
303
|
+
before the cleanup line ran. Cleanup now happens unconditionally on every
|
|
304
|
+
`onEnd`, so dropped spans no longer accumulate.
|
|
305
|
+
|
|
306
|
+
### Changed
|
|
307
|
+
|
|
308
|
+
- OTLP log records sent by `createPlatformLogger` now carry the actual
|
|
309
|
+
installed SDK version under `scope.version`. Previously every record was
|
|
310
|
+
tagged with a hardcoded `'0.0.1'` regardless of what was published.
|
|
311
|
+
- `client.rag.reset()` invalidates the cached schema fingerprint, so the
|
|
312
|
+
first ingest after a reset re-provisions the schema without serving a
|
|
313
|
+
stale `rag_dim_mismatch`.
|
|
314
|
+
- `client.rag.ingest`: `ensureSchema` now memoises the verified dimensions
|
|
315
|
+
per `Sql` instance, dropping the per-call `_rag_meta` SELECT on the hot
|
|
316
|
+
path. The `stackbone_rag_jobs` writer also collapses its
|
|
317
|
+
`ensureCollection` lookup to a single `INSERT … ON CONFLICT … RETURNING`
|
|
318
|
+
(was a `SELECT` followed by an upsert) and trims `isCancelled` to
|
|
319
|
+
`SELECT status … LIMIT 1`.
|
|
320
|
+
- `OtlpLogsBody` serialisation no longer iterates `Object.entries(LOG_LEVELS)`
|
|
321
|
+
per record to map level numbers to severity text — precomputed lookup
|
|
322
|
+
table replaces the linear scan.
|
|
323
|
+
- `RagModule` test seams (`sqlProvider`, `jobWriterFactory`) now flow
|
|
324
|
+
through a typed 4th constructor parameter (`RagModuleTestDeps`) instead of
|
|
325
|
+
private fields mutated via `as unknown as` casts.
|
|
326
|
+
|
|
10
327
|
## [0.1.0-alpha.1] - 2026-05-15
|
|
11
328
|
|
|
12
329
|
### BREAKING
|
package/README.md
CHANGED
|
@@ -13,15 +13,12 @@ Official TypeScript SDK for [Stackbone](https://stackbone.ai) — the marketplac
|
|
|
13
13
|
- **Database** — Direct Postgres access through a lazy Drizzle ORM wrapper bound to the agent's connection string, with full type-safe queries, transactions, and `sql` template literals — Drizzle is re-exported via `@stackbone/sdk/db` so the agent's `package.json` only depends on `@stackbone/sdk`
|
|
14
14
|
- **Storage** — S3-compatible object storage (Cloudflare R2 in prod, MinIO in dev) with automatic per-agent key prefixing and signed URLs
|
|
15
15
|
- **AI** — Wrapper around the official `openai` SDK pointed at OpenRouter, so 300+ chat, embedding and image models are reachable through a single OpenAI-compatible API
|
|
16
|
-
- **Queues** _(coming soon)_ — Cross-container HTTP push via QStash, with typed job payloads through module augmentation
|
|
17
16
|
- **RAG** — Document parsing, chunking and `pgvector`-backed retrieval through a flat, ceremony-free API
|
|
18
|
-
- **
|
|
19
|
-
- **Prompts** _(coming soon)_ — Managed prompts stored in the Stackbone control plane: fetch by name with optional version pinning, compile Mustache `{{var}}` templates against a variables map, and full CRUD so prompts can evolve without rebuilding the container
|
|
20
|
-
- **Observability** _(coming soon)_ — OpenTelemetry traces and logs flushed through the platform collector
|
|
17
|
+
- **Observability** — OpenTelemetry span processor + run-cost aggregator + per-run logger
|
|
21
18
|
- **Approval** — Human-in-the-loop inbox: fire-and-forget approval requests, HMAC-signed decision callbacks, and an LLM tool wrapper that gates execution behind human review
|
|
22
|
-
- **Secrets** — Read
|
|
19
|
+
- **Secrets** — Read organization-encrypted secrets registered in the dashboard, no client-side cache so rotations propagate immediately
|
|
23
20
|
- **Config** — Typed reads of dynamic per-agent configuration the user set in the dashboard
|
|
24
|
-
- **
|
|
21
|
+
- **Coming soon (types only)** — `queues` (cross-container HTTP push via QStash), `memory` (mem0-backed long-term memory), `prompts` (managed prompts with `{{var}}` templates), `connections` (OAuth integrations) and `events` (org-wide event bus). Public types are exported from `@stackbone/sdk` today; the runtime is not wired yet, so there is no live `client.X` accessor.
|
|
25
22
|
- **TypeScript-first** — Full type definitions and a uniform `Result<T>` envelope on every method (no thrown errors at the SDK boundary)
|
|
26
23
|
- **Lazy initialization** — Modules and partner SDKs are constructed on first access, so env vars rotated by the control plane after `createClient()` are still picked up
|
|
27
24
|
|
|
@@ -327,7 +324,7 @@ await stackbone.rag.reset();
|
|
|
327
324
|
|
|
328
325
|
`client.approval` exposes a **fire-and-forget** primitive: the agent issues an approval request, the call returns immediately with the `approvalId` and a `callbackUrl`, and the control plane POSTs the human's decision to the endpoint the agent registered in `onDecide`. The callback is HMAC-signed (`Stackbone-Signature: t=<unix>,v1=<hex>`) so the agent can authenticate it without storing any per-approval secret.
|
|
329
326
|
|
|
330
|
-
This shape decouples waiting from the container lifecycle — the agent does not have to keep a long-poll connection alive while a human reviews, which means it survives
|
|
327
|
+
This shape decouples waiting from the container lifecycle — the agent does not have to keep a long-poll connection alive while a human reviews, which means it survives idle scale-to-zero and arbitrary numbers of concurrent approvals without tying up sockets.
|
|
331
328
|
|
|
332
329
|
```ts
|
|
333
330
|
// 1. Issue an approval — returns immediately, the human reviews asynchronously.
|
|
@@ -432,7 +429,7 @@ The signing key for `verify()` is read from `STACKBONE_APPROVAL_SIGNING_KEY` (or
|
|
|
432
429
|
|
|
433
430
|
### Secrets
|
|
434
431
|
|
|
435
|
-
`client.secrets` reads
|
|
432
|
+
`client.secrets` reads organization-encrypted secrets the user registered in the dashboard. The SDK is **read-only** by design — secrets are managed in the control plane, never written from inside an agent container. There is no client-side cache: every `get()` is a network call so dashboard rotations propagate immediately.
|
|
436
433
|
|
|
437
434
|
```ts
|
|
438
435
|
// Single secret — `secrets_not_found` error if the name is not registered.
|
|
@@ -468,97 +465,28 @@ const { data: cfg } = await stackbone.config.getMany<{
|
|
|
468
465
|
}>(['reply_tone', 'max_emails']);
|
|
469
466
|
```
|
|
470
467
|
|
|
471
|
-
`secrets.get` returns `secrets_not_found` (with the name in `meta`) when the secret is missing; `config.get` returns `config_not_found` symmetrically. Both `getMany` calls return whatever subset the
|
|
468
|
+
`secrets.get` returns `secrets_not_found` (with the name in `meta`) when the secret is missing; `config.get` returns `config_not_found` symmetrically. Both `getMany` calls return whatever subset the organization has registered — no error for missing entries.
|
|
472
469
|
|
|
473
|
-
### Memory
|
|
470
|
+
### Memory (pending)
|
|
474
471
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
Memories are scoped along three axes:
|
|
478
|
-
|
|
479
|
-
- `'user'` — long-term, persists across sessions of a given end user. Default.
|
|
480
|
-
- `'session'` — short-lived; collapsed (or dropped) when `endSession()` runs.
|
|
481
|
-
- `'agent'` — shared across every user of the agent (preferences, global knowledge).
|
|
482
|
-
|
|
483
|
-
```ts
|
|
484
|
-
// Ingest a fact (raw text or an OpenAI-shaped conversation).
|
|
485
|
-
const { data: memory } = await stackbone.memory.add(
|
|
486
|
-
'The user prefers replies in Spanish and addresses them by their last name.',
|
|
487
|
-
{ userId: 'user_42', scope: 'user', metadata: { source: 'onboarding' } },
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
// Or ingest a whole conversation — the backend summarises it into facts.
|
|
491
|
-
await stackbone.memory.add(
|
|
492
|
-
[
|
|
493
|
-
{ role: 'user', content: 'Call me Mr. Torres please.' },
|
|
494
|
-
{ role: 'assistant', content: 'Got it, Mr. Torres.' },
|
|
495
|
-
],
|
|
496
|
-
{ userId: 'user_42', sessionId: 'sess_abc', scope: 'session' },
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
// Semantic search across the user's memories.
|
|
500
|
-
const { data: hits } = await stackbone.memory.search('how should I address this user?', {
|
|
501
|
-
userId: 'user_42',
|
|
502
|
-
limit: 5,
|
|
503
|
-
threshold: 0.7,
|
|
504
|
-
includeScopes: ['user', 'agent'],
|
|
505
|
-
});
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
Management — read, paginate, edit and audit a single fact:
|
|
509
|
-
|
|
510
|
-
```ts
|
|
511
|
-
const { data: memory } = await stackbone.memory.get(memoryId);
|
|
512
|
-
|
|
513
|
-
const { data: page } = await stackbone.memory.list({
|
|
514
|
-
userId: 'user_42',
|
|
515
|
-
limit: 50,
|
|
516
|
-
cursor: previousCursor,
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
await stackbone.memory.update(memoryId, {
|
|
520
|
-
content: 'The user prefers replies in Spanish.',
|
|
521
|
-
metadata: { reviewed: true },
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
const { data: events } = await stackbone.memory.history(memoryId);
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
Deletion — single fact, all data for a user (GDPR), or end-of-session collapse:
|
|
528
|
-
|
|
529
|
-
```ts
|
|
530
|
-
// Forget one fact.
|
|
531
|
-
await stackbone.memory.delete(memoryId);
|
|
532
|
-
|
|
533
|
-
// GDPR: forget everything we have on this user.
|
|
534
|
-
await stackbone.memory.deleteAll({ userId: 'user_42' });
|
|
535
|
-
|
|
536
|
-
// Close a session and consolidate its facts into long-term memory (default).
|
|
537
|
-
const { data } = await stackbone.memory.endSession('sess_abc', { persist: true });
|
|
538
|
-
// data: { sessionId, persisted }
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
> Until the mem0 integration ships, every method returns `{ data: null, error: { code: 'not_implemented', ... } }`. The shape of the responses described here is the contract callers can already write code against.
|
|
472
|
+
Long-term memory is a pending surface: its public types (`AddMemoryRequest`, `MemoryItem`, `MemoryHit`, `MemoryScope`, …) are exported from `@stackbone/sdk` so creators can type their integrations today, but there is no live `client.memory` accessor yet. The mem0-backed runtime will be added in a future release and promoted into the Features list above. See the [Coming Soon](#coming-soon) section for the full pending set.
|
|
542
473
|
|
|
543
474
|
### Coming Soon
|
|
544
475
|
|
|
545
|
-
The following
|
|
476
|
+
The following surfaces have stable public **types** (so creators can write code against them today) but no runtime yet. They live under `src/surfaces/pending/` in the SDK source and do NOT appear as `client.X` accessors — importing the types directly from `@stackbone/sdk` is the supported path while the runtime is in flight:
|
|
546
477
|
|
|
547
478
|
```ts
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
// Emit an event to the workspace event bus.
|
|
556
|
-
await stackbone.events.emit('lead.qualified', { leadId: 42 });
|
|
557
|
-
|
|
558
|
-
// List OAuth connections the user attached (Notion, GDrive, Slack…).
|
|
559
|
-
const { data: connections } = await stackbone.connections.list();
|
|
479
|
+
import type {
|
|
480
|
+
PublishRequest,
|
|
481
|
+
AddMemoryRequest,
|
|
482
|
+
MemoryHit,
|
|
483
|
+
Prompt,
|
|
484
|
+
CreatePromptRequest,
|
|
485
|
+
} from '@stackbone/sdk';
|
|
560
486
|
```
|
|
561
487
|
|
|
488
|
+
The pending surfaces are `queues` (cross-container HTTP push), `memory` (mem0-backed long-term memory), `prompts` (managed prompts with `{{var}}` templates), `connections` (OAuth integrations) and `events` (organisation event bus). When their runtime lands they will be promoted into the live `client.X` set described in the Features section above.
|
|
489
|
+
|
|
562
490
|
## Configuration
|
|
563
491
|
|
|
564
492
|
`createClient` accepts a single configuration object. Every field is optional and falls back to an environment variable, which the platform injects into the agent container at boot:
|
|
@@ -571,8 +499,8 @@ const stackbone = createClient({
|
|
|
571
499
|
stackboneApiUrl: 'https://api.stackbone.ai',
|
|
572
500
|
// Stable agent identifier used as storage key prefix → STACKBONE_AGENT_ID
|
|
573
501
|
agentId: 'agent_abc123',
|
|
574
|
-
// Postgres connection string for `client.rag`
|
|
575
|
-
//
|
|
502
|
+
// Postgres connection string for `client.database`, `client.rag` and the
|
|
503
|
+
// observability exporter → STACKBONE_POSTGRES_URL
|
|
576
504
|
databaseUrl: 'postgresql://...',
|
|
577
505
|
// OpenRouter credentials → OPENROUTER_API_KEY / OPENROUTER_BASE_URL
|
|
578
506
|
openrouterKey: '...',
|
|
@@ -586,12 +514,13 @@ const stackbone = createClient({
|
|
|
586
514
|
mem0BaseUrl: 'https://api.mem0.ai',
|
|
587
515
|
// HMAC key for verifying approval-decision callbacks → STACKBONE_APPROVAL_SIGNING_KEY
|
|
588
516
|
approvalSigningKey: '...',
|
|
589
|
-
// Object storage credentials →
|
|
517
|
+
// Object storage credentials → STACKBONE_S3_ACCESS_KEY / STACKBONE_S3_SECRET_KEY / STACKBONE_S3_ENDPOINT / STACKBONE_S3_BUCKET / STACKBONE_S3_REGION
|
|
590
518
|
s3: {
|
|
591
519
|
accessKeyId: '...',
|
|
592
520
|
secretAccessKey: '...',
|
|
593
521
|
endpoint: 'https://<account>.r2.cloudflarestorage.com',
|
|
594
522
|
bucket: 'stackbone-prod',
|
|
523
|
+
region: 'auto',
|
|
595
524
|
},
|
|
596
525
|
// OpenTelemetry exporter
|
|
597
526
|
otel: {
|
|
@@ -633,18 +562,24 @@ console.log(result.data.choices[0]?.message.content);
|
|
|
633
562
|
|
|
634
563
|
Each module ships its own stable code prefix so you can pattern-match without parsing the message:
|
|
635
564
|
|
|
636
|
-
| Prefix | Source | Examples
|
|
637
|
-
| ----------------- | ---------------------- |
|
|
638
|
-
| `ai_*` | `client.ai` | `ai_unauthorized`, `ai_credits_exhausted`, `ai_rate_limited`, `ai_aborted`, `ai_no_image_generated`
|
|
639
|
-
| `s3_*` | `client.storage` | `s3_credentials_missing`, `s3_invalid_key`, `s3_error`
|
|
640
|
-
| `rag_*` | `client.rag` | `rag_invalid_request`, `rag_dim_mismatch`, `rag_embedding_failed`, `rag_error`
|
|
641
|
-
| `approval_*` | `client.approval` | `approval_invalid_request`, `approval_invalid_signature`, `approval_signature_expired`, `approval_signing_key_missing`, `approval_invalid_payload`, `approval_tool_execute_failed` |
|
|
642
|
-
| `secrets_*` | `client.secrets` | `secrets_invalid_request`, `secrets_not_found`, `secrets_invalid_response`
|
|
643
|
-
| `config_*` | `client.config` | `config_invalid_request`, `config_not_found`, `config_invalid_response`
|
|
644
|
-
| `memory_*` | `client.memory` | reserved for the future mem0-backed implementation; today every method returns `not_implemented`
|
|
645
|
-
| `http_*` |
|
|
646
|
-
|
|
|
647
|
-
| `
|
|
565
|
+
| Prefix | Source | Examples |
|
|
566
|
+
| ----------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
567
|
+
| `ai_*` | `client.ai` | `ai_unauthorized`, `ai_credits_exhausted`, `ai_rate_limited`, `ai_aborted`, `ai_no_image_generated` |
|
|
568
|
+
| `s3_*` | `client.storage` | `s3_credentials_missing`, `s3_invalid_key`, `s3_error` |
|
|
569
|
+
| `rag_*` | `client.rag` | `rag_invalid_request`, `rag_dim_mismatch`, `rag_embedding_failed`, `rag_error` |
|
|
570
|
+
| `approval_*` | `client.approval` | `approval_invalid_request`, `approval_invalid_signature`, `approval_signature_expired`, `approval_signing_key_missing`, `approval_invalid_payload`, `approval_tool_execute_failed`, `approval_unauthorized`, `approval_forbidden`, `approval_not_found`, `approval_rate_limited`, `approval_unavailable` |
|
|
571
|
+
| `secrets_*` | `client.secrets` | `secrets_invalid_request`, `secrets_not_found`, `secrets_invalid_response`, `secrets_unauthorized`, `secrets_forbidden`, `secrets_rate_limited`, `secrets_unavailable` |
|
|
572
|
+
| `config_*` | `client.config` | `config_invalid_request`, `config_not_found`, `config_invalid_response`, `config_unauthorized`, `config_forbidden`, `config_rate_limited`, `config_unavailable` |
|
|
573
|
+
| `memory_*` | `client.memory` | reserved for the future mem0-backed implementation; today every method returns `not_implemented` |
|
|
574
|
+
| `http_*` | transport-level errors | `http_timeout`, `http_aborted`, `http_network_error`, `http_parse_error`, `http_request_failed` — surface-level statuses (401/403/404/429/5xx) remap to `<surface>_*` codes |
|
|
575
|
+
| `database_*` | `client.database` | `database_not_configured` (raised when `STACKBONE_POSTGRES_URL` is unset) |
|
|
576
|
+
| `observability_*` | `client.observability` | `observability_close_run_failed` |
|
|
577
|
+
| `contract_*` | gated surfaces | `contract_malformed`, `contract_unreachable`, `contract_version_unsupported` — emitted by any gated call when the contract handshake fails |
|
|
578
|
+
| `capability_*` | gated surfaces | `capability_unavailable` — emitted when the negotiated contract advertises no support for the surface |
|
|
579
|
+
| `*_missing` | configuration | `agent_id_missing`, `openrouter_key_missing`, `database_url_missing`, `stackbone_api_url_missing` |
|
|
580
|
+
| `not_implemented` | stubbed module surface | returned by every "coming soon" method until it ships |
|
|
581
|
+
|
|
582
|
+
> The canonical inventory of every code lives in `src/errors/codes.ts` as a typed catalog (`SdkErrorCode`). The table above is the human-readable summary; adding or removing a code is a single-file edit there and the compiler refuses any literal `code` value not declared in the catalog. Pattern-match against `SdkErrorCode` (exported from `@stackbone/sdk`) for full type narrowing, or call `isSdkErrorCode(raw)` to widen a wire string back into the catalog.
|
|
648
583
|
|
|
649
584
|
## TypeScript Support
|
|
650
585
|
|
package/db/testing/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var postgresql = require('@testcontainers/postgresql');
|
|
4
4
|
var postgresJs = require('drizzle-orm/postgres-js');
|
|
5
5
|
var migrator = require('drizzle-orm/postgres-js/migrator');
|
|
6
6
|
var postgres = require('postgres');
|
|
@@ -11,46 +11,14 @@ var postgres__default = /*#__PURE__*/_interopDefault(postgres);
|
|
|
11
11
|
|
|
12
12
|
var __defProp = Object.defineProperty;
|
|
13
13
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
14
|
-
var
|
|
15
|
-
var generateSchemaName = /* @__PURE__ */ __name(() => `stackbone_test_${crypto.randomBytes(8).toString("hex")}`, "generateSchemaName");
|
|
16
|
-
var tagConnectionStringWithSchema = /* @__PURE__ */ __name((url, schemaName) => {
|
|
17
|
-
const u = new URL(url);
|
|
18
|
-
u.searchParams.set("schema", schemaName);
|
|
19
|
-
return u.toString();
|
|
20
|
-
}, "tagConnectionStringWithSchema");
|
|
21
|
-
var dropSchema = /* @__PURE__ */ __name(async (adminUrl, schemaName) => {
|
|
22
|
-
const admin = postgres__default.default(adminUrl, {
|
|
23
|
-
max: 1,
|
|
24
|
-
onnotice: /* @__PURE__ */ __name(() => void 0, "onnotice")
|
|
25
|
-
});
|
|
26
|
-
try {
|
|
27
|
-
await admin.unsafe(`DROP SCHEMA "${schemaName.replace(/"/g, '""')}" CASCADE`);
|
|
28
|
-
} finally {
|
|
29
|
-
await admin.end({
|
|
30
|
-
timeout: 5
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}, "dropSchema");
|
|
14
|
+
var DEFAULT_IMAGE = "pgvector/pgvector:pg17";
|
|
34
15
|
var createTestDatabase = /* @__PURE__ */ __name(async (options) => {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
onnotice: /* @__PURE__ */ __name(() => void 0, "onnotice")
|
|
40
|
-
});
|
|
41
|
-
try {
|
|
42
|
-
await admin`CREATE SCHEMA ${admin(schemaName)}`;
|
|
43
|
-
} finally {
|
|
44
|
-
await admin.end({
|
|
45
|
-
timeout: 5
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
const client = postgres__default.default(adminUrl, {
|
|
16
|
+
const image = options.image ?? DEFAULT_IMAGE;
|
|
17
|
+
const container = await new postgresql.PostgreSqlContainer(image).withDatabase("stackbone_test").withUsername("stackbone").withPassword("stackbone").start();
|
|
18
|
+
const connectionString = container.getConnectionUri();
|
|
19
|
+
const client = postgres__default.default(connectionString, {
|
|
49
20
|
max: 5,
|
|
50
|
-
onnotice: /* @__PURE__ */ __name(() => void 0, "onnotice")
|
|
51
|
-
connection: {
|
|
52
|
-
search_path: schemaName
|
|
53
|
-
}
|
|
21
|
+
onnotice: /* @__PURE__ */ __name(() => void 0, "onnotice")
|
|
54
22
|
});
|
|
55
23
|
const db = postgresJs.drizzle(client);
|
|
56
24
|
let disposed = false;
|
|
@@ -60,19 +28,20 @@ var createTestDatabase = /* @__PURE__ */ __name(async (options) => {
|
|
|
60
28
|
await client.end({
|
|
61
29
|
timeout: 5
|
|
62
30
|
}).catch(() => void 0);
|
|
63
|
-
await
|
|
31
|
+
await container.stop({
|
|
32
|
+
timeout: 5e3
|
|
33
|
+
}).catch(() => void 0);
|
|
64
34
|
}, "dispose");
|
|
65
35
|
try {
|
|
66
36
|
await migrator.migrate(db, {
|
|
67
|
-
migrationsFolder: options.migrationsDir
|
|
68
|
-
migrationsSchema: schemaName
|
|
37
|
+
migrationsFolder: options.migrationsDir
|
|
69
38
|
});
|
|
70
39
|
} catch (err) {
|
|
71
40
|
await dispose();
|
|
72
41
|
throw err;
|
|
73
42
|
}
|
|
74
43
|
return {
|
|
75
|
-
connectionString
|
|
44
|
+
connectionString,
|
|
76
45
|
drizzle: db,
|
|
77
46
|
dispose
|
|
78
47
|
};
|