@zapier/kitcore 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,156 @@
1
1
  # @zapier/kitcore
2
2
 
3
+ ## 0.21.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2102c00: **Removed the legacy function-plugin bridge.** A plugin is now always a
8
+ descriptor built by `define*` / `declare*`, never a `(sdk) => provides`
9
+ function. These exits shipped deprecated with runtime warnings in 0.5.0 and are
10
+ now gone:
11
+
12
+ | Removed | Use instead |
13
+ | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
14
+ | `createPluginStack`, `composePlugins` | `definePlugin({ name, exports })` and `createSdk(root)` |
15
+ | `createPluginMethod`, `createPaginatedPluginMethod` | `defineMethod({ output: "item" })` / `defineMethod({ output: "list" })` |
16
+ | `createCorePlugin(options)` | `createSdk(root, { configuration: { [CORE_OPTIONS_ID]: options } })` |
17
+ | `fromFunctionPlugin`, `defineLegacyMerge` | `createSdk(root)` on a descriptor root |
18
+ | `definePlugin(fn)` (the function form) | `defineMethod` / `defineProperty` / `definePlugin({ ... })` |
19
+ | `Plugin<TSdk, TProvides>` (the function-plugin type) | `Plugin` now names the descriptor a `define*` call returns |
20
+ | `PluginProvides`, `Sdk`, `PluginStack`, `LegacyPlugin`, `LegacyMergePlugin` types | `PluginSurface<typeof plugin>` for a plugin's own surface |
21
+ | `OutputFormatter` type | `defineFormatter`, whose result is a `Formatter` |
22
+
23
+ Also removed, with no deprecation warning: `createFunction`,
24
+ `createPaginatedFunction`, and the `AdaptPage` type. Only a function plugin
25
+ could use their output. Author with `defineMethod({ output: "item" })` or
26
+ `defineMethod({ output: "list" })` instead.
27
+
28
+ Consequences for code that builds with `createSdk`:
29
+ - `addPlugin(sdk, plugin)` types `plugin` as a descriptor, so a function or
30
+ any other non-plugin no longer compiles, and it refuses one at runtime too.
31
+ - `context.meta` and `context.core` are gone. Read metadata with
32
+ `getRegistry(sdk)`, and inject framework options under `CORE_OPTIONS_ID`.
33
+ - `SdkContext` has no index signature, so `sdk.context.anything` no longer
34
+ typechecks.
35
+ - Description fields live flat on the descriptor, as the author wrote them,
36
+ and flat on the built entry, like its runtime fields. There is no `meta` bag
37
+ on either any more. A `defineOverride` carries its fields as
38
+ `MethodOverridePlugin.patch` and writes them into the entry, so the
39
+ descriptor still shows what the author wrote.
40
+ - The description fields split by kind. `PropertyMeta` is what any leaf can
41
+ carry: `description`, `categories`, `packages`, `stability`, `experimental`,
42
+ `deprecation`. `MethodMeta` extends it with what only a callable has: `type`,
43
+ `itemType`, `returnType`, `confirm`, `aliases`, `supportsJsonOutput`. A
44
+ property config no longer accepts `aliases`, and a `defineOverride` patch is
45
+ typed as `OverridableMetaFields` instead of the whole bag. `PluginMeta` and
46
+ `LeafMeta` are deprecated aliases of `MethodMeta`. Gone from all of them:
47
+ `formatter`, `resolvers`, the `TSdk` type parameter, `inputSchema` and
48
+ `outputSchema` (method fields, since they drive validation), and the
49
+ `[key: string]: any` index signature, so an unknown field no longer compiles.
50
+ `skipOutputValidation` is a runtime switch and lives on the method config
51
+ beside `skipInputValidation`.
52
+ - Every descriptor kind extends `PluginBase` (`pluginType`, `name`,
53
+ `namespace`, `id`, `imports`, `importBindings`). Methods and properties extend
54
+ `LeafBase` (stand-in and default markers, `setup`, `dispose`), and a hook adds
55
+ `PluginLifecycle`. `PluginType` names the discriminant union. No shape
56
+ changed; the shared fields are declared once.
57
+ - Pagination helpers are exported by name rather than through a wildcard. The
58
+ helpers themselves are unchanged.
59
+ - `getRegistry` is no longer a reserved root key. `context` still is, and
60
+ `createSdk` now refuses it for a root too, not only `addPlugin`.
61
+
62
+ Errors that used to surface as a `TypeError` about a kitcore internal now name
63
+ the mistake:
64
+ - `getContext(sdk)` and `getRegistry(sdk)` throw `CoreErrorCode.NoSdkContext`
65
+ when the object has no plugin graph. `getRegistry(sdk)` still falls back to a
66
+ surfaced `getRegistry()` first.
67
+ - `createSdk` and `addPlugin` refuse a non-descriptor, a `declare*` stand-in,
68
+ and any `pluginType` the model does not build. An extension built against a
69
+ kitcore that still had the bridge fails this way, with
70
+ `unknown pluginType "legacy"`, and must be rebuilt against this release.
71
+ Nested plugins are checked too, and `definePlugin`, `defineMethod`,
72
+ `defineProperty`, and `defineHook` check each `imports` and `exports` entry as
73
+ it is written.
74
+
75
+ `context.plugins` now holds every plugin in the graph. A hook and a
76
+ `defineOverride` have entries too, with no value, and every entry carries
77
+ `descriptor`, the plugin object it was built from.
78
+
79
+ `addPlugin` is transactional and checks identity:
80
+ - A failed add rolls back everything it wrote and rethrows the original error.
81
+ A `dispose` registered by a `setup` that completed runs during the rollback.
82
+ Only its synchronous part is guaranteed, so release before the first `await`.
83
+ - A plugin whose id another plugin already claimed is refused instead of being
84
+ dropped silently. Re-adding the same descriptor still works, as does a
85
+ provider for an optional stand-in that nothing satisfied. Two overrides of
86
+ one method need distinct `namespace`s. A provider for an id a `declareDefault`
87
+ claimed belongs in the `createSdk` graph.
88
+
89
+ `Plugin` is the type of any `define*` result, for typing a slot that accepts
90
+ plugins. It replaces the function-plugin type of the same name.
91
+
92
+ Fixed: an inline `define*` call inside `definePlugin({ exports: [...] })` keeps
93
+ its inferred types instead of widening `input` to `any`.
94
+
95
+ Fixed: a plugin named `__proto__` replaced the prototype of kitcore's internal
96
+ plugin table instead of being stored. Every table kitcore keys by an
97
+ author-chosen name (plugin id, binding name) is now created without a
98
+ prototype, so any `Object.prototype` member name (`toString`, `constructor`,
99
+ `__proto__`) is an ordinary plugin name.
100
+
101
+ ## 0.20.0
102
+
103
+ ### Minor Changes
104
+
105
+ - 4b9d735: `createSdk` now checks that each id's providers match the contract its reference
106
+ declared, not only that a provider exists. A graph joined by string id used to
107
+ compile on the strength of the string alone, so a provider whose types
108
+ contradicted the reference reached the consumer and broke at runtime.
109
+
110
+ ```ts
111
+ const doubleRef = declareMethod<"double", { value: number }, number>({
112
+ id: "double",
113
+ });
114
+ // Written for a display surface: it formats instead of computing.
115
+ const formatsIt = defineMethod({
116
+ name: "double",
117
+ run: ({ input }: { input: { value: number } }) => `${input.value * 2}`,
118
+ });
119
+ // Compiled clean before. Rejected at createSdk now.
120
+ ```
121
+
122
+ A provider may accept **wider** input and must return a **subtype** of the
123
+ declared output, since the comparison is on the call a consumer makes. See the
124
+ kitcore README for how this applies to optional references, module references,
125
+ and `declareDefault`.
126
+
127
+ `selectExports` and `omitExports` now forward their source's ledgers. They
128
+ returned a bare aggregate before, which dropped both the requirements and the
129
+ contract ledger for everything behind them, so a head that reached a module
130
+ through either helper got no graph checking at all.
131
+
132
+ **Both changes can surface an error in a graph that compiled before.** That is
133
+ the point, since the mismatch was already there. Correct the provider, or widen
134
+ the reference to what the provider actually offers.
135
+
136
+ This release also fixes a latent problem with nested plugins. The cost of
137
+ checking a graph grew exponentially with aggregate nesting depth, and with the
138
+ length of a method import chain. Deep enough composition failed to compile at
139
+ all, reporting `error TS2589`. Both are close to linear now, so nesting and
140
+ chaining stay affordable.
141
+
142
+ Contract checking itself is new work, and it grows the declaration files a
143
+ package publishes. Every downstream typecheck and editor pays that cost.
144
+
145
+ **A hand-written annotation must be updated to keep its check.** `LeafSummary`
146
+ now requires the surfaced binding as a fourth argument, so a three-argument use
147
+ is a compile error. A `PluginSummary` written with two arguments still compiles
148
+ and declares no contract, which drops that plugin out of the check silently.
149
+
150
+ Added `ContractEntry`, `MethodContract`, `DeclarationSummary`, and
151
+ `OptionalDeclarationSummary`, for annotating a plugin by hand instead of letting
152
+ `define*` infer it.
153
+
3
154
  ## 0.19.0
4
155
 
5
156
  ### Minor Changes
package/README.md CHANGED
@@ -366,7 +366,7 @@ Re-export the built-in `getRegistryPlugin` if you also want a `getRegistry()` me
366
366
 
367
367
  The result is memoized per SDK and per package filter, and `addPlugin` drops it, so a registry read still reflects anything added after the build. Because every reader shares that one object, its `functions` and `categories` arrays are frozen: copy before sorting or filtering in place.
368
368
 
369
- That holds for an SDK with a kitcore context, which is anything `createSdk` built. An SDK without one, meaning a `createPluginStack()` build, has no memo to share: `getRegistry(sdk)` forwards to the SDK's own `getRegistry()` and hands back whatever it builds, fresh and mutable on every call.
369
+ That holds for an SDK with a kitcore context, which is anything `createSdk` built. An object without one that still surfaces its own `getRegistry()`, such as a test stub or a wrapper projecting someone else's registry, has no memo to share: `getRegistry(sdk)` forwards to that accessor and hands back whatever it builds, fresh and mutable on every call.
370
370
 
371
371
  ## Resolving inputs: controllers
372
372
 
@@ -454,6 +454,51 @@ Two flavors, by what happens when nothing provides the id:
454
454
  - **Required** (`declareMethod` / `declareProperty`): an unsatisfied reference is a **compile-time** error (with a runtime backstop). Use when the capability must be present.
455
455
  - **Optional** (`declareOptionalProperty` / `declareOptionalMethod`): binds `undefined` instead of failing, so the consumer handles absence in code (`{ ...DEFAULTS, ...imports.config }` for a value, `imports.track?.(...)` for a method). Use to reference a _foreign_ capability userland may or may not import, without claiming its slot.
456
456
 
457
+ ### The contract is checked, not just the id
458
+
459
+ `createSdk` checks that each id's providers match the contract the reference declared, not only that a provider exists. The comparison is on the call a consumer makes, so a provider may accept **wider** input and must return a **subtype** of the declared output. A provider written from scratch is checked the same as one written against the reference.
460
+
461
+ ```ts
462
+ const doubleRef = declareMethod<"double", { value: number }, number>({
463
+ id: "double",
464
+ });
465
+
466
+ // Rejected at createSdk: this author wrote `double` for a display surface, so
467
+ // it formats instead of computing. The declaration promised a number.
468
+ defineMethod({
469
+ name: "double",
470
+ run: ({ input }: { input: { value: number } }) => `${input.value * 2}`,
471
+ });
472
+
473
+ // Also rejected: an `item` method is a fine plugin, but the declaration asked
474
+ // for a bare number, not an envelope.
475
+ defineMethod({
476
+ name: "double",
477
+ output: "item",
478
+ run: async ({ input }: { input: { value: number } }) => ({
479
+ data: input.value * 2,
480
+ }),
481
+ });
482
+
483
+ // Accepted.
484
+ defineMethod({
485
+ name: "double",
486
+ run: ({ input }: { input: { value: number } }) => input.value * 2,
487
+ });
488
+ ```
489
+
490
+ Two modules declaring one id independently is the intended use, and stays legal while they agree. They are caught when they disagree, because no single provider can satisfy both. Every reachable provider under an id must honor the contract, so a sound `declareDefault` beside an unsound explicit provider is still rejected: the explicit one is what the runtime picks.
491
+
492
+ An optional reference is checked the same way when a provider does appear. Its contract is the binding a consumer sees, so `undefined` is part of it: a consumer of an optional reference handles the absent case regardless, and a by-reference provider is allowed to pass `undefined` explicitly.
493
+
494
+ A module reference (`declarePlugin`) is checked through its export references, leaf by leaf, so a module method typed differently from its reference is caught. What is NOT checked is whether the real module exports everything the reference promised: only the module's own id is a requirement, so a promised export the module lacks still fails at runtime.
495
+
496
+ Spell the contract out. An id-only `declareMethod<"double">({ id: "double" })` defaults to `unknown` on both sides. That makes it over-strict on input, since a consumer may call it with no argument and any provider needing real input is rejected, and vacuous on output, since every provider returns a subtype of `unknown`. No `declare*` call opts out of the compatibility check.
497
+
498
+ What the graph checks do NOT reach. A `defineHook` carries no summary, so a declaration reachable only through a hook's `imports` is checked by neither ledger. `addPlugin` performs no graph checks at all. And a positional surface cannot be declared by id: `declareMethod` describes an object call only, so a positional provider is correctly rejected against one, and referencing a positional method means casting past the ledger (`curl` does exactly that for `fetch`). Those three are limitations, not design.
499
+
500
+ If you annotate a plugin by hand rather than letting `define*` infer it, pass the contract. `LeafSummary` takes the surfaced binding as a fourth argument (`LeafSummary<"", "setup", typeof imports, MethodContract<SetupOptions, Promise<void>>>`), and a reference is `DeclarationSummary<TId, TBinding>` or `OptionalDeclarationSummary<TId, TBinding>`. A `PluginSummary` written with two arguments still compiles and declares no contract, so a hand-annotated plugin drops out of the compatibility check silently.
501
+
457
502
  ## Defaults
458
503
 
459
504
  When you _own_ a capability slot and can ship a working implementation, register it as a default with `declareDefault({ plugin })` rather than referencing it. A default is a real, single node in the graph, so it works out of the box, hooks and middleware can wrap it, and an explicit provider of the same id silently preempts it (that is the seam for userland or another plugin to replace it).