@andesine/extensions 0.0.0-stage → 0.5.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 ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## 0.5.0
4
+
5
+ - First release, versioned with `@andesine/sdk` and the `andesine` CLI.
6
+ - Add the root entry for `andesine.config.ts`: `defineExtension` (the manifest; a Zod
7
+ `configuration` becomes its JSON Schema subset) and `defineBuild` (the frontend module
8
+ and custom icon sets for `andesine extensions build`).
9
+ - Add `@andesine/extensions/solid`: the worker runtime for extension frontends
10
+ (`startExtension`), a `solid-js/universal` renderer that sends serialized view
11
+ patches to Andesine, and the host components: layout (`Stack`, `Grid`, `Box`,
12
+ `Divider`, `ScrollArea`), text (`Text`, `Heading`, `Code`, `Link`, `Badge`,
13
+ `Icon`), controls (`Button`, `IconButton`, `Input`, `Textarea`, `Select`,
14
+ `Combobox`, `Checkbox`, `Toggle`, `ToggleGroup`, `ColorInput`, `Tooltip`),
15
+ settings (`Setting`, `List`), and data display (`Card`, `Tree`, `Tabs`, `Tab`,
16
+ `Disclosure`, `Callout`, `Steps`, `Step`, `Figure`, `CodeBlock`, `TimeAgo`,
17
+ `Spinner`, `Skeleton`).
18
+ - Layout and text components (`Stack`, `Grid`, `Box`, `Text`, `Heading`) accept
19
+ `class` with utility classes of the Andesine UnoCSS preset. Icons are icon
20
+ classes (`i-lucide:rocket`; `?bg` for colored icons) from the default Lucide
21
+ and Tabler sets, installed Iconify sets, or custom sets.
22
+ - Add `ContentSlot` for element views: it shows the element's editable content,
23
+ which Andesine owns. Element views receive `{ element, props }` with the
24
+ element name and its props.
25
+ - Add host actions: `copyText`, `downloadFile`, and `openURL`. They run shortly
26
+ after a user interaction in the extension's view and reject with
27
+ `ExtensionRequestError` (`code`) otherwise.
28
+ - Add `useEditor()` for views: block action views can `replace` the selected
29
+ blocks, `insertAfter` them, and `close`; element views can `setElementProps`.
30
+ Andesine validates each edit and applies it as the member's edit.
31
+ - Add `usePermissions()` and `hasPermission()` for the member's effective
32
+ permissions, `apiFetch` and `EXTENSION_API_URL` to use the Andesine SDK from
33
+ the frontend (requests go through Andesine with the member's session), and
34
+ `backend.fetch` for the extension's own backend with a session token.
35
+ - Add `useConfiguration()` for the configuration's non-secret values, with
36
+ defaults for unset fields. It updates when a manager saves the configuration.
37
+ - Add `storage` for the extension's JSON key-value storage, which its backend
38
+ shares: `get`, `set`, `delete`, and `list` (in key order, with an optional
39
+ prefix).
40
+ - `backend.fetch` also sends the `Andesine-Extension` and `Andesine-Instance`
41
+ headers, which `verifySession` from `@andesine/sdk/extensions` needs. The
42
+ backend's CORS configuration must allow them with `Authorization`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vrite, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,4 +1,85 @@
1
- # Temporary Holding Version
1
+ # @andesine/extensions
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
4
- If no other versions are published within 30 days, this package and version will be deleted.
3
+ Authoring tools for Andesine extensions. Extension frontends are Solid components
4
+ that run in a sandboxed worker. Andesine renders its own components from the
5
+ serialized view tree; extension code has no DOM access.
6
+
7
+ ## Solid entry
8
+
9
+ Compile extension JSX for the universal renderer:
10
+
11
+ ```js
12
+ // babel-preset-solid options
13
+ { generate: "universal", moduleName: "@andesine/extensions/solid" }
14
+ ```
15
+
16
+ Bundle for the `browser` export condition (Solid's `worker` condition selects its
17
+ non-reactive server build). Then register the views that the manifest names:
18
+
19
+ ```tsx
20
+ import { createSignal } from "solid-js";
21
+ import { Button, Stack, Text, startExtension } from "@andesine/extensions/solid";
22
+
23
+ const Counter = () => {
24
+ const [count, setCount] = createSignal(0);
25
+
26
+ return (
27
+ <Stack direction="row" gap="small">
28
+ <Text>{count()}</Text>
29
+ <Button onClick={() => setCount(count() + 1)}>Add</Button>
30
+ </Stack>
31
+ );
32
+ };
33
+
34
+ startExtension({ Counter });
35
+ ```
36
+
37
+ Only the exported host components render. Props must be JSON values; functions
38
+ become callbacks that the host calls with plain values.
39
+
40
+ ## Element views
41
+
42
+ An element view renders a document element (for example `<Accordion>`) and
43
+ receives `{ element, props }`. `ContentSlot` shows the element's editable
44
+ content; Andesine keeps that content when the slot is hidden. A view's CSS does
45
+ not apply inside its slot.
46
+
47
+ ## Host actions
48
+
49
+ `copyText(text)`, `downloadFile(name, text, type?)`, and `openURL(url)` ask
50
+ Andesine to act for the extension. Call them from an interaction, for example a
51
+ button's `onClick`; otherwise they reject with `ExtensionRequestError` and the
52
+ code `user_interaction_required`. `openURL` asks the member to confirm URLs
53
+ outside the manifest's declared URLs.
54
+
55
+ ## Block actions and edits
56
+
57
+ A block action view receives `{ blocks }`: the selected blocks as editor JSON.
58
+ `useEditor()` returns `replace(content)`, `insertAfter(content)`, and `close()`
59
+ for block action views, and `setElementProps(props)` for element views (call it
60
+ from an interaction). Content must be blocks that are allowed where the
61
+ selection is; edits fail when the selected blocks changed meanwhile.
62
+
63
+ ## API, permissions, and backend
64
+
65
+ ```ts
66
+ import { createClient } from "@andesine/sdk";
67
+ import { EXTENSION_API_URL, apiFetch, backend, hasPermission } from "@andesine/extensions/solid";
68
+
69
+ const client = createClient({ baseURL: EXTENSION_API_URL, fetch: apiFetch });
70
+ const entries = hasPermission("read:entries") ? await client.entries.list({}) : [];
71
+ const response = await backend.fetch("/publish", { method: "POST" });
72
+ ```
73
+
74
+ API requests use the member's session and are limited to the member's
75
+ permissions that the extension was granted (`usePermissions()`); other
76
+ operations answer with HTTP 403. `backend.fetch` calls paths of the manifest's
77
+ backend URL with a short-lived session token in `Authorization: Bearer`, which
78
+ the backend verifies with Andesine.
79
+
80
+ ## Examples
81
+
82
+ See the extension examples in this repository (`packages/public/extension-examples`): element
83
+ views, a block action that calls its backend, a webhook-driven backend, and a secret
84
+ configuration field. `npx andesine extensions init` creates a project with samples of every
85
+ feature.
@@ -0,0 +1,295 @@
1
+ import * as z from "zod";
2
+ //#region ../../private/contracts/src/extensions/configuration.d.ts
3
+ type ExtensionConfiguration = z.output<typeof extensionConfigurationType>;
4
+ /** Flat JSON Schema (draft 2020-12) subset that the host validates without running extension code. */
5
+ declare const extensionConfigurationType: z.ZodObject<{
6
+ $schema: z.ZodOptional<z.ZodLiteral<"https://json-schema.org/draft/2020-12/schema">>;
7
+ type: z.ZodLiteral<"object">;
8
+ properties: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
9
+ type: z.ZodLiteral<"string">;
10
+ minLength: z.ZodOptional<z.ZodInt>;
11
+ maxLength: z.ZodOptional<z.ZodInt>;
12
+ pattern: z.ZodOptional<z.ZodString>;
13
+ format: z.ZodOptional<z.ZodEnum<{
14
+ uri: "uri";
15
+ email: "email";
16
+ }>>;
17
+ default: z.ZodOptional<z.ZodString>;
18
+ secret: z.ZodOptional<z.ZodLiteral<true>>;
19
+ control: z.ZodOptional<z.ZodLiteral<"textarea">>;
20
+ title: z.ZodOptional<z.ZodString>;
21
+ description: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strict>, z.ZodObject<{
23
+ type: z.ZodLiteral<"string">;
24
+ enum: z.ZodArray<z.ZodString>;
25
+ default: z.ZodOptional<z.ZodString>;
26
+ title: z.ZodOptional<z.ZodString>;
27
+ description: z.ZodOptional<z.ZodString>;
28
+ }, z.core.$strict>, z.ZodObject<{
29
+ type: z.ZodEnum<{
30
+ number: "number";
31
+ integer: "integer";
32
+ }>;
33
+ minimum: z.ZodOptional<z.ZodNumber>;
34
+ maximum: z.ZodOptional<z.ZodNumber>;
35
+ exclusiveMinimum: z.ZodOptional<z.ZodNumber>;
36
+ exclusiveMaximum: z.ZodOptional<z.ZodNumber>;
37
+ multipleOf: z.ZodOptional<z.ZodNumber>;
38
+ default: z.ZodOptional<z.ZodNumber>;
39
+ title: z.ZodOptional<z.ZodString>;
40
+ description: z.ZodOptional<z.ZodString>;
41
+ }, z.core.$strict>, z.ZodObject<{
42
+ type: z.ZodLiteral<"boolean">;
43
+ default: z.ZodOptional<z.ZodBoolean>;
44
+ title: z.ZodOptional<z.ZodString>;
45
+ description: z.ZodOptional<z.ZodString>;
46
+ }, z.core.$strict>, z.ZodObject<{
47
+ type: z.ZodLiteral<"array">;
48
+ items: z.ZodUnion<readonly [z.ZodObject<{
49
+ type: z.ZodLiteral<"string">;
50
+ minLength: z.ZodOptional<z.ZodInt>;
51
+ maxLength: z.ZodOptional<z.ZodInt>;
52
+ pattern: z.ZodOptional<z.ZodString>;
53
+ format: z.ZodOptional<z.ZodEnum<{
54
+ uri: "uri";
55
+ email: "email";
56
+ }>>;
57
+ }, z.core.$strict>, z.ZodObject<{
58
+ type: z.ZodLiteral<"string">;
59
+ enum: z.ZodArray<z.ZodString>;
60
+ }, z.core.$strict>, z.ZodObject<{
61
+ type: z.ZodEnum<{
62
+ number: "number";
63
+ integer: "integer";
64
+ }>;
65
+ minimum: z.ZodOptional<z.ZodNumber>;
66
+ maximum: z.ZodOptional<z.ZodNumber>;
67
+ exclusiveMinimum: z.ZodOptional<z.ZodNumber>;
68
+ exclusiveMaximum: z.ZodOptional<z.ZodNumber>;
69
+ multipleOf: z.ZodOptional<z.ZodNumber>;
70
+ }, z.core.$strict>, z.ZodObject<{
71
+ type: z.ZodLiteral<"boolean">;
72
+ }, z.core.$strict>]>;
73
+ minItems: z.ZodOptional<z.ZodInt>;
74
+ maxItems: z.ZodOptional<z.ZodInt>;
75
+ uniqueItems: z.ZodOptional<z.ZodBoolean>;
76
+ default: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
77
+ title: z.ZodOptional<z.ZodString>;
78
+ description: z.ZodOptional<z.ZodString>;
79
+ }, z.core.$strict>]>>;
80
+ required: z.ZodOptional<z.ZodArray<z.ZodString>>;
81
+ additionalProperties: z.ZodLiteral<false>;
82
+ }, z.core.$strict>;
83
+ //#endregion
84
+ //#region ../../private/contracts/src/extensions/manifest.d.ts
85
+ declare const extensionManifestType: z.ZodObject<{
86
+ elementViews: z.ZodDefault<z.ZodArray<z.ZodObject<{
87
+ id: z.ZodString;
88
+ name: z.ZodString;
89
+ element: z.ZodString;
90
+ entry: z.ZodString;
91
+ descendants: z.ZodDefault<z.ZodArray<z.ZodObject<{
92
+ element: z.ZodString;
93
+ entry: z.ZodString;
94
+ }, z.core.$strict>>>;
95
+ }, z.core.$strict>>>;
96
+ blockActions: z.ZodDefault<z.ZodArray<z.ZodObject<{
97
+ id: z.ZodString;
98
+ label: z.ZodString;
99
+ icon: z.ZodOptional<z.ZodString>;
100
+ blocks: z.ZodArray<z.ZodString>;
101
+ entry: z.ZodString;
102
+ }, z.core.$strict>>>;
103
+ panels: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
104
+ side: z.ZodLiteral<"left">;
105
+ entry: z.ZodString;
106
+ id: z.ZodString;
107
+ name: z.ZodString;
108
+ icon: z.ZodString;
109
+ }, z.core.$strict>, z.ZodObject<{
110
+ side: z.ZodLiteral<"right">;
111
+ context: z.ZodEnum<{
112
+ entry: "entry";
113
+ workspace: "workspace";
114
+ collection: "collection";
115
+ }>;
116
+ entry: z.ZodString;
117
+ id: z.ZodString;
118
+ name: z.ZodString;
119
+ icon: z.ZodString;
120
+ }, z.core.$strict>], "side">>>;
121
+ settingsSections: z.ZodDefault<z.ZodArray<z.ZodObject<{
122
+ id: z.ZodString;
123
+ heading: z.ZodString;
124
+ entry: z.ZodString;
125
+ }, z.core.$strict>>>;
126
+ name: z.ZodString;
127
+ version: z.ZodString;
128
+ displayName: z.ZodString;
129
+ description: z.ZodString;
130
+ icon: z.ZodOptional<z.ZodString>;
131
+ apiVersion: z.ZodLiteral<1>;
132
+ permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<{
133
+ "read:restricted_collections": "read:restricted_collections";
134
+ entries: "entries";
135
+ "read:entries": "read:entries";
136
+ versions: "versions";
137
+ "read:versions": "read:versions";
138
+ publishing: "publishing";
139
+ "read:publishing": "read:publishing";
140
+ collections: "collections";
141
+ "read:collections": "read:collections";
142
+ memberships: "memberships";
143
+ "read:memberships": "read:memberships";
144
+ roles: "roles";
145
+ "read:roles": "read:roles";
146
+ webhooks: "webhooks";
147
+ "read:webhooks": "read:webhooks";
148
+ "ai-answers": "ai-answers";
149
+ }>>>;
150
+ requests: z.ZodDefault<z.ZodArray<z.ZodString>>;
151
+ backend: z.ZodOptional<z.ZodObject<{
152
+ url: z.ZodString;
153
+ keys: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
154
+ kid: z.ZodString;
155
+ kty: z.ZodLiteral<"OKP">;
156
+ alg: z.ZodLiteral<"EdDSA">;
157
+ crv: z.ZodLiteral<"Ed25519">;
158
+ x: z.ZodString;
159
+ }, z.core.$strict>, z.ZodObject<{
160
+ kid: z.ZodString;
161
+ kty: z.ZodLiteral<"EC">;
162
+ alg: z.ZodLiteral<"ES256">;
163
+ crv: z.ZodLiteral<"P-256">;
164
+ x: z.ZodString;
165
+ y: z.ZodString;
166
+ }, z.core.$strict>], "alg">>;
167
+ }, z.core.$strict>>;
168
+ webhooks: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
169
+ events: z.ZodArray<z.ZodEnum<{
170
+ "entry.created": "entry.created";
171
+ "entry.updated": "entry.updated";
172
+ "entry.content_saved": "entry.content_saved";
173
+ "entry.moved": "entry.moved";
174
+ "entry.deleted": "entry.deleted";
175
+ "entry.restored": "entry.restored";
176
+ "collection.created": "collection.created";
177
+ "collection.updated": "collection.updated";
178
+ "collection.moved": "collection.moved";
179
+ "collection.deleted": "collection.deleted";
180
+ "collection.restored": "collection.restored";
181
+ "publishing.channel_advanced": "publishing.channel_advanced";
182
+ "publishing.channel_created": "publishing.channel_created";
183
+ "publishing.channel_deleted": "publishing.channel_deleted";
184
+ "extension.installed": "extension.installed";
185
+ "extension.enabled": "extension.enabled";
186
+ "extension.disabled": "extension.disabled";
187
+ "extension.updated": "extension.updated";
188
+ "extension.configured": "extension.configured";
189
+ "extension.uninstalled": "extension.uninstalled";
190
+ }>>;
191
+ path: z.ZodString;
192
+ channels: z.ZodDefault<z.ZodArray<z.ZodString>>;
193
+ }, z.core.$strict>>>;
194
+ configuration: z.ZodOptional<z.ZodObject<{
195
+ $schema: z.ZodOptional<z.ZodLiteral<"https://json-schema.org/draft/2020-12/schema">>;
196
+ type: z.ZodLiteral<"object">;
197
+ properties: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
198
+ type: z.ZodLiteral<"string">;
199
+ minLength: z.ZodOptional<z.ZodInt>;
200
+ maxLength: z.ZodOptional<z.ZodInt>;
201
+ pattern: z.ZodOptional<z.ZodString>;
202
+ format: z.ZodOptional<z.ZodEnum<{
203
+ uri: "uri";
204
+ email: "email";
205
+ }>>;
206
+ default: z.ZodOptional<z.ZodString>;
207
+ secret: z.ZodOptional<z.ZodLiteral<true>>;
208
+ control: z.ZodOptional<z.ZodLiteral<"textarea">>;
209
+ title: z.ZodOptional<z.ZodString>;
210
+ description: z.ZodOptional<z.ZodString>;
211
+ }, z.core.$strict>, z.ZodObject<{
212
+ type: z.ZodLiteral<"string">;
213
+ enum: z.ZodArray<z.ZodString>;
214
+ default: z.ZodOptional<z.ZodString>;
215
+ title: z.ZodOptional<z.ZodString>;
216
+ description: z.ZodOptional<z.ZodString>;
217
+ }, z.core.$strict>, z.ZodObject<{
218
+ type: z.ZodEnum<{
219
+ number: "number";
220
+ integer: "integer";
221
+ }>;
222
+ minimum: z.ZodOptional<z.ZodNumber>;
223
+ maximum: z.ZodOptional<z.ZodNumber>;
224
+ exclusiveMinimum: z.ZodOptional<z.ZodNumber>;
225
+ exclusiveMaximum: z.ZodOptional<z.ZodNumber>;
226
+ multipleOf: z.ZodOptional<z.ZodNumber>;
227
+ default: z.ZodOptional<z.ZodNumber>;
228
+ title: z.ZodOptional<z.ZodString>;
229
+ description: z.ZodOptional<z.ZodString>;
230
+ }, z.core.$strict>, z.ZodObject<{
231
+ type: z.ZodLiteral<"boolean">;
232
+ default: z.ZodOptional<z.ZodBoolean>;
233
+ title: z.ZodOptional<z.ZodString>;
234
+ description: z.ZodOptional<z.ZodString>;
235
+ }, z.core.$strict>, z.ZodObject<{
236
+ type: z.ZodLiteral<"array">;
237
+ items: z.ZodUnion<readonly [z.ZodObject<{
238
+ type: z.ZodLiteral<"string">;
239
+ minLength: z.ZodOptional<z.ZodInt>;
240
+ maxLength: z.ZodOptional<z.ZodInt>;
241
+ pattern: z.ZodOptional<z.ZodString>;
242
+ format: z.ZodOptional<z.ZodEnum<{
243
+ uri: "uri";
244
+ email: "email";
245
+ }>>;
246
+ }, z.core.$strict>, z.ZodObject<{
247
+ type: z.ZodLiteral<"string">;
248
+ enum: z.ZodArray<z.ZodString>;
249
+ }, z.core.$strict>, z.ZodObject<{
250
+ type: z.ZodEnum<{
251
+ number: "number";
252
+ integer: "integer";
253
+ }>;
254
+ minimum: z.ZodOptional<z.ZodNumber>;
255
+ maximum: z.ZodOptional<z.ZodNumber>;
256
+ exclusiveMinimum: z.ZodOptional<z.ZodNumber>;
257
+ exclusiveMaximum: z.ZodOptional<z.ZodNumber>;
258
+ multipleOf: z.ZodOptional<z.ZodNumber>;
259
+ }, z.core.$strict>, z.ZodObject<{
260
+ type: z.ZodLiteral<"boolean">;
261
+ }, z.core.$strict>]>;
262
+ minItems: z.ZodOptional<z.ZodInt>;
263
+ maxItems: z.ZodOptional<z.ZodInt>;
264
+ uniqueItems: z.ZodOptional<z.ZodBoolean>;
265
+ default: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
266
+ title: z.ZodOptional<z.ZodString>;
267
+ description: z.ZodOptional<z.ZodString>;
268
+ }, z.core.$strict>]>>;
269
+ required: z.ZodOptional<z.ZodArray<z.ZodString>>;
270
+ additionalProperties: z.ZodLiteral<false>;
271
+ }, z.core.$strict>>;
272
+ }, z.core.$strict>;
273
+ //#endregion
274
+ //#region src/index.d.ts
275
+ interface ExtensionDefinition extends Omit<ExtensionManifestInput, "configuration"> {
276
+ /** A Zod object of flat fields, or the configuration schema it converts to. */
277
+ configuration?: z.ZodObject | ExtensionConfiguration;
278
+ }
279
+ /** Options of `andesine extensions build`, exported from the config file as `build`. */
280
+ interface ExtensionBuildOptions {
281
+ /** The frontend module that exports the manifest's view entries (default: `src/frontend/index.tsx`). */
282
+ frontend?: string;
283
+ /** Custom icon sets: a set prefix and a directory of SVG files, e.g. `{ acme: "./icons" }`. */
284
+ icons?: Record<string, string>;
285
+ }
286
+ type ExtensionManifestInput = z.input<typeof extensionManifestType>;
287
+ /**
288
+ * Defines the extension manifest, the default export of `andesine.config.ts`. A Zod
289
+ * configuration becomes its JSON Schema subset; `andesine extensions build` validates the rest.
290
+ */
291
+ declare const defineExtension: (definition: ExtensionDefinition) => ExtensionManifestInput;
292
+ declare const defineBuild: (options: ExtensionBuildOptions) => ExtensionBuildOptions;
293
+ //#endregion
294
+ export { type ExtensionBuildOptions, type ExtensionDefinition, type ExtensionManifestInput, defineBuild, defineExtension };
295
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,177 @@
1
+ import * as z from "zod";
2
+ //#region ../../private/contracts/src/primitives/unique.ts
3
+ /** Whether every value is distinct. */
4
+ const uniqueItems = (values) => new Set(values).size === values.length;
5
+ //#endregion
6
+ //#region ../../private/contracts/src/extensions/configuration.ts
7
+ const JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
8
+ const MAX_CONFIGURATION_STRING_LENGTH = 1e4;
9
+ const MAX_CONFIGURATION_ARRAY_ITEMS = 100;
10
+ const MAX_CONFIGURATION_ENUM_VALUES = 100;
11
+ const MAX_CONFIGURATION_PATTERN_LENGTH = 500;
12
+ const isValidPattern = (pattern) => {
13
+ try {
14
+ new RegExp(pattern);
15
+ return true;
16
+ } catch {
17
+ return false;
18
+ }
19
+ };
20
+ const fieldKeyType = z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/, "Use a letter, then letters, digits, or underscores");
21
+ const lengthType = z.int().min(0).max(MAX_CONFIGURATION_STRING_LENGTH);
22
+ const metadata = {
23
+ title: z.string().min(1).max(100).optional(),
24
+ description: z.string().min(1).max(500).optional()
25
+ };
26
+ const stringItemType = z.strictObject({
27
+ type: z.literal("string"),
28
+ minLength: lengthType.optional(),
29
+ maxLength: lengthType.optional(),
30
+ pattern: z.string().min(1).max(MAX_CONFIGURATION_PATTERN_LENGTH).refine(isValidPattern, "Use a valid regular expression").optional(),
31
+ format: z.enum(["uri", "email"]).optional()
32
+ });
33
+ const enumItemType = z.strictObject({
34
+ type: z.literal("string"),
35
+ enum: z.array(z.string().min(1).max(200)).min(1).max(MAX_CONFIGURATION_ENUM_VALUES).refine(uniqueItems, "Options must be unique")
36
+ });
37
+ const numberItemType = z.strictObject({
38
+ type: z.enum(["number", "integer"]),
39
+ minimum: z.number().optional(),
40
+ maximum: z.number().optional(),
41
+ exclusiveMinimum: z.number().optional(),
42
+ exclusiveMaximum: z.number().optional(),
43
+ multipleOf: z.number().positive().optional()
44
+ });
45
+ const booleanItemType = z.strictObject({ type: z.literal("boolean") });
46
+ const stringFormatTypes = {
47
+ uri: z.url(),
48
+ email: z.email()
49
+ };
50
+ const itemType = z.union([
51
+ stringItemType,
52
+ enumItemType,
53
+ numberItemType,
54
+ booleanItemType
55
+ ]);
56
+ const fieldType = z.union([
57
+ stringItemType.extend({
58
+ ...metadata,
59
+ default: z.string().optional(),
60
+ secret: z.literal(true).optional(),
61
+ control: z.literal("textarea").optional()
62
+ }),
63
+ enumItemType.extend({
64
+ ...metadata,
65
+ default: z.string().optional()
66
+ }),
67
+ numberItemType.extend({
68
+ ...metadata,
69
+ default: z.number().optional()
70
+ }),
71
+ booleanItemType.extend({
72
+ ...metadata,
73
+ default: z.boolean().optional()
74
+ }),
75
+ z.strictObject({
76
+ ...metadata,
77
+ type: z.literal("array"),
78
+ items: itemType,
79
+ minItems: z.int().min(0).max(MAX_CONFIGURATION_ARRAY_ITEMS).optional(),
80
+ maxItems: z.int().min(0).max(MAX_CONFIGURATION_ARRAY_ITEMS).optional(),
81
+ uniqueItems: z.boolean().optional(),
82
+ default: z.array(z.union([
83
+ z.string(),
84
+ z.number(),
85
+ z.boolean()
86
+ ])).optional()
87
+ })
88
+ ]);
89
+ /** Flat JSON Schema (draft 2020-12) subset that the host validates without running extension code. */
90
+ const extensionConfigurationType = z.strictObject({
91
+ $schema: z.literal(JSON_SCHEMA_DIALECT).optional(),
92
+ type: z.literal("object"),
93
+ properties: z.record(fieldKeyType, fieldType).refine((properties) => Object.keys(properties).length <= 50, `Use at most 50 fields`),
94
+ required: z.array(fieldKeyType).refine(uniqueItems, "Fields must be unique").optional(),
95
+ additionalProperties: z.literal(false)
96
+ }).superRefine((configuration, context) => {
97
+ for (const [index, key] of (configuration.required || []).entries()) if (!configuration.properties[key]) context.addIssue({
98
+ code: "custom",
99
+ path: ["required", index],
100
+ message: "Unknown field"
101
+ });
102
+ for (const [key, field] of Object.entries(configuration.properties)) if ("secret" in field && field.secret && field.default !== void 0) context.addIssue({
103
+ code: "custom",
104
+ path: [
105
+ "properties",
106
+ key,
107
+ "default"
108
+ ],
109
+ message: "Secret fields cannot have defaults"
110
+ });
111
+ else if (field.default !== void 0) {
112
+ if (!createConfigurationValueType(field).safeParse(field.default).success) context.addIssue({
113
+ code: "custom",
114
+ path: [
115
+ "properties",
116
+ key,
117
+ "default"
118
+ ],
119
+ message: "The default does not match the field"
120
+ });
121
+ }
122
+ });
123
+ const createConfigurationValueType = (field) => {
124
+ if (field.type === "boolean") return z.boolean();
125
+ if (field.type === "array") {
126
+ const maxItems = Math.min(field.maxItems ?? MAX_CONFIGURATION_ARRAY_ITEMS, MAX_CONFIGURATION_ARRAY_ITEMS);
127
+ const items = z.array(createConfigurationValueType(field.items)).min(field.minItems ?? 0).max(maxItems);
128
+ return field.uniqueItems ? items.refine(uniqueItems, "Values must be unique") : items;
129
+ }
130
+ if ("enum" in field) return z.enum(field.enum);
131
+ if (field.type === "string") {
132
+ const maxLength = Math.min(field.maxLength ?? MAX_CONFIGURATION_STRING_LENGTH, MAX_CONFIGURATION_STRING_LENGTH);
133
+ const value = (field.format ? stringFormatTypes[field.format] : z.string()).min(field.minLength ?? 0).max(maxLength);
134
+ return field.pattern ? value.regex(new RegExp(field.pattern)) : value;
135
+ }
136
+ let value = field.type === "integer" ? z.int() : z.number();
137
+ if (field.minimum !== void 0) value = value.gte(field.minimum);
138
+ if (field.maximum !== void 0) value = value.lte(field.maximum);
139
+ if (field.exclusiveMinimum !== void 0) value = value.gt(field.exclusiveMinimum);
140
+ if (field.exclusiveMaximum !== void 0) value = value.lt(field.exclusiveMaximum);
141
+ if (field.multipleOf !== void 0) value = value.multipleOf(field.multipleOf);
142
+ return value;
143
+ };
144
+ /** Converts an authoring Zod object to the configuration subset; unsupported constructs throw. */
145
+ const toExtensionConfiguration = (schema) => {
146
+ const jsonSchema = z.toJSONSchema(schema, {
147
+ io: "input",
148
+ target: "draft-2020-12",
149
+ unrepresentable: "throw"
150
+ });
151
+ const result = extensionConfigurationType.safeParse({
152
+ additionalProperties: false,
153
+ ...jsonSchema
154
+ });
155
+ if (!result.success) throw new Error(`Unsupported configuration schema:\n${z.prettifyError(result.error)}`);
156
+ return result.data;
157
+ };
158
+ //#endregion
159
+ //#region src/index.ts
160
+ const isZodObject = (value) => "_zod" in value;
161
+ /**
162
+ * Defines the extension manifest, the default export of `andesine.config.ts`. A Zod
163
+ * configuration becomes its JSON Schema subset; `andesine extensions build` validates the rest.
164
+ */
165
+ const defineExtension = (definition) => {
166
+ const { configuration, ...manifest } = definition;
167
+ if (!configuration) return manifest;
168
+ return {
169
+ ...manifest,
170
+ configuration: isZodObject(configuration) ? toExtensionConfiguration(configuration) : configuration
171
+ };
172
+ };
173
+ const defineBuild = (options) => options;
174
+ //#endregion
175
+ export { defineBuild, defineExtension };
176
+
177
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../private/contracts/src/primitives/unique.ts","../../../private/contracts/src/extensions/configuration.ts","../src/index.ts"],"sourcesContent":["/** Whether every value is distinct. */\nconst uniqueItems = <T>(values: T[]): boolean => new Set(values).size === values.length;\n\nexport { uniqueItems };\n","import * as z from \"zod\";\nimport { uniqueItems } from \"../primitives/unique\";\n\ntype ExtensionConfiguration = z.output<typeof extensionConfigurationType>;\ntype ExtensionConfigurationField = ExtensionConfiguration[\"properties\"][string];\ntype ExtensionConfigurationItem = Exclude<ExtensionConfigurationField, { type: \"array\" }>;\ntype ExtensionConfigurationPrimitive = string | number | boolean;\ntype ExtensionConfigurationValue =\n ExtensionConfigurationPrimitive | ExtensionConfigurationPrimitive[];\n\nconst JSON_SCHEMA_DIALECT = \"https://json-schema.org/draft/2020-12/schema\";\nconst MAX_CONFIGURATION_FIELDS = 50;\nconst MAX_CONFIGURATION_STRING_LENGTH = 10_000;\nconst MAX_CONFIGURATION_ARRAY_ITEMS = 100;\nconst MAX_CONFIGURATION_ENUM_VALUES = 100;\nconst MAX_CONFIGURATION_PATTERN_LENGTH = 500;\nconst isValidPattern = (pattern: string): boolean => {\n try {\n new RegExp(pattern);\n\n return true;\n } catch {\n return false;\n }\n};\nconst fieldKeyType = z\n .string()\n .regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/, \"Use a letter, then letters, digits, or underscores\");\nconst lengthType = z.int().min(0).max(MAX_CONFIGURATION_STRING_LENGTH);\nconst metadata = {\n title: z.string().min(1).max(100).optional(),\n description: z.string().min(1).max(500).optional()\n};\nconst stringItemType = z.strictObject({\n type: z.literal(\"string\"),\n minLength: lengthType.optional(),\n maxLength: lengthType.optional(),\n pattern: z\n .string()\n .min(1)\n .max(MAX_CONFIGURATION_PATTERN_LENGTH)\n .refine(isValidPattern, \"Use a valid regular expression\")\n .optional(),\n format: z.enum([\"uri\", \"email\"]).optional()\n});\nconst enumItemType = z.strictObject({\n type: z.literal(\"string\"),\n enum: z\n .array(z.string().min(1).max(200))\n .min(1)\n .max(MAX_CONFIGURATION_ENUM_VALUES)\n .refine(uniqueItems, \"Options must be unique\")\n});\nconst numberItemType = z.strictObject({\n type: z.enum([\"number\", \"integer\"]),\n minimum: z.number().optional(),\n maximum: z.number().optional(),\n exclusiveMinimum: z.number().optional(),\n exclusiveMaximum: z.number().optional(),\n multipleOf: z.number().positive().optional()\n});\nconst booleanItemType = z.strictObject({ type: z.literal(\"boolean\") });\nconst stringFormatTypes = { uri: z.url(), email: z.email() };\nconst itemType = z.union([stringItemType, enumItemType, numberItemType, booleanItemType]);\nconst fieldType = z.union([\n stringItemType.extend({\n ...metadata,\n default: z.string().optional(),\n // Secret values are write-only and readable only by the extension backend.\n secret: z.literal(true).optional(),\n control: z.literal(\"textarea\").optional()\n }),\n enumItemType.extend({ ...metadata, default: z.string().optional() }),\n numberItemType.extend({ ...metadata, default: z.number().optional() }),\n booleanItemType.extend({ ...metadata, default: z.boolean().optional() }),\n z.strictObject({\n ...metadata,\n type: z.literal(\"array\"),\n items: itemType,\n minItems: z.int().min(0).max(MAX_CONFIGURATION_ARRAY_ITEMS).optional(),\n maxItems: z.int().min(0).max(MAX_CONFIGURATION_ARRAY_ITEMS).optional(),\n uniqueItems: z.boolean().optional(),\n default: z.array(z.union([z.string(), z.number(), z.boolean()])).optional()\n })\n]);\n/** Flat JSON Schema (draft 2020-12) subset that the host validates without running extension code. */\nconst extensionConfigurationType = z\n .strictObject({\n $schema: z.literal(JSON_SCHEMA_DIALECT).optional(),\n type: z.literal(\"object\"),\n properties: z\n .record(fieldKeyType, fieldType)\n .refine(\n (properties) => Object.keys(properties).length <= MAX_CONFIGURATION_FIELDS,\n `Use at most ${MAX_CONFIGURATION_FIELDS} fields`\n ),\n required: z.array(fieldKeyType).refine(uniqueItems, \"Fields must be unique\").optional(),\n additionalProperties: z.literal(false)\n })\n .superRefine((configuration, context) => {\n for (const [index, key] of (configuration.required || []).entries()) {\n if (!configuration.properties[key]) {\n context.addIssue({ code: \"custom\", path: [\"required\", index], message: \"Unknown field\" });\n }\n }\n\n for (const [key, field] of Object.entries(configuration.properties)) {\n const isSecretWithDefault = \"secret\" in field && field.secret && field.default !== undefined;\n\n if (isSecretWithDefault) {\n context.addIssue({\n code: \"custom\",\n path: [\"properties\", key, \"default\"],\n message: \"Secret fields cannot have defaults\"\n });\n } else if (field.default !== undefined) {\n const result = createConfigurationValueType(field).safeParse(field.default);\n\n if (!result.success) {\n context.addIssue({\n code: \"custom\",\n path: [\"properties\", key, \"default\"],\n message: \"The default does not match the field\"\n });\n }\n }\n }\n });\nconst createConfigurationValueType = (\n field: ExtensionConfigurationField | ExtensionConfigurationItem\n): z.ZodType<ExtensionConfigurationValue> => {\n if (field.type === \"boolean\") return z.boolean();\n\n if (field.type === \"array\") {\n const maxItems = Math.min(\n field.maxItems ?? MAX_CONFIGURATION_ARRAY_ITEMS,\n MAX_CONFIGURATION_ARRAY_ITEMS\n );\n const items = z\n .array(\n createConfigurationValueType(field.items) as z.ZodType<ExtensionConfigurationPrimitive>\n )\n .min(field.minItems ?? 0)\n .max(maxItems);\n\n return field.uniqueItems ? items.refine(uniqueItems, \"Values must be unique\") : items;\n }\n\n if (\"enum\" in field) return z.enum(field.enum as [string, ...string[]]);\n\n if (field.type === \"string\") {\n const maxLength = Math.min(\n field.maxLength ?? MAX_CONFIGURATION_STRING_LENGTH,\n MAX_CONFIGURATION_STRING_LENGTH\n );\n const value = (field.format ? stringFormatTypes[field.format] : z.string())\n .min(field.minLength ?? 0)\n .max(maxLength);\n\n return field.pattern ? value.regex(new RegExp(field.pattern)) : value;\n }\n\n let value = field.type === \"integer\" ? z.int() : z.number();\n\n if (field.minimum !== undefined) value = value.gte(field.minimum);\n\n if (field.maximum !== undefined) value = value.lte(field.maximum);\n\n if (field.exclusiveMinimum !== undefined) value = value.gt(field.exclusiveMinimum);\n\n if (field.exclusiveMaximum !== undefined) value = value.lt(field.exclusiveMaximum);\n\n if (field.multipleOf !== undefined) value = value.multipleOf(field.multipleOf);\n\n return value;\n};\n/** Validates stored values: known fields only; missing required fields are a state, not an error. */\nconst createConfigurationValuesType = (\n configuration: ExtensionConfiguration\n): z.ZodType<Record<string, ExtensionConfigurationValue>> => {\n const shape = Object.fromEntries(\n Object.entries(configuration.properties).map(([key, field]) => {\n return [key, createConfigurationValueType(field).optional()];\n })\n );\n\n return z.strictObject(shape) as z.ZodType<Record<string, ExtensionConfigurationValue>>;\n};\n/** Converts an authoring Zod object to the configuration subset; unsupported constructs throw. */\nconst toExtensionConfiguration = (schema: z.ZodObject): ExtensionConfiguration => {\n const jsonSchema = z.toJSONSchema(schema, {\n io: \"input\",\n target: \"draft-2020-12\",\n unrepresentable: \"throw\"\n });\n const result = extensionConfigurationType.safeParse({\n additionalProperties: false,\n ...jsonSchema\n });\n\n if (!result.success) {\n throw new Error(`Unsupported configuration schema:\\n${z.prettifyError(result.error)}`);\n }\n\n return result.data;\n};\n\nexport {\n MAX_CONFIGURATION_FIELDS,\n extensionConfigurationType,\n createConfigurationValueType,\n createConfigurationValuesType,\n toExtensionConfiguration\n};\nexport type { ExtensionConfiguration, ExtensionConfigurationField, ExtensionConfigurationValue };\n","import { type extensionManifestType } from \"@andesine/contracts/extensions\";\n// The configuration module alone, so the bundle carries no other contracts code.\nimport {\n type ExtensionConfiguration,\n toExtensionConfiguration\n} from \"@andesine/contracts/extensions/configuration\";\nimport type * as z from \"zod\";\n\ninterface ExtensionDefinition extends Omit<ExtensionManifestInput, \"configuration\"> {\n /** A Zod object of flat fields, or the configuration schema it converts to. */\n configuration?: z.ZodObject | ExtensionConfiguration;\n}\n/** Options of `andesine extensions build`, exported from the config file as `build`. */\ninterface ExtensionBuildOptions {\n /** The frontend module that exports the manifest's view entries (default: `src/frontend/index.tsx`). */\n frontend?: string;\n /** Custom icon sets: a set prefix and a directory of SVG files, e.g. `{ acme: \"./icons\" }`. */\n icons?: Record<string, string>;\n}\n\ntype ExtensionManifestInput = z.input<typeof extensionManifestType>;\n\nconst isZodObject = (value: object): value is z.ZodObject => \"_zod\" in value;\n/**\n * Defines the extension manifest, the default export of `andesine.config.ts`. A Zod\n * configuration becomes its JSON Schema subset; `andesine extensions build` validates the rest.\n */\nconst defineExtension = (definition: ExtensionDefinition): ExtensionManifestInput => {\n const { configuration, ...manifest } = definition;\n\n if (!configuration) return manifest;\n\n return {\n ...manifest,\n configuration: isZodObject(configuration)\n ? toExtensionConfiguration(configuration)\n : configuration\n };\n};\nconst defineBuild = (options: ExtensionBuildOptions): ExtensionBuildOptions => options;\n\nexport { defineExtension, defineBuild };\nexport type { ExtensionDefinition, ExtensionBuildOptions, ExtensionManifestInput };\n"],"mappings":"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"}