-
Notifications
You must be signed in to change notification settings - Fork 89
Add plugin authoring best-practices section and ref docs #621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
5
commits into
main
Choose a base branch
from
copilot/add-plugin-authoring-section
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+651
−0
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
4b96b2a
docs: add plugin authoring best practices to plugins guide
Copilot f2ea280
docs: clarify plugin icon format and secret reference rationale
Copilot 32a616c
docs: plan plugin API reference pages
Copilot 3b64e69
docs: restructure plugin API reference as single page
Copilot caaadfb
docs: replace per-method pages with single plugin-api reference page
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -111,3 +111,245 @@ Once installed, all decorators, data types, and resolver functions provided by t | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some decorators or resolver functions may require the plugin to be initialized and will throw an error if not set up properly. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Please refer to the specific plugin's documentation for details on usage. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Plugin authoring best practices | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Plugins are TypeScript modules that import from `varlock/plugin-lib` and register their functionality at module load time. Everything below is drawn from real first-party plugins. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Scaffold and metadata | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A plugin is a single TypeScript file (`src/plugin.ts`). All registration calls run at the top level when the module is loaded. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```ts title="src/plugin.ts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { type Resolver, plugin } from 'varlock/plugin-lib'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Destructure the error classes you need | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { ValidationError, SchemaError, ResolutionError } = plugin.ERRORS; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Short identifier used internally (e.g. for logging) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.name = 'myplugin'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Optional: debug logger (only active when VARLOCK_DEBUG is set) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { debug } = plugin; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| debug('init - version =', plugin.version); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Icon from simple-icons (https://simpleicons.org) or any Iconify set | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.icon = 'simple-icons:yourservice'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Optional: declare well-known env var names so users get warnings if they | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // forget to wire them up as schema items | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.standardVars = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initDecorator: '@initMyPlugin', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token: { key: 'MY_SERVICE_TOKEN' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: { key: 'MY_SERVICE_URL' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // registration calls follow… | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### `plugin.registerRootDecorator` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Root decorators appear as `@decoratorName(...)` comments at the top of a `.env` file. They are used for plugin initialization and run in two phases: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - **`process`** — runs during schema parsing. Validates static arguments (e.g. `id=`), creates the instance record, and returns a plain serialisable object containing any `Resolver` references for dynamic args. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - **`execute`** — runs during value resolution. Awaits the dynamic resolvers returned by `process` and performs auth/connection setup. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```ts title="src/plugin.ts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface PluginInstance { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const instances: Record<string, PluginInstance> = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.registerRootDecorator({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'initMyPlugin', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Initialise a MyPlugin instance', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isFunction: true, // required when the decorator accepts arguments | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async process(argsVal) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { objArgs } = argsVal; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!objArgs) throw new SchemaError('@initMyPlugin requires arguments'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // id must be a literal string so we can key the instance map at parse time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (objArgs.id && !objArgs.id.isStatic) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new SchemaError('id must be a static value'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const id = String(objArgs.id?.staticValue ?? '_default'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (instances[id]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new SchemaError(`Instance "${id}" is already initialised`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| instances[id] = {}; // reserve the slot | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Return resolvers for dynamic args alongside any static data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { id, tokenResolver: objArgs.token }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async execute({ id, tokenResolver }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Await dynamic values (these may reference other schema items) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const token = await tokenResolver?.resolve(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| instances[id].token = token ? String(token) : undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### `plugin.registerDataType` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Data types appear as `@type=myType` on an item. They can mark values as sensitive, add validation, and surface documentation links. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```ts title="src/plugin.ts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.registerDataType({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'myServiceToken', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sensitive: true, // value will be redacted in logs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typeDescription: 'Authentication token for MyService', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon: 'simple-icons:yourservice', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docs: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Creating API tokens', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: 'https://docs.yourservice.example/tokens', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Optional: validate the raw string value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async validate(val) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof val !== 'string' || !val.startsWith('mst_')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ValidationError('Token must start with "mst_"'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### `plugin.registerResolverFunction` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Resolver functions appear as values in `.env` files: `MY_SECRET=myPlugin(ref)`. They also run in two phases: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - **`process`** — runs at parse time. Validate argument shapes and return the resolvers + metadata your `resolve` call needs. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - **`resolve`** — runs at resolution time. Awaits resolvers, contacts the external service, and returns the final string value. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```ts title="src/plugin.ts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.registerResolverFunction({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'myPlugin', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: 'Fetch secret from MyService', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon: 'simple-icons:yourservice', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| argsSchema: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'array', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| arrayMinLength: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| arrayMaxLength: 2, // myPlugin(ref) or myPlugin(instanceId, ref) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let instanceId = '_default'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let refResolver: Resolver; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.arrArgs!.length === 1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refResolver = this.arrArgs![0]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // first arg is the instance id – must be a literal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.arrArgs![0].isStatic) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new SchemaError('Instance id must be a static value'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| instanceId = String(this.arrArgs![0].staticValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refResolver = this.arrArgs![1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+240
to
+253
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!instances[instanceId]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new SchemaError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `No MyPlugin instance "${instanceId}" found`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { tip: 'Add @initMyPlugin() to your .env.schema file' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { instanceId, refResolver }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async resolve({ instanceId, refResolver }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ref = await refResolver.resolve(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof ref !== 'string') throw new SchemaError('Expected a string reference'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const instance = instances[instanceId]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ... call your SDK / API and return the secret value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return `fetched:${ref}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Error handling | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Always use error classes from `plugin.ERRORS`: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Class | When to use | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |---|---| | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SchemaError` | Problems detected at parse/schema-build time (bad args, missing config) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `ResolutionError` | Problems at value-fetch time (secret not found, network error) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `ValidationError` | Value fails a `@type` constraint | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `CoercionError` | Value cannot be converted to the expected type | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Pass a `tip` string (or array of strings) to guide users toward a fix: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ResolutionError(`Secret "${ref}" not found`, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tip: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Verify the secret name is correct in MyService', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Check your token has read access', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Package setup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```json title="package.json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "module", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "main": "dist/index.js", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "types": "dist/index.d.ts", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "exports": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "./plugin": "./dist/plugin.cjs" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "files": ["dist"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "engines": { "node": ">=22" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "peerDependencies": { "varlock": "*" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "devDependencies": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "varlock": "...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "tsup": "...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "vitest": "..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Key points: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `"./plugin"` exports to a **CJS** file — this is required for runtime plugin loading. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - SDK/client libraries should go in `devDependencies` and be bundled via tsup; they must **not** be listed as runtime `dependencies` (which would require the user to install them separately). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `varlock` is a `peerDependency` so that `instanceof` checks on error classes work correctly. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```ts title="tsup.config.ts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { defineConfig } from 'tsup'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default defineConfig({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entry: ['src/plugin.ts'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| format: ['cjs'], // CJS required for plugin loading | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dts: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sourcemap: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| treeshake: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| external: ['varlock'], // peer – do NOT bundle | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Testing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```ts title="vitest.config.ts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { defineConfig } from 'vitest/config'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default defineConfig({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolve: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Resolve varlock's `ts-src` condition so tests run against TypeScript source | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conditions: ['ts-src'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| define: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Required – varlock uses these globals at import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __VARLOCK_BUILD_TYPE__: JSON.stringify('test'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __VARLOCK_SEA_BUILD__: 'false', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Without `conditions: ['ts-src']` and the two `define` entries your tests will fail with a `ReferenceError`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.