@zapier/kitcore 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +60 -16
- package/dist/index.cjs +925 -227
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +728 -252
- package/dist/index.d.ts +728 -252
- package/dist/index.mjs +915 -227
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
# @zapier/kitcore
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c398f14: A caller-provided parameter is now used exactly as given: `controller.resolve()` and `controller.start()` settle seeded input up front, so the resolution walk no longer descends into provided objects or arrays and only asks for parameters the caller left out.
|
|
8
|
+
|
|
9
|
+
## 0.5.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 5f5f028: The module model is complete; the legacy function-plugin bridge is deprecated.
|
|
14
|
+
|
|
15
|
+
New:
|
|
16
|
+
- `createSdk(root, { configuration })` injects immutable values by plugin id
|
|
17
|
+
at build time (your SDK's options, framework options). A `declareProperty`
|
|
18
|
+
or `declareOptionalProperty` (new) stand-in with a matching id receives the
|
|
19
|
+
value as a normal import.
|
|
20
|
+
- The `kitcore/coreOptions` configuration entry now accepts a
|
|
21
|
+
`logDeprecation` handler, called on every invocation of a deprecated
|
|
22
|
+
method. The default logs a warning once per process.
|
|
23
|
+
- `defineHook`, a new plugin kind that attaches behavior to methods:
|
|
24
|
+
`observe` runs `onMethodStart` / `onMethodEnd` observers over every method
|
|
25
|
+
call, and `wrap` is targeted, contract-preserving middleware over a direct
|
|
26
|
+
import (a `{ imports, input, state, next }` bag).
|
|
27
|
+
- Every plugin can declare `dispose` beside `setup`. `disposeSdk(sdk,
|
|
28
|
+
input?)` runs them in reverse build order — call it to release what your
|
|
29
|
+
plugins acquired (timers, listeners). Idempotent; failures aggregate into
|
|
30
|
+
`CoreDisposeError`.
|
|
31
|
+
- `resolvePlugin(sdk, ref)` reads a plugin binding off a built sdk, for
|
|
32
|
+
infrastructure code that can't declare `imports`.
|
|
33
|
+
- `PluginSurface<typeof plugin>` derives the surface a plugin contributes
|
|
34
|
+
(method callable, property value, or an aggregate's bindings), so you don't
|
|
35
|
+
hand-write provides types. The `PluginSummary` / `LeafSummary` ledger types
|
|
36
|
+
are exported alongside it, so a package that exports plugins can emit their
|
|
37
|
+
inferred descriptor types — completeness ledger included — in its
|
|
38
|
+
declarations.
|
|
39
|
+
- Raw-output methods now get the full method boundary — input validation
|
|
40
|
+
branded through your `adaptError`, lifecycle hooks — while staying
|
|
41
|
+
synchronous.
|
|
42
|
+
- Object parameters resolve more naturally in interactive mode: an optional
|
|
43
|
+
object sits behind one entry question (declining skips its field fetch),
|
|
44
|
+
required fields come first, and the remaining optionals are offered as a
|
|
45
|
+
batch. A nested field's `requireParameters` can now name top-level
|
|
46
|
+
parameters. `collection` questions carry `container: "array" | "object"`;
|
|
47
|
+
hosts that render `message` + `actions` generically need no changes.
|
|
48
|
+
|
|
49
|
+
Breaking:
|
|
50
|
+
- `definePlugin`'s `middleware` map is removed. Declare a `defineHook` with
|
|
51
|
+
`wrap` instead.
|
|
52
|
+
- `listItems` is required on dynamic resolvers: a dynamic resolver IS a
|
|
53
|
+
candidate-lister. For a free-text field, use `type: "static"`.
|
|
54
|
+
- On `collection` questions, `count` and `min` are now optional (present on
|
|
55
|
+
array decisions only, absent on the new object gates). A host reading
|
|
56
|
+
`question.count` unconditionally must handle `undefined`.
|
|
57
|
+
|
|
58
|
+
Deprecated (runtime warnings now, removal in a later release):
|
|
59
|
+
`createPluginStack`, `createPluginMethod`, `createPaginatedPluginMethod`,
|
|
60
|
+
`createCorePlugin`, `fromFunctionPlugin`, `defineLegacyMerge`, and the
|
|
61
|
+
function form of `definePlugin`. Author with `defineMethod` /
|
|
62
|
+
`defineProperty` / `definePlugin({ ... })` and build with `createSdk`.
|
|
63
|
+
|
|
64
|
+
Fixed: an sdk built by one bundled copy of kitcore is now readable by another
|
|
65
|
+
copy's `getContext` / `resolvePlugin` (the context key moved to the global
|
|
66
|
+
symbol registry).
|
|
67
|
+
|
|
3
68
|
## 0.4.0
|
|
4
69
|
|
|
5
70
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -40,10 +40,11 @@ sdk.greet({ name: "Ada" }); // "Hi Ada"
|
|
|
40
40
|
|
|
41
41
|
## Plugins are modules
|
|
42
42
|
|
|
43
|
-
There are
|
|
43
|
+
There are four kinds of plugin, and an SDK is just a materialized plugin:
|
|
44
44
|
|
|
45
45
|
- **Method** (`defineMethod`) — a leaf that _is_ a function.
|
|
46
46
|
- **Property** (`defineProperty`) — a leaf that _is_ a value.
|
|
47
|
+
- **Hook** (`defineHook`) — a leaf contributing cross-cutting behavior (see [Hooks](#hooks-wrap-and-observe)).
|
|
47
48
|
- **Aggregate** (`definePlugin`) — re-exports other plugins under binding names.
|
|
48
49
|
|
|
49
50
|
A plugin declares `imports` (an array of the plugins it depends on) and receives them as a flat `imports` bag, with each import bound under its own name. The two ends share the word the way ES modules do: `import { x } from "y"` is the declaration, `x` is the binding.
|
|
@@ -211,15 +212,19 @@ const fetchMethod = defineMethod({
|
|
|
211
212
|
sdk.fetch("https://example.com", { method: "GET" });
|
|
212
213
|
```
|
|
213
214
|
|
|
214
|
-
##
|
|
215
|
+
## Hooks: wrap and observe
|
|
215
216
|
|
|
216
|
-
|
|
217
|
+
Cross-cutting behavior lives in a **hook** (`defineHook`), a fourth kind of leaf with two faces.
|
|
218
|
+
|
|
219
|
+
`wrap` is targeted middleware. An entry is keyed by the import binding it wraps and receives `{ imports, next, input }`. It must preserve the target's contract (enforced by the types), so it cannot change the public signature.
|
|
217
220
|
|
|
218
221
|
```ts
|
|
219
|
-
|
|
222
|
+
import { defineHook } from "@zapier/kitcore";
|
|
223
|
+
|
|
224
|
+
const auth = defineHook({
|
|
220
225
|
name: "auth",
|
|
221
226
|
imports: [transport, credentials],
|
|
222
|
-
|
|
227
|
+
wrap: {
|
|
223
228
|
transport: ({ imports, next, input }) =>
|
|
224
229
|
next({
|
|
225
230
|
...input,
|
|
@@ -228,34 +233,55 @@ const auth = definePlugin({
|
|
|
228
233
|
},
|
|
229
234
|
});
|
|
230
235
|
|
|
231
|
-
const retry =
|
|
236
|
+
const retry = defineHook({
|
|
232
237
|
name: "retry",
|
|
233
238
|
imports: [transport, auth], // import auth so retry nests around it
|
|
234
|
-
|
|
239
|
+
wrap: { transport: ({ next, input }) => withRetry(() => next(input)) },
|
|
235
240
|
});
|
|
236
241
|
```
|
|
237
242
|
|
|
238
243
|
A method that imports `transport` knows nothing about `auth` or `retry`; it just calls `imports.transport(...)` and the chain runs automatically. Nesting follows the dependency edges (`retry` imports `auth`, so it wraps around it), not registration order, and `next(input)` runs the next layer.
|
|
239
244
|
|
|
245
|
+
`observe` is fire-and-forget: `onMethodStart` / `onMethodEnd` observers that the method boundary fires around every SDK method (`input` carries the lifecycle context: `methodName`, `args`, `durationMs`, `error`, ...). Observers run defensively, so a throwing observer never breaks the observed call, and an observer that itself calls SDK methods does not re-trigger itself. `setup` gives the hook private state, delivered to each observer.
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const telemetry = defineHook({
|
|
249
|
+
name: "telemetry",
|
|
250
|
+
setup: () => ({ events: [] as string[] }),
|
|
251
|
+
observe: {
|
|
252
|
+
onMethodEnd: ({ input, state }) =>
|
|
253
|
+
state.events.push(`${input.methodName}:${input.error ? "err" : "ok"}`),
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
A hook joins the graph like any other plugin: import or export it from an aggregate.
|
|
259
|
+
|
|
240
260
|
## Dependency injection
|
|
241
261
|
|
|
242
262
|
A plugin "names what it needs and receives it" without knowing who supplied it. That makes test doubles and configured providers trivial: register a real implementation under the same id and dependents get it transparently. This is the normal way one plugin reaches another, import the plugin, read it from the `imports` bag.
|
|
243
263
|
|
|
244
|
-
###
|
|
264
|
+
### Configuration
|
|
245
265
|
|
|
246
|
-
|
|
266
|
+
Runtime values (an options object, a configured client) enter through `createSdk`'s `configuration` map, keyed by plugin id. Each entry materializes as a value property with that id, satisfying a `declareProperty` / `declareOptionalProperty` stand-in — so plugins read configuration through `imports` like any other dependency, and tests inject doubles the same way.
|
|
247
267
|
|
|
248
268
|
```ts
|
|
249
|
-
import {
|
|
269
|
+
import { declareOptionalProperty } from "@zapier/kitcore";
|
|
250
270
|
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
271
|
+
const configRef = declareOptionalProperty<"config", { pageSize?: number }>({
|
|
272
|
+
id: "config",
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const listThings = defineMethod({
|
|
276
|
+
name: "listThings",
|
|
277
|
+
imports: [configRef],
|
|
278
|
+
run: ({ imports }) => api.list({ pageSize: imports.config?.pageSize ?? 10 }),
|
|
255
279
|
});
|
|
256
|
-
```
|
|
257
280
|
|
|
258
|
-
|
|
281
|
+
const sdk = createSdk(listThings, {
|
|
282
|
+
configuration: { config: { pageSize: 50 } },
|
|
283
|
+
});
|
|
284
|
+
```
|
|
259
285
|
|
|
260
286
|
## Extending a built SDK
|
|
261
287
|
|
|
@@ -268,6 +294,24 @@ addPlugin(sdk, defineMethod({ name: "ping", run: () => "pong" }));
|
|
|
268
294
|
sdk.ping(); // "pong", typed
|
|
269
295
|
```
|
|
270
296
|
|
|
297
|
+
## Teardown
|
|
298
|
+
|
|
299
|
+
`dispose` is `setup`'s dual: a leaf that owns a resource declares one, receiving `{ imports, state, input }`. `disposeSdk(sdk, input?)` runs every disposer in reverse build order (dependents before their dependencies) and is idempotent; `input` is forwarded to each disposer (e.g. `{ exitCode }` from a CLI shutdown).
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
import { disposeSdk } from "@zapier/kitcore";
|
|
303
|
+
|
|
304
|
+
const emitter = defineProperty({
|
|
305
|
+
name: "emitter",
|
|
306
|
+
setup: () => createEmitter(),
|
|
307
|
+
get: ({ state }) => state,
|
|
308
|
+
dispose: ({ state }) => state.close(),
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// at shutdown
|
|
312
|
+
await disposeSdk(sdk, { exitCode: 0 });
|
|
313
|
+
```
|
|
314
|
+
|
|
271
315
|
## Introspection
|
|
272
316
|
|
|
273
317
|
Re-export the built-in `getRegistryPlugin` to put a `getRegistry()` method on the SDK. It reports the live surface as plain data (one entry per binding, with the leaf's metadata), which is what drives generated docs, CLI commands, and MCP tools.
|