@andesine/extensions 0.5.0 → 0.6.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 +23 -0
- package/README.md +26 -6
- package/dist/index.d.ts +5 -11
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/solid.d.ts +66 -25
- package/dist/solid.js +28 -25
- package/dist/solid.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
- `Button` and `IconButton` take one `variant`: `primary`, `secondary`, `ghost`, `link`, or
|
|
6
|
+
`danger` (`color` is removed). Buttons default to `secondary` and icon buttons to `ghost`.
|
|
7
|
+
`Input` renders with the outlined look of Andesine's own fields.
|
|
8
|
+
- Remove `settingsSections` from the manifest: the host-rendered configuration form
|
|
9
|
+
covers settings, and custom extension UI belongs in panels.
|
|
10
|
+
- Add `description` and `icon` to element views; Andesine shows them in the editor's
|
|
11
|
+
slash menu and in the extension's settings.
|
|
12
|
+
- Block action views decide their UI: a view that renders nothing runs without UI
|
|
13
|
+
(the block menu shows a spinner until it closes), and one root `Dialog` or
|
|
14
|
+
`Menu` opens a modal or a menu where the block menu was.
|
|
15
|
+
- Add `Dialog` and `Menu` for any view. `Menu` is a dropdown menu with
|
|
16
|
+
`MenuTrigger` (or a right-click area with `contextMenu`), `MenuItem` (with
|
|
17
|
+
submenus and a `loading` spinner), `MenuGroup`, `MenuSeparator`, and custom
|
|
18
|
+
`MenuContent` items. Outside block actions, a dialog needs a recent interaction.
|
|
19
|
+
- Add `notify(text, type?)`, a host action that shows a short notification.
|
|
20
|
+
- Block action edits accept elements as `{ type: "element", attrs: { name, props } }`;
|
|
21
|
+
Andesine derives their tag.
|
|
22
|
+
- Fix block action and element view edits built from view props: `editor.replace`,
|
|
23
|
+
`editor.insertAfter`, and `editor.setElementProps` now send plain JSON instead of
|
|
24
|
+
store proxies, which could not be sent and stopped the extension.
|
|
25
|
+
|
|
3
26
|
## 0.5.0
|
|
4
27
|
|
|
5
28
|
- First release, versioned with `@andesine/sdk` and the `andesine` CLI.
|
package/README.md
CHANGED
|
@@ -50,7 +50,8 @@ not apply inside its slot.
|
|
|
50
50
|
Andesine to act for the extension. Call them from an interaction, for example a
|
|
51
51
|
button's `onClick`; otherwise they reject with `ExtensionRequestError` and the
|
|
52
52
|
code `user_interaction_required`. `openURL` asks the member to confirm URLs
|
|
53
|
-
outside the manifest's declared URLs.
|
|
53
|
+
outside the manifest's declared URLs. `notify(text, type?)` shows a short
|
|
54
|
+
notification with the extension's name and needs no interaction.
|
|
54
55
|
|
|
55
56
|
## Block actions and edits
|
|
56
57
|
|
|
@@ -58,7 +59,27 @@ A block action view receives `{ blocks }`: the selected blocks as editor JSON.
|
|
|
58
59
|
`useEditor()` returns `replace(content)`, `insertAfter(content)`, and `close()`
|
|
59
60
|
for block action views, and `setElementProps(props)` for element views (call it
|
|
60
61
|
from an interaction). Content must be blocks that are allowed where the
|
|
61
|
-
selection is; edits fail when the selected blocks changed meanwhile.
|
|
62
|
+
selection is; edits fail when the selected blocks changed meanwhile. Elements
|
|
63
|
+
are `{ type: "element", attrs: { name, props }, content }`; Andesine derives
|
|
64
|
+
their tag.
|
|
65
|
+
|
|
66
|
+
The view decides its UI at its root. A view that renders nothing runs without
|
|
67
|
+
UI: the block menu shows a spinner until the view calls `close()`, and Andesine
|
|
68
|
+
stops it after 30 seconds. One root `<Dialog title>` opens a modal, and one root
|
|
69
|
+
`<Menu>` opens a menu where the block menu was; anything else at the root stops
|
|
70
|
+
the action with an error. Escape, an outside click, or the close button close
|
|
71
|
+
them like `close()`; after a menu choice, the action runs on until it closes.
|
|
72
|
+
Choosing the action counts as an interaction, so host actions work right away.
|
|
73
|
+
|
|
74
|
+
## Dialogs and menus
|
|
75
|
+
|
|
76
|
+
`Dialog` and `Menu` work in any view. A dialog is shown while it is rendered and
|
|
77
|
+
calls `onClose` when dismissed; outside block actions, render it right after an
|
|
78
|
+
interaction, e.g. from `onClick`, otherwise it is not shown. A `Menu` is a
|
|
79
|
+
dropdown menu: `MenuTrigger` holds its trigger (with `contextMenu`, the area
|
|
80
|
+
that opens it on a right click), and its items are `MenuItem` (nested items
|
|
81
|
+
make a submenu; `loading` shows a spinner), `MenuGroup`, `MenuSeparator`, and
|
|
82
|
+
custom `MenuContent` with any content. `opened` and `onOpenChange` control it.
|
|
62
83
|
|
|
63
84
|
## API, permissions, and backend
|
|
64
85
|
|
|
@@ -79,7 +100,6 @@ the backend verifies with Andesine.
|
|
|
79
100
|
|
|
80
101
|
## Examples
|
|
81
102
|
|
|
82
|
-
|
|
83
|
-
views, a block action that calls its backend,
|
|
84
|
-
|
|
85
|
-
feature.
|
|
103
|
+
`npx andesine extensions init` creates a project with samples of the main features: element
|
|
104
|
+
views, a block action, a panel that calls its backend, settings, and webhooks. Published
|
|
105
|
+
extensions in the public registry show complete projects.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
//#region ../../private/contracts/src/extensions/configuration.d.ts
|
|
3
3
|
type ExtensionConfiguration = z.output<typeof extensionConfigurationType>;
|
|
4
|
-
/** Flat JSON Schema
|
|
4
|
+
/** Flat JSON Schema 2020-12 subset; the host validates it without running extension code. */
|
|
5
5
|
declare const extensionConfigurationType: z.ZodObject<{
|
|
6
6
|
$schema: z.ZodOptional<z.ZodLiteral<"https://json-schema.org/draft/2020-12/schema">>;
|
|
7
7
|
type: z.ZodLiteral<"object">;
|
|
@@ -86,6 +86,8 @@ declare const extensionManifestType: z.ZodObject<{
|
|
|
86
86
|
elementViews: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
87
87
|
id: z.ZodString;
|
|
88
88
|
name: z.ZodString;
|
|
89
|
+
description: z.ZodOptional<z.ZodString>;
|
|
90
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
89
91
|
element: z.ZodString;
|
|
90
92
|
entry: z.ZodString;
|
|
91
93
|
descendants: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -118,11 +120,6 @@ declare const extensionManifestType: z.ZodObject<{
|
|
|
118
120
|
name: z.ZodString;
|
|
119
121
|
icon: z.ZodString;
|
|
120
122
|
}, 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
123
|
name: z.ZodString;
|
|
127
124
|
version: z.ZodString;
|
|
128
125
|
displayName: z.ZodString;
|
|
@@ -278,16 +275,13 @@ interface ExtensionDefinition extends Omit<ExtensionManifestInput, "configuratio
|
|
|
278
275
|
}
|
|
279
276
|
/** Options of `andesine extensions build`, exported from the config file as `build`. */
|
|
280
277
|
interface ExtensionBuildOptions {
|
|
281
|
-
/** The
|
|
278
|
+
/** The module exporting the manifest's view entries (default: `src/frontend/index.tsx`). */
|
|
282
279
|
frontend?: string;
|
|
283
280
|
/** Custom icon sets: a set prefix and a directory of SVG files, e.g. `{ acme: "./icons" }`. */
|
|
284
281
|
icons?: Record<string, string>;
|
|
285
282
|
}
|
|
286
283
|
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
|
-
*/
|
|
284
|
+
/** Defines the `andesine.config.ts` manifest; a Zod configuration becomes JSON Schema. */
|
|
291
285
|
declare const defineExtension: (definition: ExtensionDefinition) => ExtensionManifestInput;
|
|
292
286
|
declare const defineBuild: (options: ExtensionBuildOptions) => ExtensionBuildOptions;
|
|
293
287
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
//#region ../../private/contracts/src/primitives/unique.ts
|
|
3
|
-
/** Whether every value is distinct. */
|
|
4
3
|
const uniqueItems = (values) => new Set(values).size === values.length;
|
|
5
4
|
//#endregion
|
|
6
5
|
//#region ../../private/contracts/src/extensions/configuration.ts
|
|
@@ -86,7 +85,7 @@ const fieldType = z.union([
|
|
|
86
85
|
])).optional()
|
|
87
86
|
})
|
|
88
87
|
]);
|
|
89
|
-
/** Flat JSON Schema
|
|
88
|
+
/** Flat JSON Schema 2020-12 subset; the host validates it without running extension code. */
|
|
90
89
|
const extensionConfigurationType = z.strictObject({
|
|
91
90
|
$schema: z.literal(JSON_SCHEMA_DIALECT).optional(),
|
|
92
91
|
type: z.literal("object"),
|
|
@@ -158,10 +157,7 @@ const toExtensionConfiguration = (schema) => {
|
|
|
158
157
|
//#endregion
|
|
159
158
|
//#region src/index.ts
|
|
160
159
|
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
|
-
*/
|
|
160
|
+
/** Defines the `andesine.config.ts` manifest; a Zod configuration becomes JSON Schema. */
|
|
165
161
|
const defineExtension = (definition) => {
|
|
166
162
|
const { configuration, ...manifest } = definition;
|
|
167
163
|
if (!configuration) return manifest;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../private/contracts/src/primitives/unique.ts","../../../private/contracts/src/extensions/configuration.ts","../src/index.ts"],"sourcesContent":["const 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 2020-12 subset; the host validates it 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/** Known fields only; missing required fields are a state, not a validation 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 module exporting 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/** Defines the `andesine.config.ts` manifest; a Zod configuration becomes JSON Schema. */\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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"}
|
package/dist/solid.d.ts
CHANGED
|
@@ -25,8 +25,8 @@ interface StyleProps {
|
|
|
25
25
|
class?: string;
|
|
26
26
|
}
|
|
27
27
|
interface ButtonBaseProps {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
/** Defaults to `secondary` for buttons and `ghost` for icon buttons. */
|
|
29
|
+
variant?: "primary" | "secondary" | "ghost" | "link" | "danger";
|
|
30
30
|
size?: "small" | "medium";
|
|
31
31
|
disabled?: boolean;
|
|
32
32
|
onClick?(): void;
|
|
@@ -124,6 +124,50 @@ interface ColorInputProps {
|
|
|
124
124
|
interface TooltipProps extends ParentProps {
|
|
125
125
|
content: string;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* A modal, shown while rendered. Outside block actions, render it right after an interaction,
|
|
129
|
+
* e.g. from `onClick`; otherwise it isn't shown. In a block action, closing it closes the action.
|
|
130
|
+
*/
|
|
131
|
+
interface DialogProps extends ParentProps {
|
|
132
|
+
title: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
size?: Size;
|
|
135
|
+
/** Whether Escape, an outside click, or the close button call `onClose` (default true). */
|
|
136
|
+
dismissible?: boolean;
|
|
137
|
+
onClose?(): void;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A dropdown menu of `MenuItem`, `MenuGroup`, `MenuSeparator`, and `MenuContent` children,
|
|
141
|
+
* opened by its `MenuTrigger`, or with `contextMenu` by a right click on it. At a block action
|
|
142
|
+
* view's root it needs no trigger: it opens where the block menu was, and closing it closes the action.
|
|
143
|
+
*/
|
|
144
|
+
interface MenuProps extends ParentProps {
|
|
145
|
+
/** The title of the bottom sheet on small screens. */
|
|
146
|
+
title?: string;
|
|
147
|
+
/** Controls the open state; without it, the menu opens and closes on its own. */
|
|
148
|
+
opened?: boolean;
|
|
149
|
+
contextMenu?: boolean;
|
|
150
|
+
placement?: "bottom-start" | "bottom-end" | "top-start" | "top-end" | "right-start" | "left-start";
|
|
151
|
+
size?: "small" | "medium";
|
|
152
|
+
onOpenChange?(opened: boolean): void;
|
|
153
|
+
}
|
|
154
|
+
/** Nested items make a submenu, which replaces `onSelect`. */
|
|
155
|
+
interface MenuItemProps extends ParentProps {
|
|
156
|
+
label: string;
|
|
157
|
+
icon?: string;
|
|
158
|
+
/** Shown next to the label; handle the keys yourself. */
|
|
159
|
+
shortcut?: string;
|
|
160
|
+
color?: "base" | "danger";
|
|
161
|
+
disabled?: boolean;
|
|
162
|
+
/** A spinner replaces the icon, and the item is disabled. */
|
|
163
|
+
loading?: boolean;
|
|
164
|
+
selected?: boolean;
|
|
165
|
+
closeOnSelect?: boolean;
|
|
166
|
+
onSelect?(): void;
|
|
167
|
+
}
|
|
168
|
+
interface MenuGroupProps extends ParentProps {
|
|
169
|
+
label?: string;
|
|
170
|
+
}
|
|
127
171
|
interface SettingProps extends ParentProps {
|
|
128
172
|
label: string;
|
|
129
173
|
description?: string;
|
|
@@ -217,6 +261,14 @@ declare const Toggle: (props: ToggleProps) => JSX.Element;
|
|
|
217
261
|
declare const ToggleGroup: (props: ToggleGroupProps) => JSX.Element;
|
|
218
262
|
declare const ColorInput: (props: ColorInputProps) => JSX.Element;
|
|
219
263
|
declare const Tooltip: (props: TooltipProps) => JSX.Element;
|
|
264
|
+
declare const Dialog: (props: DialogProps) => JSX.Element;
|
|
265
|
+
declare const Menu: (props: MenuProps) => JSX.Element;
|
|
266
|
+
declare const MenuTrigger: (props: ParentProps) => JSX.Element;
|
|
267
|
+
declare const MenuItem: (props: MenuItemProps) => JSX.Element;
|
|
268
|
+
declare const MenuGroup: (props: MenuGroupProps) => JSX.Element;
|
|
269
|
+
declare const MenuSeparator: (props: EmptyProps) => JSX.Element;
|
|
270
|
+
/** A custom menu item: any content, e.g. a result or a form. */
|
|
271
|
+
declare const MenuContent: (props: ParentProps) => JSX.Element;
|
|
220
272
|
declare const Setting: (props: SettingProps) => JSX.Element;
|
|
221
273
|
declare const List: (props: ListProps) => JSX.Element;
|
|
222
274
|
declare const Card: (props: CardProps) => JSX.Element;
|
|
@@ -237,10 +289,7 @@ declare const ContentSlot: (props: EmptyProps) => JSX.Element;
|
|
|
237
289
|
//#endregion
|
|
238
290
|
//#region src/solid/runtime.d.ts
|
|
239
291
|
type ExtensionViews = Record<string, Component<never>>;
|
|
240
|
-
/**
|
|
241
|
-
* Starts the extension frontend in its sandbox worker. `entries` maps the view entries that the
|
|
242
|
-
* manifest names to Solid components; each view receives its host props as reactive props.
|
|
243
|
-
*/
|
|
292
|
+
/** Starts the frontend worker; `entries` maps manifest view entries to Solid components. */
|
|
244
293
|
declare const startExtension: (entries: ExtensionViews) => void;
|
|
245
294
|
//#endregion
|
|
246
295
|
//#region src/solid/actions.d.ts
|
|
@@ -250,6 +299,8 @@ declare const copyText: (text: string) => Promise<void>;
|
|
|
250
299
|
declare const downloadFile: (name: string, text: string, type?: string) => Promise<void>;
|
|
251
300
|
/** Opens an `https:` URL in a new tab; URLs outside the declared URLs need confirmation. */
|
|
252
301
|
declare const openURL: (url: string) => Promise<void>;
|
|
302
|
+
/** Shows a short notification, e.g. the result of a block action without UI; it needs no interaction. */
|
|
303
|
+
declare const notify: (text: string, type?: "success" | "error") => Promise<void>;
|
|
253
304
|
//#endregion
|
|
254
305
|
//#region src/solid/requests.d.ts
|
|
255
306
|
/** A host request failed; `code` says why, for example `user_interaction_required`. */
|
|
@@ -259,7 +310,7 @@ declare class ExtensionRequestError extends Error {
|
|
|
259
310
|
}
|
|
260
311
|
//#endregion
|
|
261
312
|
//#region src/solid/editor.d.ts
|
|
262
|
-
/**
|
|
313
|
+
/** Editor JSON block, e.g. `{ type: "paragraph", content: [{ type: "text", text: "Hi" }] }`. */
|
|
263
314
|
interface ContentNode {
|
|
264
315
|
type: string;
|
|
265
316
|
attrs?: Record<string, JSONValue>;
|
|
@@ -280,10 +331,7 @@ interface ViewEditor {
|
|
|
280
331
|
/** Block action views: closes the view. */
|
|
281
332
|
close(): Promise<void>;
|
|
282
333
|
}
|
|
283
|
-
/**
|
|
284
|
-
* Edits the document from the current view. Andesine validates every edit and applies it as the
|
|
285
|
-
* member's own edit; edits reject with `ExtensionRequestError` when they do not fit.
|
|
286
|
-
*/
|
|
334
|
+
/** Edits the document as the member; invalid edits reject with `ExtensionRequestError`. */
|
|
287
335
|
declare const useEditor: () => ViewEditor;
|
|
288
336
|
//#endregion
|
|
289
337
|
//#region src/solid/storage.d.ts
|
|
@@ -306,9 +354,8 @@ interface StoragePage {
|
|
|
306
354
|
hasMore: boolean;
|
|
307
355
|
}
|
|
308
356
|
/**
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* wins. Failed requests reject with `ExtensionRequestError`.
|
|
357
|
+
* JSON key-value storage shared with the backend; the last write wins.
|
|
358
|
+
* Limits: 256-character keys, 128 KiB values, 10,000 entries, and 10 MiB in total.
|
|
312
359
|
*/
|
|
313
360
|
declare const storage: {
|
|
314
361
|
/** The entry, or null when the key is not set. */
|
|
@@ -326,31 +373,25 @@ declare const storage: {
|
|
|
326
373
|
//#region src/solid/context.d.ts
|
|
327
374
|
/** The origin `apiFetch` accepts; use it as the SDK `baseURL`. */
|
|
328
375
|
declare const EXTENSION_API_URL = "https://api.andesine.invalid";
|
|
329
|
-
/**
|
|
330
|
-
* The configuration's non-secret values, with defaults for unset fields (reactive). Secret
|
|
331
|
-
* fields are readable only by the extension backend.
|
|
332
|
-
*/
|
|
376
|
+
/** The non-secret configuration values with defaults (reactive); only the backend reads secrets. */
|
|
333
377
|
declare const useConfiguration: <T extends Record<string, JSONValue> = Record<string, JSONValue>>() => () => Partial<T>;
|
|
334
378
|
/** The member's effective permissions for this extension (reactive), e.g. `read:entries`. */
|
|
335
379
|
declare const usePermissions: () => import("solid-js").Accessor<string[]>;
|
|
336
380
|
/** Whether the effective permissions include `permission`; a write permission includes its read. */
|
|
337
381
|
declare const hasPermission: (permission: string) => boolean;
|
|
338
382
|
/**
|
|
339
|
-
*
|
|
340
|
-
* session, within the effective permissions. Forbidden operations answer with HTTP 403.
|
|
383
|
+
* SDK `fetch` that calls Andesine with the member's session; forbidden operations return HTTP 403.
|
|
341
384
|
* @example createClient({ baseURL: EXTENSION_API_URL, fetch: apiFetch })
|
|
342
385
|
*/
|
|
343
386
|
declare const apiFetch: typeof fetch;
|
|
344
387
|
/** The extension's own backend, as declared in the manifest. */
|
|
345
388
|
declare const backend: {
|
|
346
389
|
/**
|
|
347
|
-
* Fetches a
|
|
348
|
-
*
|
|
349
|
-
* backend verifies them with `verifySession` from `@andesine/sdk/extensions`. Its CORS
|
|
350
|
-
* configuration must allow these headers.
|
|
390
|
+
* Fetches a backend path with `Authorization`, `Andesine-Extension`, and `Andesine-Instance`
|
|
391
|
+
* headers (CORS must allow them); verify them with `verifySession` of `@andesine/sdk/extensions`.
|
|
351
392
|
*/
|
|
352
393
|
fetch(path: string, init?: RequestInit): Promise<Response>;
|
|
353
394
|
};
|
|
354
395
|
//#endregion
|
|
355
|
-
export { Badge, type BadgeProps, Box, type BoxProps, Button, type ButtonProps, Callout, type CalloutProps, Card, type CardProps, Checkbox, type ChoiceProps, Code, CodeBlock, type CodeBlockProps, ColorInput, type ColorInputProps, Combobox, type ContentNode, ContentSlot, Disclosure, type DisclosureProps, Divider, EXTENSION_API_URL, type EmptyProps, ExtensionRequestError, Figure, type FigureProps, Grid, type GridProps, Heading, type HeadingProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, Link, type LinkProps, List, type ListProps, type Option, type ParentProps, ScrollArea, type ScrollAreaProps, Select, Setting, type SettingProps, Skeleton, type SkeletonProps, Spinner, type SpinnerProps, Stack, type StackProps, Step, type StepProps, Steps, type StorageEntry, type StorageListOptions, type StoragePage, type StyleProps, Tab, type TabProps, Tabs, type TabsProps, Text, type TextProps, Textarea, type TextareaProps, TimeAgo, type TimeAgoProps, Toggle, ToggleGroup, type ToggleGroupProps, type ToggleProps, Tooltip, type TooltipProps, Tree, type TreeItem, type TreeProps, type ViewEditor, apiFetch, backend, copyText, downloadFile, hasPermission, openURL, startExtension, storage, useConfiguration, useEditor, usePermissions };
|
|
396
|
+
export { Badge, type BadgeProps, Box, type BoxProps, Button, type ButtonProps, Callout, type CalloutProps, Card, type CardProps, Checkbox, type ChoiceProps, Code, CodeBlock, type CodeBlockProps, ColorInput, type ColorInputProps, Combobox, type ContentNode, ContentSlot, Dialog, type DialogProps, Disclosure, type DisclosureProps, Divider, EXTENSION_API_URL, type EmptyProps, ExtensionRequestError, Figure, type FigureProps, Grid, type GridProps, Heading, type HeadingProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, Link, type LinkProps, List, type ListProps, Menu, MenuContent, MenuGroup, type MenuGroupProps, MenuItem, type MenuItemProps, type MenuProps, MenuSeparator, MenuTrigger, type Option, type ParentProps, ScrollArea, type ScrollAreaProps, Select, Setting, type SettingProps, Skeleton, type SkeletonProps, Spinner, type SpinnerProps, Stack, type StackProps, Step, type StepProps, Steps, type StorageEntry, type StorageListOptions, type StoragePage, type StyleProps, Tab, type TabProps, Tabs, type TabsProps, Text, type TextProps, Textarea, type TextareaProps, TimeAgo, type TimeAgoProps, Toggle, ToggleGroup, type ToggleGroupProps, type ToggleProps, Tooltip, type TooltipProps, Tree, type TreeItem, type TreeProps, type ViewEditor, apiFetch, backend, copyText, downloadFile, hasPermission, notify, openURL, startExtension, storage, useConfiguration, useEditor, usePermissions };
|
|
356
397
|
//# sourceMappingURL=solid.d.ts.map
|
package/dist/solid.js
CHANGED
|
@@ -199,6 +199,14 @@ const Toggle = hostComponent("Toggle");
|
|
|
199
199
|
const ToggleGroup = hostComponent("ToggleGroup");
|
|
200
200
|
const ColorInput = hostComponent("ColorInput");
|
|
201
201
|
const Tooltip = hostComponent("Tooltip");
|
|
202
|
+
const Dialog = hostComponent("Dialog");
|
|
203
|
+
const Menu = hostComponent("Menu");
|
|
204
|
+
const MenuTrigger = hostComponent("MenuTrigger");
|
|
205
|
+
const MenuItem = hostComponent("MenuItem");
|
|
206
|
+
const MenuGroup = hostComponent("MenuGroup");
|
|
207
|
+
const MenuSeparator = hostComponent("MenuSeparator");
|
|
208
|
+
/** A custom menu item: any content, e.g. a result or a form. */
|
|
209
|
+
const MenuContent = hostComponent("MenuContent");
|
|
202
210
|
const Setting = hostComponent("Setting");
|
|
203
211
|
const List = hostComponent("List");
|
|
204
212
|
const Card = hostComponent("Card");
|
|
@@ -254,13 +262,10 @@ const settleRequest = (id, result, error) => {
|
|
|
254
262
|
//#endregion
|
|
255
263
|
//#region src/solid/editor.ts
|
|
256
264
|
const ViewContext = createContext();
|
|
257
|
-
/**
|
|
258
|
-
* Edits the document from the current view. Andesine validates every edit and applies it as the
|
|
259
|
-
* member's own edit; edits reject with `ExtensionRequestError` when they do not fit.
|
|
260
|
-
*/
|
|
265
|
+
/** Edits the document as the member; invalid edits reject with `ExtensionRequestError`. */
|
|
261
266
|
const useEditor = () => {
|
|
262
267
|
const viewID = useContext(ViewContext) ?? "";
|
|
263
|
-
const asJSON = (
|
|
268
|
+
const asJSON = (value) => JSON.parse(JSON.stringify(value));
|
|
264
269
|
return {
|
|
265
270
|
replace: async (content) => {
|
|
266
271
|
await request("editor.replace", {
|
|
@@ -277,7 +282,7 @@ const useEditor = () => {
|
|
|
277
282
|
setElementProps: async (props) => {
|
|
278
283
|
await request("editor.setElementProps", {
|
|
279
284
|
viewID,
|
|
280
|
-
props
|
|
285
|
+
props: asJSON(props)
|
|
281
286
|
});
|
|
282
287
|
},
|
|
283
288
|
close: async () => {
|
|
@@ -310,10 +315,7 @@ const updateSession = (message) => {
|
|
|
310
315
|
};
|
|
311
316
|
setSession(message.token);
|
|
312
317
|
};
|
|
313
|
-
/**
|
|
314
|
-
* The configuration's non-secret values, with defaults for unset fields (reactive). Secret
|
|
315
|
-
* fields are readable only by the extension backend.
|
|
316
|
-
*/
|
|
318
|
+
/** The non-secret configuration values with defaults (reactive); only the backend reads secrets. */
|
|
317
319
|
const useConfiguration = () => {
|
|
318
320
|
return configuration;
|
|
319
321
|
};
|
|
@@ -325,8 +327,7 @@ const hasPermission = (permission) => {
|
|
|
325
327
|
return permissions().some((granted) => granted === permission || granted === write);
|
|
326
328
|
};
|
|
327
329
|
/**
|
|
328
|
-
*
|
|
329
|
-
* session, within the effective permissions. Forbidden operations answer with HTTP 403.
|
|
330
|
+
* SDK `fetch` that calls Andesine with the member's session; forbidden operations return HTTP 403.
|
|
330
331
|
* @example createClient({ baseURL: EXTENSION_API_URL, fetch: apiFetch })
|
|
331
332
|
*/
|
|
332
333
|
const apiFetch = async (input, init) => {
|
|
@@ -367,10 +368,8 @@ const waitForSession = () => {
|
|
|
367
368
|
/** The extension's own backend, as declared in the manifest. */
|
|
368
369
|
const backend = {
|
|
369
370
|
/**
|
|
370
|
-
* Fetches a
|
|
371
|
-
*
|
|
372
|
-
* backend verifies them with `verifySession` from `@andesine/sdk/extensions`. Its CORS
|
|
373
|
-
* configuration must allow these headers.
|
|
371
|
+
* Fetches a backend path with `Authorization`, `Andesine-Extension`, and `Andesine-Instance`
|
|
372
|
+
* headers (CORS must allow them); verify them with `verifySession` of `@andesine/sdk/extensions`.
|
|
374
373
|
*/
|
|
375
374
|
async fetch(path, init) {
|
|
376
375
|
const base = backendURL();
|
|
@@ -398,10 +397,7 @@ let started = false;
|
|
|
398
397
|
const getErrorMessage = (error) => {
|
|
399
398
|
return String(error instanceof Error ? error.message : error).slice(0, 500);
|
|
400
399
|
};
|
|
401
|
-
/**
|
|
402
|
-
* Starts the extension frontend in its sandbox worker. `entries` maps the view entries that the
|
|
403
|
-
* manifest names to Solid components; each view receives its host props as reactive props.
|
|
404
|
-
*/
|
|
400
|
+
/** Starts the frontend worker; `entries` maps manifest view entries to Solid components. */
|
|
405
401
|
const startExtension = (entries) => {
|
|
406
402
|
if (started) return;
|
|
407
403
|
started = true;
|
|
@@ -415,8 +411,9 @@ const startExtension = (entries) => {
|
|
|
415
411
|
...body,
|
|
416
412
|
protocol: PROTOCOL,
|
|
417
413
|
generation,
|
|
418
|
-
sequence
|
|
414
|
+
sequence
|
|
419
415
|
});
|
|
416
|
+
sequence += 1;
|
|
420
417
|
};
|
|
421
418
|
const sendError = (error, viewID) => {
|
|
422
419
|
send({
|
|
@@ -519,12 +516,18 @@ const downloadFile = async (name, text, type) => {
|
|
|
519
516
|
const openURL = async (url) => {
|
|
520
517
|
await request("host.openURL", { url });
|
|
521
518
|
};
|
|
519
|
+
/** Shows a short notification, e.g. the result of a block action without UI; it needs no interaction. */
|
|
520
|
+
const notify = async (text, type) => {
|
|
521
|
+
await request("host.notify", type ? {
|
|
522
|
+
text,
|
|
523
|
+
type
|
|
524
|
+
} : { text });
|
|
525
|
+
};
|
|
522
526
|
//#endregion
|
|
523
527
|
//#region src/solid/storage.ts
|
|
524
528
|
/**
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
* wins. Failed requests reject with `ExtensionRequestError`.
|
|
529
|
+
* JSON key-value storage shared with the backend; the last write wins.
|
|
530
|
+
* Limits: 256-character keys, 128 KiB values, 10,000 entries, and 10 MiB in total.
|
|
528
531
|
*/
|
|
529
532
|
const storage = {
|
|
530
533
|
/** The entry, or null when the key is not set. */
|
|
@@ -550,6 +553,6 @@ const storage = {
|
|
|
550
553
|
}
|
|
551
554
|
};
|
|
552
555
|
//#endregion
|
|
553
|
-
export { Badge, Box, Button, Callout, Card, Checkbox, Code, CodeBlock, ColorInput, Combobox, ContentSlot, Disclosure, Divider, EXTENSION_API_URL, ExtensionRequestError, Figure, Grid, Heading, Icon, IconButton, Input, Link, List, ScrollArea, Select, Setting, Skeleton, Spinner, Stack, Step, Steps, Tab, Tabs, Text, Textarea, TimeAgo, Toggle, ToggleGroup, Tooltip, Tree, apiFetch, backend, copyText, createComponent, createElement, createTextNode, downloadFile, effect, hasPermission, insert, insertNode, memo, mergeProps, openURL, render, setProp, spread, startExtension, storage, use, useConfiguration, useEditor, usePermissions };
|
|
556
|
+
export { Badge, Box, Button, Callout, Card, Checkbox, Code, CodeBlock, ColorInput, Combobox, ContentSlot, Dialog, Disclosure, Divider, EXTENSION_API_URL, ExtensionRequestError, Figure, Grid, Heading, Icon, IconButton, Input, Link, List, Menu, MenuContent, MenuGroup, MenuItem, MenuSeparator, MenuTrigger, ScrollArea, Select, Setting, Skeleton, Spinner, Stack, Step, Steps, Tab, Tabs, Text, Textarea, TimeAgo, Toggle, ToggleGroup, Tooltip, Tree, apiFetch, backend, copyText, createComponent, createElement, createTextNode, downloadFile, effect, hasPermission, insert, insertNode, memo, mergeProps, notify, openURL, render, setProp, spread, startExtension, storage, use, useConfiguration, useEditor, usePermissions };
|
|
554
557
|
|
|
555
558
|
//# sourceMappingURL=solid.js.map
|
package/dist/solid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solid.js","names":["nextID"],"sources":["../src/solid/renderer.ts","../src/solid/components.ts","../src/solid/requests.ts","../src/solid/editor.ts","../src/solid/context.ts","../src/solid/runtime.ts","../src/solid/actions.ts","../src/solid/storage.ts"],"sourcesContent":["import { createRenderer } from \"solid-js/universal\";\nimport type { JSONValue, ViewPatch } from \"./protocol\";\n\n/** A node of the serialized view tree; it mirrors the host tree, which has the real DOM. */\ninterface ViewNode {\n id: number;\n type: string;\n parent?: ViewNode;\n children: ViewNode[];\n callbacks: Map<string, number>;\n}\n\ntype PatchSender = (patches: ViewPatch[]) => void;\n\nconst MAX_PATCHES_PER_MESSAGE = 1000;\nconst MAX_JSON_DEPTH = 8;\nconst callbacks = new Map<number, (...args: unknown[]) => unknown>();\n// Solid moves a node by removing and inserting it again, but the host deletes removed nodes,\n// so removals wait for the flush and apply only to nodes that are still detached.\nconst removedNodes = new Set<ViewNode>();\n\nlet nextID = 1;\nlet queue: ViewPatch[] = [];\nlet flushScheduled = false;\nlet sendPatches: PatchSender = () => {};\n\nconst releaseCallbacks = (node: ViewNode): void => {\n for (const callback of node.callbacks.values()) callbacks.delete(callback);\n\n node.callbacks.clear();\n node.children.forEach(releaseCallbacks);\n};\nconst flush = (): void => {\n for (const node of removedNodes) {\n if (node.parent) continue;\n\n releaseCallbacks(node);\n queue.push({ op: \"remove\", id: node.id });\n }\n\n removedNodes.clear();\n\n const patches = queue;\n\n flushScheduled = false;\n queue = [];\n\n for (let index = 0; index < patches.length; index += MAX_PATCHES_PER_MESSAGE) {\n sendPatches(patches.slice(index, index + MAX_PATCHES_PER_MESSAGE));\n }\n};\nconst scheduleFlush = (): void => {\n if (flushScheduled) return;\n\n flushScheduled = true;\n queueMicrotask(flush);\n};\nconst queuePatch = (patch: ViewPatch): void => {\n queue.push(patch);\n scheduleFlush();\n};\nconst createNode = (type: string): ViewNode => {\n return { id: nextID++, type, children: [], callbacks: new Map() };\n};\nconst toJSONValue = (value: unknown, depth = 0): JSONValue => {\n const isPrimitive =\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"boolean\" ||\n (typeof value === \"number\" && Number.isFinite(value));\n\n if (isPrimitive) return value as JSONValue;\n\n if (depth >= MAX_JSON_DEPTH) throw new TypeError(\"Extension props are nested too deeply\");\n\n if (Array.isArray(value)) return value.map((item) => toJSONValue(item, depth + 1));\n\n if (Object.getPrototypeOf(value) === Object.prototype) {\n return Object.fromEntries(\n Object.entries(value as object).map(([key, item]) => [key, toJSONValue(item, depth + 1)])\n );\n }\n\n throw new TypeError(\"Extension props must be JSON values or functions\");\n};\nconst detach = (node: ViewNode): void => {\n const siblings = node.parent?.children;\n\n if (siblings) siblings.splice(siblings.indexOf(node), 1);\n\n node.parent = undefined;\n};\nconst renderer = createRenderer<ViewNode>({\n createElement(type) {\n const node = createNode(type);\n\n queuePatch({ op: \"create\", id: node.id, component: type });\n\n return node;\n },\n // Solid passes numbers from JSX expressions as they are; the protocol carries strings.\n createTextNode(value) {\n const node = createNode(\"#text\");\n\n queuePatch({ op: \"text\", id: node.id, value: String(value) });\n\n return node;\n },\n replaceText(node, value) {\n queuePatch({ op: \"setText\", id: node.id, value: String(value) });\n },\n isTextNode(node) {\n return node.type === \"#text\";\n },\n setProperty(node, name, value) {\n const previous = node.callbacks.get(name);\n\n if (previous) {\n callbacks.delete(previous);\n node.callbacks.delete(name);\n }\n\n if (typeof value === \"function\") {\n const callback = nextID++;\n\n callbacks.set(callback, value as (...args: unknown[]) => unknown);\n node.callbacks.set(name, callback);\n queuePatch({ op: \"callback\", id: node.id, name, callback });\n } else if (value === undefined) {\n queuePatch({ op: \"unset\", id: node.id, name });\n } else {\n queuePatch({ op: \"prop\", id: node.id, name, value: toJSONValue(value) });\n }\n },\n insertNode(parent, node, anchor) {\n const index = anchor ? parent.children.indexOf(anchor) : -1;\n\n detach(node);\n node.parent = parent;\n\n if (index >= 0) {\n parent.children.splice(index, 0, node);\n } else {\n parent.children.push(node);\n }\n\n queuePatch({ op: \"insert\", parent: parent.id, id: node.id, before: anchor?.id ?? null });\n },\n removeNode(_parent, node) {\n detach(node);\n removedNodes.add(node);\n scheduleFlush();\n },\n getParentNode(node) {\n return node.parent;\n },\n getFirstChild(node) {\n return node.children[0];\n },\n getNextSibling(node) {\n const siblings = node.parent?.children;\n\n return siblings?.[siblings.indexOf(node) + 1];\n }\n});\nconst createViewRoot = (viewID: string): ViewNode => {\n const node = createNode(\"#root\");\n\n queuePatch({ op: \"root\", id: node.id, viewID });\n\n return node;\n};\nconst removeViewRoot = (node: ViewNode): void => {\n releaseCallbacks(node);\n queuePatch({ op: \"remove\", id: node.id });\n};\nconst setPatchSender = (sender: PatchSender): void => {\n sendPatches = sender;\n};\nconst getCallback = (callback: number): ((...args: unknown[]) => unknown) | undefined => {\n return callbacks.get(callback);\n};\n\nexport const {\n render,\n effect,\n memo,\n createComponent,\n createElement,\n createTextNode,\n insertNode,\n insert,\n spread,\n setProp,\n mergeProps,\n use\n} = renderer;\nexport { createViewRoot, removeViewRoot, setPatchSender, getCallback };\nexport type { ViewNode };\n","import type { JSX } from \"solid-js\";\nimport { createElement, spread } from \"./renderer\";\n\ninterface ParentProps {\n children?: JSX.Element;\n}\ninterface StyleProps {\n /** Utility classes of the Andesine UnoCSS preset; the extension build generates their CSS. */\n class?: string;\n}\ninterface ButtonBaseProps {\n variant?: \"text\" | \"solid\" | \"outlined\";\n color?: \"base\" | \"contrast\" | \"primary\" | \"danger\" | \"success\";\n size?: \"small\" | \"medium\";\n disabled?: boolean;\n onClick?(): void;\n}\ninterface FieldProps {\n value?: string;\n placeholder?: string;\n label?: string;\n disabled?: boolean;\n onInput?(value: string): void;\n}\ninterface Option {\n value: string;\n label: string;\n}\ninterface ChoiceProps {\n value?: string;\n options: Option[];\n placeholder?: string;\n disabled?: boolean;\n onChange?(value: string): void;\n}\ninterface ToggleProps {\n checked?: boolean;\n label?: string;\n disabled?: boolean;\n onChange?(checked: boolean): void;\n}\ninterface StackProps extends ParentProps, StyleProps {\n direction?: \"row\" | \"column\";\n gap?: Gap;\n align?: \"start\" | \"center\" | \"end\" | \"stretch\";\n justify?: \"start\" | \"center\" | \"end\" | \"between\";\n wrap?: boolean;\n}\ninterface GridProps extends ParentProps, StyleProps {\n columns?: number;\n gap?: Gap;\n}\ninterface BoxProps extends ParentProps, StyleProps {\n padding?: Gap;\n background?: \"none\" | \"soft\" | \"contrast\";\n border?: boolean;\n}\ninterface ScrollAreaProps extends ParentProps {\n maxHeight?: Size;\n}\ninterface TextProps extends ParentProps, StyleProps {\n size?: \"xs\" | \"sm\" | \"base\" | \"lg\";\n weight?: \"normal\" | \"medium\" | \"semibold\";\n tone?: Tone;\n}\ninterface HeadingProps extends ParentProps, StyleProps {\n level?: 1 | 2 | 3;\n}\ninterface LinkProps extends ParentProps {\n /** An `https:` URL within the extension's declared request URLs. */\n href: string;\n}\ninterface BadgeProps extends ParentProps {\n color?: \"base\" | \"primary\" | \"danger\" | \"success\";\n}\ninterface IconProps {\n /** An icon class, e.g. `i-lucide:rocket`, or `i-acme:logo?bg` for a colored icon. */\n name: string;\n size?: Size;\n tone?: Tone;\n}\ninterface ButtonProps extends ButtonBaseProps, ParentProps {\n loading?: boolean;\n}\ninterface IconButtonProps extends ButtonBaseProps {\n icon: string;\n label: string;\n}\ninterface InputProps extends FieldProps {\n type?: \"text\" | \"url\" | \"number\";\n onEnter?(): void;\n}\ninterface TextareaProps extends FieldProps {\n rows?: number;\n}\ninterface ToggleGroupProps {\n value?: string;\n options: Option[];\n disabled?: boolean;\n onChange?(value: string): void;\n}\ninterface ColorInputProps {\n /** A hex color, e.g. `#ff3617`. */\n value?: string;\n disabled?: boolean;\n onInput?(value: string): void;\n}\ninterface TooltipProps extends ParentProps {\n content: string;\n}\ninterface SettingProps extends ParentProps {\n label: string;\n description?: string;\n}\ninterface ListProps extends ParentProps {\n addLabel?: string;\n emptyLabel?: string;\n disabled?: boolean;\n onAdd?(): void;\n onRemove?(index: number): void;\n onMove?(from: number, to: number): void;\n}\ninterface CardProps extends ParentProps {\n color?: \"base\" | \"contrast\" | \"soft\";\n}\ninterface TreeItem {\n id: string;\n label: string;\n icon?: string;\n /** The parent item ID; top level when unset. */\n parent?: string;\n}\ninterface TreeProps {\n items: TreeItem[];\n selected?: string;\n emptyLabel?: string;\n onSelect?(id: string): void;\n}\ninterface TabsProps extends ParentProps {\n value?: string;\n onChange?(value: string): void;\n}\ninterface TabProps extends ParentProps {\n value: string;\n label: string;\n}\ninterface DisclosureProps extends ParentProps {\n label: string;\n open?: boolean;\n}\ninterface CalloutProps extends ParentProps {\n tone?: \"info\" | \"warning\" | \"danger\" | \"success\";\n title?: string;\n}\ninterface StepProps extends ParentProps {\n title: string;\n}\ninterface FigureProps {\n /** An `https:` image URL within the extension's declared request URLs. */\n src: string;\n alt: string;\n caption?: string;\n}\ninterface CodeBlockProps {\n code: string;\n language?: string;\n}\ninterface TimeAgoProps {\n /** An ISO date-time. */\n date: string;\n}\ninterface SpinnerProps {\n size?: Size;\n}\ninterface SkeletonProps {\n height?: Size;\n}\n\ntype Gap = \"none\" | \"small\" | \"medium\" | \"large\";\ntype Size = \"small\" | \"medium\" | \"large\";\ntype Tone = \"default\" | \"muted\" | \"danger\" | \"success\";\ntype EmptyProps = Record<string, never>;\n\n// Host components are serialized nodes; the host renders the matching Andesine component.\nconst hostComponent = <P extends object>(name: string) => {\n return (props: P): JSX.Element => {\n const node = createElement(name);\n\n spread(node, props);\n\n return node as unknown as JSX.Element;\n };\n};\nconst Stack = hostComponent<StackProps>(\"Stack\");\nconst Grid = hostComponent<GridProps>(\"Grid\");\nconst Box = hostComponent<BoxProps>(\"Box\");\nconst Divider = hostComponent<EmptyProps>(\"Divider\");\nconst ScrollArea = hostComponent<ScrollAreaProps>(\"ScrollArea\");\nconst Text = hostComponent<TextProps>(\"Text\");\nconst Heading = hostComponent<HeadingProps>(\"Heading\");\nconst Code = hostComponent<ParentProps>(\"Code\");\nconst Link = hostComponent<LinkProps>(\"Link\");\nconst Badge = hostComponent<BadgeProps>(\"Badge\");\nconst Icon = hostComponent<IconProps>(\"Icon\");\nconst Button = hostComponent<ButtonProps>(\"Button\");\nconst IconButton = hostComponent<IconButtonProps>(\"IconButton\");\nconst Input = hostComponent<InputProps>(\"Input\");\nconst Textarea = hostComponent<TextareaProps>(\"Textarea\");\nconst Select = hostComponent<ChoiceProps>(\"Select\");\nconst Combobox = hostComponent<ChoiceProps>(\"Combobox\");\nconst Checkbox = hostComponent<ToggleProps>(\"Checkbox\");\nconst Toggle = hostComponent<ToggleProps>(\"Toggle\");\nconst ToggleGroup = hostComponent<ToggleGroupProps>(\"ToggleGroup\");\nconst ColorInput = hostComponent<ColorInputProps>(\"ColorInput\");\nconst Tooltip = hostComponent<TooltipProps>(\"Tooltip\");\nconst Setting = hostComponent<SettingProps>(\"Setting\");\nconst List = hostComponent<ListProps>(\"List\");\nconst Card = hostComponent<CardProps>(\"Card\");\nconst Tree = hostComponent<TreeProps>(\"Tree\");\nconst Tabs = hostComponent<TabsProps>(\"Tabs\");\nconst Tab = hostComponent<TabProps>(\"Tab\");\nconst Disclosure = hostComponent<DisclosureProps>(\"Disclosure\");\nconst Callout = hostComponent<CalloutProps>(\"Callout\");\nconst Steps = hostComponent<ParentProps>(\"Steps\");\nconst Step = hostComponent<StepProps>(\"Step\");\nconst Figure = hostComponent<FigureProps>(\"Figure\");\nconst CodeBlock = hostComponent<CodeBlockProps>(\"CodeBlock\");\nconst TimeAgo = hostComponent<TimeAgoProps>(\"TimeAgo\");\nconst Spinner = hostComponent<SpinnerProps>(\"Spinner\");\nconst Skeleton = hostComponent<SkeletonProps>(\"Skeleton\");\n/** Where an element view shows the element's editable content (element views only). */\nconst ContentSlot = hostComponent<EmptyProps>(\"ContentSlot\");\n\nexport {\n Stack,\n Grid,\n Box,\n Divider,\n ScrollArea,\n Text,\n Heading,\n Code,\n Link,\n Badge,\n Icon,\n Button,\n IconButton,\n Input,\n Textarea,\n Select,\n Combobox,\n Checkbox,\n Toggle,\n ToggleGroup,\n ColorInput,\n Tooltip,\n Setting,\n List,\n Card,\n Tree,\n Tabs,\n Tab,\n Disclosure,\n Callout,\n Steps,\n Step,\n Figure,\n CodeBlock,\n TimeAgo,\n Spinner,\n Skeleton,\n ContentSlot\n};\nexport type {\n StackProps,\n GridProps,\n BoxProps,\n ScrollAreaProps,\n TextProps,\n HeadingProps,\n ParentProps,\n LinkProps,\n BadgeProps,\n IconProps,\n ButtonProps,\n IconButtonProps,\n InputProps,\n TextareaProps,\n ChoiceProps,\n ToggleProps,\n ToggleGroupProps,\n ColorInputProps,\n TooltipProps,\n SettingProps,\n ListProps,\n CardProps,\n TreeItem,\n TreeProps,\n TabsProps,\n TabProps,\n DisclosureProps,\n CalloutProps,\n StepProps,\n FigureProps,\n CodeBlockProps,\n TimeAgoProps,\n SpinnerProps,\n SkeletonProps,\n EmptyProps,\n StyleProps,\n Option\n};\n","import type { JSONValue, RequestErrorCode, RequestMethod } from \"./protocol\";\n\ninterface PendingRequest {\n resolve(result: JSONValue | undefined): void;\n reject(error: ExtensionRequestError): void;\n}\n\ntype RequestSender = (id: number, method: RequestMethod, params: JSONValue) => void;\n\nconst pending = new Map<number, PendingRequest>();\n\nlet nextID = 1;\nlet sendRequest: RequestSender | null = null;\n\n/** A host request failed; `code` says why, for example `user_interaction_required`. */\nclass ExtensionRequestError extends Error {\n constructor(\n readonly code: RequestErrorCode,\n message: string\n ) {\n super(message);\n }\n}\n\nconst setRequestSender = (sender: RequestSender): void => {\n sendRequest = sender;\n};\nconst request = (method: RequestMethod, params: JSONValue): Promise<JSONValue | undefined> => {\n if (!sendRequest) {\n return Promise.reject(new ExtensionRequestError(\"unavailable\", \"The extension is not running\"));\n }\n\n const id = nextID++;\n const send = sendRequest;\n\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject });\n send(id, method, params);\n });\n};\nconst settleRequest = (\n id: number,\n result: JSONValue | undefined,\n error?: { code: RequestErrorCode; message: string }\n): void => {\n const request = pending.get(id);\n\n if (!request) return;\n\n pending.delete(id);\n\n if (error) {\n request.reject(new ExtensionRequestError(error.code, error.message));\n } else {\n request.resolve(result);\n }\n};\n\nexport { ExtensionRequestError, request, setRequestSender, settleRequest };\n","import { createContext, useContext } from \"solid-js\";\nimport type { JSONValue } from \"./protocol\";\nimport { request } from \"./requests\";\n\n/** A document block in editor JSON, e.g. `{ type: \"paragraph\", content: [{ type: \"text\", text: \"Hi\" }] }`. */\ninterface ContentNode {\n type: string;\n attrs?: Record<string, JSONValue>;\n content?: ContentNode[];\n marks?: Array<{ type: string; attrs?: Record<string, JSONValue> }>;\n text?: string;\n}\ninterface ViewEditor {\n /** Block action views: replaces the blocks the action was opened for. */\n replace(content: ContentNode[]): Promise<void>;\n /** Block action views: inserts blocks after those the action was opened for. */\n insertAfter(content: ContentNode[]): Promise<void>;\n /** Element views: updates the element's props; call it from a user interaction. */\n setElementProps(props: Record<string, JSONValue>): Promise<void>;\n /** Block action views: closes the view. */\n close(): Promise<void>;\n}\n\nconst ViewContext = createContext<string>();\n\n/**\n * Edits the document from the current view. Andesine validates every edit and applies it as the\n * member's own edit; edits reject with `ExtensionRequestError` when they do not fit.\n */\nconst useEditor = (): ViewEditor => {\n const viewID = useContext(ViewContext) ?? \"\";\n const asJSON = (content: ContentNode[]) => content as unknown as JSONValue;\n\n return {\n replace: async (content) => {\n await request(\"editor.replace\", { viewID, content: asJSON(content) });\n },\n insertAfter: async (content) => {\n await request(\"editor.insertAfter\", { viewID, content: asJSON(content) });\n },\n setElementProps: async (props) => {\n await request(\"editor.setElementProps\", { viewID, props });\n },\n close: async () => {\n await request(\"view.close\", { viewID });\n }\n };\n};\n\nexport { ViewContext, useEditor };\nexport type { ContentNode, ViewEditor };\n","import { createSignal } from \"solid-js\";\nimport { type JSONValue } from \"./protocol\";\nimport { ExtensionRequestError, request } from \"./requests\";\n\ninterface ExtensionContext {\n permissions: string[];\n backend: string | null;\n configuration: Record<string, JSONValue>;\n}\ninterface SessionMessage {\n token: string | null;\n extensionID: string;\n instance: string;\n}\ninterface APIFetchResult {\n status: number;\n contentType: string;\n body: string;\n}\n\n/** The origin `apiFetch` accepts; use it as the SDK `baseURL`. */\nconst EXTENSION_API_URL = \"https://api.andesine.invalid\";\nconst SESSION_WAIT = 10_000;\nconst [permissions, setPermissions] = createSignal<string[]>([]);\nconst [backendURL, setBackendURL] = createSignal<string | null>(null);\nconst [session, setSession] = createSignal<string | null>(null);\nconst [configuration, setConfiguration] = createSignal<Record<string, JSONValue>>({});\n\nlet identity = { extensionID: \"\", instance: \"\" };\n\nconst updateContext = (context: ExtensionContext): void => {\n setPermissions(context.permissions);\n setBackendURL(context.backend);\n setConfiguration(context.configuration);\n};\nconst updateSession = (message: SessionMessage): void => {\n identity = { extensionID: message.extensionID, instance: message.instance };\n setSession(message.token);\n};\n/**\n * The configuration's non-secret values, with defaults for unset fields (reactive). Secret\n * fields are readable only by the extension backend.\n */\nconst useConfiguration = <T extends Record<string, JSONValue> = Record<string, JSONValue>>() => {\n return configuration as () => Partial<T>;\n};\n/** The member's effective permissions for this extension (reactive), e.g. `read:entries`. */\nconst usePermissions = () => permissions;\n/** Whether the effective permissions include `permission`; a write permission includes its read. */\nconst hasPermission = (permission: string): boolean => {\n const write = permission.startsWith(\"read:\") ? permission.slice(5) : null;\n\n return permissions().some((granted) => granted === permission || granted === write);\n};\n/**\n * A fetch implementation for the Andesine SDK: requests go through Andesine with the member's\n * session, within the effective permissions. Forbidden operations answer with HTTP 403.\n * @example createClient({ baseURL: EXTENSION_API_URL, fetch: apiFetch })\n */\nconst apiFetch: typeof fetch = async (input, init) => {\n const outgoing = new Request(input, init);\n const url = new URL(outgoing.url);\n const hasBody = ![\"GET\", \"HEAD\"].includes(outgoing.method);\n\n if (url.origin !== EXTENSION_API_URL) {\n throw new TypeError(`apiFetch only accepts ${EXTENSION_API_URL} URLs`);\n }\n\n try {\n const result = (await request(\"api.fetch\", {\n method: outgoing.method,\n path: `${url.pathname}${url.search}`,\n ...(hasBody && { body: await outgoing.text() })\n })) as unknown as APIFetchResult;\n\n return new Response(result.body, {\n status: result.status,\n headers: { \"content-type\": result.contentType }\n });\n } catch (error) {\n if (!(error instanceof ExtensionRequestError) || error.code !== \"forbidden\") throw error;\n\n return Response.json(\n { code: \"FORBIDDEN\", message: \"The extension cannot call this operation\" },\n { status: 403 }\n );\n }\n};\nconst waitForSession = (): Promise<string> => {\n return new Promise((resolve, reject) => {\n const started = Date.now();\n const check = () => {\n const token = session();\n\n if (token) {\n resolve(token);\n } else if (Date.now() - started > SESSION_WAIT) {\n reject(new ExtensionRequestError(\"unavailable\", \"No backend session is available\"));\n } else {\n setTimeout(check, 100);\n }\n };\n\n check();\n });\n};\n/** The extension's own backend, as declared in the manifest. */\nconst backend = {\n /**\n * Fetches a path of the backend URL with the member's session token in `Authorization`, and\n * the extension ID and instance API URL in `Andesine-Extension` and `Andesine-Instance`; the\n * backend verifies them with `verifySession` from `@andesine/sdk/extensions`. Its CORS\n * configuration must allow these headers.\n */\n async fetch(path: string, init?: RequestInit): Promise<Response> {\n const base = backendURL();\n\n if (!base) throw new ExtensionRequestError(\"unavailable\", \"The extension has no backend\");\n\n const root = base.endsWith(\"/\") ? base : `${base}/`;\n const url = new URL(path.replace(/^\\/+/, \"\"), root);\n\n if (!url.href.startsWith(root)) throw new TypeError(\"The path is outside the backend URL\");\n\n const headers = new Headers(init?.headers);\n\n headers.set(\"Authorization\", `Bearer ${await waitForSession()}`);\n headers.set(\"Andesine-Extension\", identity.extensionID);\n headers.set(\"Andesine-Instance\", identity.instance);\n\n return fetch(url, { ...init, headers, credentials: \"omit\", referrerPolicy: \"no-referrer\" });\n }\n};\n\nexport {\n EXTENSION_API_URL,\n apiFetch,\n backend,\n hasPermission,\n updateContext,\n updateSession,\n useConfiguration,\n usePermissions\n};\n","import { catchError, type Component } from \"solid-js\";\nimport { ViewContext } from \"./editor\";\nimport { createStore, reconcile } from \"solid-js/store\";\nimport {\n createComponent,\n createViewRoot,\n getCallback,\n removeViewRoot,\n render,\n setPatchSender,\n type ViewNode\n} from \"./renderer\";\nimport type { Envelope, HostMessageBody, WorkerMessageBody } from \"./protocol\";\nimport { setRequestSender, settleRequest } from \"./requests\";\nimport { updateContext, updateSession } from \"./context\";\n\ninterface RunningView {\n root: ViewNode;\n setProps(props: Record<string, unknown>): void;\n dispose(): void;\n}\n\ntype ExtensionViews = Record<string, Component<never>>;\n\nconst PROTOCOL: Envelope[\"protocol\"] = 1;\n// Context providers return their children, which are view nodes in this renderer.\nconst ViewProvider = ViewContext.Provider as unknown as (props: {\n value: string;\n children: ViewNode;\n}) => ViewNode;\nconst views = new Map<string, RunningView>();\n\nlet started = false;\n\nconst getErrorMessage = (error: unknown): string => {\n return String(error instanceof Error ? error.message : error).slice(0, 500);\n};\n/**\n * Starts the extension frontend in its sandbox worker. `entries` maps the view entries that the\n * manifest names to Solid components; each view receives its host props as reactive props.\n */\nconst startExtension = (entries: ExtensionViews): void => {\n if (started) return;\n\n started = true;\n globalThis.addEventListener(\"message\", function handleInit(event: MessageEvent) {\n const isInit = event.data?.type === \"andesine:init\" && event.ports.length === 1;\n\n if (!isInit) return;\n\n const port = event.ports[0];\n const generation = event.data.generation as number;\n\n let sequence = 0;\n\n const send = (body: WorkerMessageBody): void => {\n port.postMessage({ ...body, protocol: PROTOCOL, generation, sequence: sequence++ });\n };\n const sendError = (error: unknown, viewID?: string): void => {\n send({ type: \"error\", viewID, message: getErrorMessage(error) });\n };\n const createView = (viewID: string, entry: string, props: Record<string, unknown>): void => {\n // Solid components return JSX elements, which are view nodes in this renderer.\n const component = Object.hasOwn(entries, entry)\n ? (entries[entry] as unknown as (props: object) => ViewNode)\n : undefined;\n\n if (!component || views.has(viewID)) {\n sendError(`Unknown or duplicate view: ${entry}`, viewID);\n\n return;\n }\n\n const root = createViewRoot(viewID);\n const [viewProps, setViewProps] = createStore({ ...props });\n const dispose = render(() => {\n return catchError(\n () => {\n return createComponent(ViewProvider, {\n value: viewID,\n get children() {\n return createComponent(component, viewProps);\n }\n });\n },\n (error) => sendError(error, viewID)\n )!;\n }, root);\n\n views.set(viewID, {\n root,\n setProps: (next) => setViewProps(reconcile(next)),\n dispose\n });\n };\n const disposeView = (viewID: string): void => {\n const view = views.get(viewID);\n\n if (!view) return;\n\n views.delete(viewID);\n view.dispose();\n removeViewRoot(view.root);\n };\n const handleMessage = (message: HostMessageBody): void => {\n if (message.type === \"heartbeat\") {\n send({ type: \"heartbeat\", id: message.id });\n } else if (message.type === \"view.create\") {\n createView(message.viewID, message.entry, message.props);\n } else if (message.type === \"view.update\") {\n views.get(message.viewID)?.setProps(message.props);\n } else if (message.type === \"view.dispose\") {\n disposeView(message.viewID);\n } else if (message.type === \"context\") {\n updateContext(message);\n } else if (message.type === \"session\") {\n updateSession(message);\n } else if (message.type === \"response\") {\n settleRequest(message.id, message.result, message.error);\n } else if (message.type === \"callback\") {\n // Callbacks of removed nodes are released; late calls are ignored.\n getCallback(message.callback)?.(...message.args);\n } else if (message.type === \"dispose\") {\n [...views.keys()].forEach(disposeView);\n port.close();\n globalThis.close();\n }\n };\n\n globalThis.removeEventListener(\"message\", handleInit);\n globalThis.addEventListener(\"error\", (error) => sendError(error.message));\n globalThis.addEventListener(\"unhandledrejection\", (event) => sendError(event.reason));\n setPatchSender((patches) => send({ type: \"patch\", patches }));\n setRequestSender((id, method, params) => send({ type: \"request\", id, method, params }));\n port.addEventListener(\"message\", (event: MessageEvent<HostMessageBody>) => {\n try {\n handleMessage(event.data);\n } catch (error) {\n sendError(error);\n }\n });\n port.start();\n send({ type: \"ready\" });\n });\n};\n\nexport { startExtension };\n","import { request } from \"./requests\";\n\n// Host actions run only shortly after the user interacts with the extension's view, for example\n// from a button's `onClick`. They reject with `ExtensionRequestError`.\n\n/** Copies text; the host shows a fallback dialog if the browser refuses. */\nconst copyText = async (text: string): Promise<void> => {\n await request(\"host.copyText\", { text });\n};\n/** Downloads a text file, for example `downloadFile(\"andesine.config.ts\", source)`. */\nconst downloadFile = async (name: string, text: string, type?: string): Promise<void> => {\n await request(\"host.downloadFile\", type ? { name, text, type } : { name, text });\n};\n/** Opens an `https:` URL in a new tab; URLs outside the declared URLs need confirmation. */\nconst openURL = async (url: string): Promise<void> => {\n await request(\"host.openURL\", { url });\n};\n\nexport { copyText, downloadFile, openURL };\n","import { type JSONValue } from \"./protocol\";\nimport { request } from \"./requests\";\n\ninterface StorageEntry<T extends JSONValue = JSONValue> {\n key: string;\n value: T;\n /** An ISO date-time. */\n updatedAt: string;\n}\ninterface StorageListOptions {\n /** Only keys that start with the prefix. */\n prefix?: string;\n /** Entries after this key, from the previous page. */\n after?: string;\n /** At most 100 (the default). */\n limit?: number;\n}\ninterface StoragePage {\n data: StorageEntry[];\n hasMore: boolean;\n}\n\n/**\n * The extension's JSON key-value storage, shared with its backend. Keys have up to 256\n * characters; values up to 128 KiB; the storage up to 10,000 entries and 10 MiB. The last write\n * wins. Failed requests reject with `ExtensionRequestError`.\n */\nconst storage = {\n /** The entry, or null when the key is not set. */\n async get<T extends JSONValue = JSONValue>(key: string): Promise<StorageEntry<T> | null> {\n return ((await request(\"storage.get\", { key })) ?? null) as StorageEntry<T> | null;\n },\n /** Creates or replaces an entry; resolves with its update time. */\n async set(key: string, value: JSONValue): Promise<{ updatedAt: string }> {\n const { updatedAt } = (await request(\"storage.set\", { key, value })) as { updatedAt: string };\n\n return { updatedAt };\n },\n /** Resolves with whether the key was set. */\n async delete(key: string): Promise<boolean> {\n const { deleted } = (await request(\"storage.delete\", { key })) as { deleted: boolean };\n\n return deleted;\n },\n /** Entries in key order; a page can have fewer entries than `limit` when values are large. */\n async list(options: StorageListOptions = {}): Promise<StoragePage> {\n return (await request(\"storage.list\", { ...options })) as unknown as StoragePage;\n }\n};\n\nexport { storage };\nexport type { StorageEntry, StorageListOptions, StoragePage };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"solid.js","names":["nextID"],"sources":["../src/solid/renderer.ts","../src/solid/components.ts","../src/solid/requests.ts","../src/solid/editor.ts","../src/solid/context.ts","../src/solid/runtime.ts","../src/solid/actions.ts","../src/solid/storage.ts"],"sourcesContent":["import { createRenderer } from \"solid-js/universal\";\nimport type { JSONValue, ViewPatch } from \"./protocol\";\n\n/** A node of the serialized view tree; it mirrors the host tree, which has the real DOM. */\ninterface ViewNode {\n id: number;\n type: string;\n parent?: ViewNode;\n children: ViewNode[];\n callbacks: Map<string, number>;\n}\n\ntype PatchSender = (patches: ViewPatch[]) => void;\n\nconst MAX_PATCHES_PER_MESSAGE = 1000;\nconst MAX_JSON_DEPTH = 8;\nconst callbacks = new Map<number, (...args: unknown[]) => unknown>();\n// Solid moves nodes by removal and insertion; removals wait for the flush to skip reinserted nodes.\nconst removedNodes = new Set<ViewNode>();\n\nlet nextID = 1;\nlet queue: ViewPatch[] = [];\nlet flushScheduled = false;\nlet sendPatches: PatchSender = () => {};\n\nconst releaseCallbacks = (node: ViewNode): void => {\n for (const callback of node.callbacks.values()) callbacks.delete(callback);\n\n node.callbacks.clear();\n node.children.forEach(releaseCallbacks);\n};\nconst flush = (): void => {\n for (const node of removedNodes) {\n if (node.parent) continue;\n\n releaseCallbacks(node);\n queue.push({ op: \"remove\", id: node.id });\n }\n\n removedNodes.clear();\n\n const patches = queue;\n\n flushScheduled = false;\n queue = [];\n\n for (let index = 0; index < patches.length; index += MAX_PATCHES_PER_MESSAGE) {\n sendPatches(patches.slice(index, index + MAX_PATCHES_PER_MESSAGE));\n }\n};\nconst scheduleFlush = (): void => {\n if (flushScheduled) return;\n\n flushScheduled = true;\n queueMicrotask(flush);\n};\nconst queuePatch = (patch: ViewPatch): void => {\n queue.push(patch);\n scheduleFlush();\n};\nconst createNode = (type: string): ViewNode => {\n return { id: nextID++, type, children: [], callbacks: new Map() };\n};\nconst toJSONValue = (value: unknown, depth = 0): JSONValue => {\n const isPrimitive =\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"boolean\" ||\n (typeof value === \"number\" && Number.isFinite(value));\n\n if (isPrimitive) return value as JSONValue;\n\n if (depth >= MAX_JSON_DEPTH) throw new TypeError(\"Extension props are nested too deeply\");\n\n if (Array.isArray(value)) return value.map((item) => toJSONValue(item, depth + 1));\n\n if (Object.getPrototypeOf(value) === Object.prototype) {\n return Object.fromEntries(\n Object.entries(value as object).map(([key, item]) => [key, toJSONValue(item, depth + 1)])\n );\n }\n\n throw new TypeError(\"Extension props must be JSON values or functions\");\n};\nconst detach = (node: ViewNode): void => {\n const siblings = node.parent?.children;\n\n if (siblings) siblings.splice(siblings.indexOf(node), 1);\n\n node.parent = undefined;\n};\nconst renderer = createRenderer<ViewNode>({\n createElement(type) {\n const node = createNode(type);\n\n queuePatch({ op: \"create\", id: node.id, component: type });\n\n return node;\n },\n // Solid passes numbers from JSX expressions as they are; the protocol carries strings.\n createTextNode(value) {\n const node = createNode(\"#text\");\n\n queuePatch({ op: \"text\", id: node.id, value: String(value) });\n\n return node;\n },\n replaceText(node, value) {\n queuePatch({ op: \"setText\", id: node.id, value: String(value) });\n },\n isTextNode(node) {\n return node.type === \"#text\";\n },\n setProperty(node, name, value) {\n const previous = node.callbacks.get(name);\n\n if (previous) {\n callbacks.delete(previous);\n node.callbacks.delete(name);\n }\n\n if (typeof value === \"function\") {\n const callback = nextID++;\n\n callbacks.set(callback, value as (...args: unknown[]) => unknown);\n node.callbacks.set(name, callback);\n queuePatch({ op: \"callback\", id: node.id, name, callback });\n } else if (value === undefined) {\n queuePatch({ op: \"unset\", id: node.id, name });\n } else {\n queuePatch({ op: \"prop\", id: node.id, name, value: toJSONValue(value) });\n }\n },\n insertNode(parent, node, anchor) {\n const index = anchor ? parent.children.indexOf(anchor) : -1;\n\n detach(node);\n node.parent = parent;\n\n if (index >= 0) {\n parent.children.splice(index, 0, node);\n } else {\n parent.children.push(node);\n }\n\n queuePatch({ op: \"insert\", parent: parent.id, id: node.id, before: anchor?.id ?? null });\n },\n removeNode(_parent, node) {\n detach(node);\n removedNodes.add(node);\n scheduleFlush();\n },\n getParentNode(node) {\n return node.parent;\n },\n getFirstChild(node) {\n return node.children[0];\n },\n getNextSibling(node) {\n const siblings = node.parent?.children;\n\n return siblings?.[siblings.indexOf(node) + 1];\n }\n});\nconst createViewRoot = (viewID: string): ViewNode => {\n const node = createNode(\"#root\");\n\n queuePatch({ op: \"root\", id: node.id, viewID });\n\n return node;\n};\nconst removeViewRoot = (node: ViewNode): void => {\n releaseCallbacks(node);\n queuePatch({ op: \"remove\", id: node.id });\n};\nconst setPatchSender = (sender: PatchSender): void => {\n sendPatches = sender;\n};\nconst getCallback = (callback: number): ((...args: unknown[]) => unknown) | undefined => {\n return callbacks.get(callback);\n};\n\nexport const {\n render,\n effect,\n memo,\n createComponent,\n createElement,\n createTextNode,\n insertNode,\n insert,\n spread,\n setProp,\n mergeProps,\n use\n} = renderer;\nexport { createViewRoot, removeViewRoot, setPatchSender, getCallback };\nexport type { ViewNode };\n","import type { JSX } from \"solid-js\";\nimport { createElement, spread } from \"./renderer\";\n\ninterface ParentProps {\n children?: JSX.Element;\n}\ninterface StyleProps {\n /** Utility classes of the Andesine UnoCSS preset; the extension build generates their CSS. */\n class?: string;\n}\ninterface ButtonBaseProps {\n /** Defaults to `secondary` for buttons and `ghost` for icon buttons. */\n variant?: \"primary\" | \"secondary\" | \"ghost\" | \"link\" | \"danger\";\n size?: \"small\" | \"medium\";\n disabled?: boolean;\n onClick?(): void;\n}\ninterface FieldProps {\n value?: string;\n placeholder?: string;\n label?: string;\n disabled?: boolean;\n onInput?(value: string): void;\n}\ninterface Option {\n value: string;\n label: string;\n}\ninterface ChoiceProps {\n value?: string;\n options: Option[];\n placeholder?: string;\n disabled?: boolean;\n onChange?(value: string): void;\n}\ninterface ToggleProps {\n checked?: boolean;\n label?: string;\n disabled?: boolean;\n onChange?(checked: boolean): void;\n}\ninterface StackProps extends ParentProps, StyleProps {\n direction?: \"row\" | \"column\";\n gap?: Gap;\n align?: \"start\" | \"center\" | \"end\" | \"stretch\";\n justify?: \"start\" | \"center\" | \"end\" | \"between\";\n wrap?: boolean;\n}\ninterface GridProps extends ParentProps, StyleProps {\n columns?: number;\n gap?: Gap;\n}\ninterface BoxProps extends ParentProps, StyleProps {\n padding?: Gap;\n background?: \"none\" | \"soft\" | \"contrast\";\n border?: boolean;\n}\ninterface ScrollAreaProps extends ParentProps {\n maxHeight?: Size;\n}\ninterface TextProps extends ParentProps, StyleProps {\n size?: \"xs\" | \"sm\" | \"base\" | \"lg\";\n weight?: \"normal\" | \"medium\" | \"semibold\";\n tone?: Tone;\n}\ninterface HeadingProps extends ParentProps, StyleProps {\n level?: 1 | 2 | 3;\n}\ninterface LinkProps extends ParentProps {\n /** An `https:` URL within the extension's declared request URLs. */\n href: string;\n}\ninterface BadgeProps extends ParentProps {\n color?: \"base\" | \"primary\" | \"danger\" | \"success\";\n}\ninterface IconProps {\n /** An icon class, e.g. `i-lucide:rocket`, or `i-acme:logo?bg` for a colored icon. */\n name: string;\n size?: Size;\n tone?: Tone;\n}\ninterface ButtonProps extends ButtonBaseProps, ParentProps {\n loading?: boolean;\n}\ninterface IconButtonProps extends ButtonBaseProps {\n icon: string;\n label: string;\n}\ninterface InputProps extends FieldProps {\n type?: \"text\" | \"url\" | \"number\";\n onEnter?(): void;\n}\ninterface TextareaProps extends FieldProps {\n rows?: number;\n}\ninterface ToggleGroupProps {\n value?: string;\n options: Option[];\n disabled?: boolean;\n onChange?(value: string): void;\n}\ninterface ColorInputProps {\n /** A hex color, e.g. `#ff3617`. */\n value?: string;\n disabled?: boolean;\n onInput?(value: string): void;\n}\ninterface TooltipProps extends ParentProps {\n content: string;\n}\n/**\n * A modal, shown while rendered. Outside block actions, render it right after an interaction,\n * e.g. from `onClick`; otherwise it isn't shown. In a block action, closing it closes the action.\n */\ninterface DialogProps extends ParentProps {\n title: string;\n description?: string;\n size?: Size;\n /** Whether Escape, an outside click, or the close button call `onClose` (default true). */\n dismissible?: boolean;\n onClose?(): void;\n}\n/**\n * A dropdown menu of `MenuItem`, `MenuGroup`, `MenuSeparator`, and `MenuContent` children,\n * opened by its `MenuTrigger`, or with `contextMenu` by a right click on it. At a block action\n * view's root it needs no trigger: it opens where the block menu was, and closing it closes the action.\n */\ninterface MenuProps extends ParentProps {\n /** The title of the bottom sheet on small screens. */\n title?: string;\n /** Controls the open state; without it, the menu opens and closes on its own. */\n opened?: boolean;\n contextMenu?: boolean;\n placement?:\n \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\" | \"right-start\" | \"left-start\";\n size?: \"small\" | \"medium\";\n onOpenChange?(opened: boolean): void;\n}\n/** Nested items make a submenu, which replaces `onSelect`. */\ninterface MenuItemProps extends ParentProps {\n label: string;\n icon?: string;\n /** Shown next to the label; handle the keys yourself. */\n shortcut?: string;\n color?: \"base\" | \"danger\";\n disabled?: boolean;\n /** A spinner replaces the icon, and the item is disabled. */\n loading?: boolean;\n selected?: boolean;\n closeOnSelect?: boolean;\n onSelect?(): void;\n}\ninterface MenuGroupProps extends ParentProps {\n label?: string;\n}\ninterface SettingProps extends ParentProps {\n label: string;\n description?: string;\n}\ninterface ListProps extends ParentProps {\n addLabel?: string;\n emptyLabel?: string;\n disabled?: boolean;\n onAdd?(): void;\n onRemove?(index: number): void;\n onMove?(from: number, to: number): void;\n}\ninterface CardProps extends ParentProps {\n color?: \"base\" | \"contrast\" | \"soft\";\n}\ninterface TreeItem {\n id: string;\n label: string;\n icon?: string;\n /** The parent item ID; top level when unset. */\n parent?: string;\n}\ninterface TreeProps {\n items: TreeItem[];\n selected?: string;\n emptyLabel?: string;\n onSelect?(id: string): void;\n}\ninterface TabsProps extends ParentProps {\n value?: string;\n onChange?(value: string): void;\n}\ninterface TabProps extends ParentProps {\n value: string;\n label: string;\n}\ninterface DisclosureProps extends ParentProps {\n label: string;\n open?: boolean;\n}\ninterface CalloutProps extends ParentProps {\n tone?: \"info\" | \"warning\" | \"danger\" | \"success\";\n title?: string;\n}\ninterface StepProps extends ParentProps {\n title: string;\n}\ninterface FigureProps {\n /** An `https:` image URL within the extension's declared request URLs. */\n src: string;\n alt: string;\n caption?: string;\n}\ninterface CodeBlockProps {\n code: string;\n language?: string;\n}\ninterface TimeAgoProps {\n /** An ISO date-time. */\n date: string;\n}\ninterface SpinnerProps {\n size?: Size;\n}\ninterface SkeletonProps {\n height?: Size;\n}\n\ntype Gap = \"none\" | \"small\" | \"medium\" | \"large\";\ntype Size = \"small\" | \"medium\" | \"large\";\ntype Tone = \"default\" | \"muted\" | \"danger\" | \"success\";\ntype EmptyProps = Record<string, never>;\n\n// Host components are serialized nodes; the host renders the matching Andesine component.\nconst hostComponent = <P extends object>(name: string) => {\n return (props: P): JSX.Element => {\n const node = createElement(name);\n\n spread(node, props);\n\n return node as unknown as JSX.Element;\n };\n};\nconst Stack = hostComponent<StackProps>(\"Stack\");\nconst Grid = hostComponent<GridProps>(\"Grid\");\nconst Box = hostComponent<BoxProps>(\"Box\");\nconst Divider = hostComponent<EmptyProps>(\"Divider\");\nconst ScrollArea = hostComponent<ScrollAreaProps>(\"ScrollArea\");\nconst Text = hostComponent<TextProps>(\"Text\");\nconst Heading = hostComponent<HeadingProps>(\"Heading\");\nconst Code = hostComponent<ParentProps>(\"Code\");\nconst Link = hostComponent<LinkProps>(\"Link\");\nconst Badge = hostComponent<BadgeProps>(\"Badge\");\nconst Icon = hostComponent<IconProps>(\"Icon\");\nconst Button = hostComponent<ButtonProps>(\"Button\");\nconst IconButton = hostComponent<IconButtonProps>(\"IconButton\");\nconst Input = hostComponent<InputProps>(\"Input\");\nconst Textarea = hostComponent<TextareaProps>(\"Textarea\");\nconst Select = hostComponent<ChoiceProps>(\"Select\");\nconst Combobox = hostComponent<ChoiceProps>(\"Combobox\");\nconst Checkbox = hostComponent<ToggleProps>(\"Checkbox\");\nconst Toggle = hostComponent<ToggleProps>(\"Toggle\");\nconst ToggleGroup = hostComponent<ToggleGroupProps>(\"ToggleGroup\");\nconst ColorInput = hostComponent<ColorInputProps>(\"ColorInput\");\nconst Tooltip = hostComponent<TooltipProps>(\"Tooltip\");\nconst Dialog = hostComponent<DialogProps>(\"Dialog\");\nconst Menu = hostComponent<MenuProps>(\"Menu\");\nconst MenuTrigger = hostComponent<ParentProps>(\"MenuTrigger\");\nconst MenuItem = hostComponent<MenuItemProps>(\"MenuItem\");\nconst MenuGroup = hostComponent<MenuGroupProps>(\"MenuGroup\");\nconst MenuSeparator = hostComponent<EmptyProps>(\"MenuSeparator\");\n/** A custom menu item: any content, e.g. a result or a form. */\nconst MenuContent = hostComponent<ParentProps>(\"MenuContent\");\nconst Setting = hostComponent<SettingProps>(\"Setting\");\nconst List = hostComponent<ListProps>(\"List\");\nconst Card = hostComponent<CardProps>(\"Card\");\nconst Tree = hostComponent<TreeProps>(\"Tree\");\nconst Tabs = hostComponent<TabsProps>(\"Tabs\");\nconst Tab = hostComponent<TabProps>(\"Tab\");\nconst Disclosure = hostComponent<DisclosureProps>(\"Disclosure\");\nconst Callout = hostComponent<CalloutProps>(\"Callout\");\nconst Steps = hostComponent<ParentProps>(\"Steps\");\nconst Step = hostComponent<StepProps>(\"Step\");\nconst Figure = hostComponent<FigureProps>(\"Figure\");\nconst CodeBlock = hostComponent<CodeBlockProps>(\"CodeBlock\");\nconst TimeAgo = hostComponent<TimeAgoProps>(\"TimeAgo\");\nconst Spinner = hostComponent<SpinnerProps>(\"Spinner\");\nconst Skeleton = hostComponent<SkeletonProps>(\"Skeleton\");\n/** Where an element view shows the element's editable content (element views only). */\nconst ContentSlot = hostComponent<EmptyProps>(\"ContentSlot\");\n\nexport {\n Stack,\n Grid,\n Box,\n Divider,\n ScrollArea,\n Text,\n Heading,\n Code,\n Link,\n Badge,\n Icon,\n Button,\n IconButton,\n Input,\n Textarea,\n Select,\n Combobox,\n Checkbox,\n Toggle,\n ToggleGroup,\n ColorInput,\n Tooltip,\n Dialog,\n Menu,\n MenuTrigger,\n MenuItem,\n MenuGroup,\n MenuSeparator,\n MenuContent,\n Setting,\n List,\n Card,\n Tree,\n Tabs,\n Tab,\n Disclosure,\n Callout,\n Steps,\n Step,\n Figure,\n CodeBlock,\n TimeAgo,\n Spinner,\n Skeleton,\n ContentSlot\n};\nexport type {\n StackProps,\n GridProps,\n BoxProps,\n ScrollAreaProps,\n TextProps,\n HeadingProps,\n ParentProps,\n LinkProps,\n BadgeProps,\n IconProps,\n ButtonProps,\n IconButtonProps,\n InputProps,\n TextareaProps,\n ChoiceProps,\n ToggleProps,\n ToggleGroupProps,\n ColorInputProps,\n TooltipProps,\n DialogProps,\n MenuProps,\n MenuItemProps,\n MenuGroupProps,\n SettingProps,\n ListProps,\n CardProps,\n TreeItem,\n TreeProps,\n TabsProps,\n TabProps,\n DisclosureProps,\n CalloutProps,\n StepProps,\n FigureProps,\n CodeBlockProps,\n TimeAgoProps,\n SpinnerProps,\n SkeletonProps,\n EmptyProps,\n StyleProps,\n Option\n};\n","import type { JSONValue, RequestError, RequestErrorCode, RequestMethod } from \"./protocol\";\n\ninterface PendingRequest {\n resolve(result: JSONValue | undefined): void;\n reject(error: ExtensionRequestError): void;\n}\n\ntype RequestSender = (id: number, method: RequestMethod, params: JSONValue) => void;\n\nconst pending = new Map<number, PendingRequest>();\n\nlet nextID = 1;\nlet sendRequest: RequestSender | null = null;\n\n/** A host request failed; `code` says why, for example `user_interaction_required`. */\nclass ExtensionRequestError extends Error {\n constructor(\n readonly code: RequestErrorCode,\n message: string\n ) {\n super(message);\n }\n}\n\nconst setRequestSender = (sender: RequestSender): void => {\n sendRequest = sender;\n};\nconst request = (method: RequestMethod, params: JSONValue): Promise<JSONValue | undefined> => {\n if (!sendRequest) {\n return Promise.reject(new ExtensionRequestError(\"unavailable\", \"The extension is not running\"));\n }\n\n const id = nextID++;\n const send = sendRequest;\n\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject });\n send(id, method, params);\n });\n};\nconst settleRequest = (id: number, result: JSONValue | undefined, error?: RequestError): void => {\n const request = pending.get(id);\n\n if (!request) return;\n\n pending.delete(id);\n\n if (error) {\n request.reject(new ExtensionRequestError(error.code, error.message));\n } else {\n request.resolve(result);\n }\n};\n\nexport { ExtensionRequestError, request, setRequestSender, settleRequest };\n","import { createContext, useContext } from \"solid-js\";\nimport type { JSONValue } from \"./protocol\";\nimport { request } from \"./requests\";\n\n/** Editor JSON block, e.g. `{ type: \"paragraph\", content: [{ type: \"text\", text: \"Hi\" }] }`. */\ninterface ContentNode {\n type: string;\n attrs?: Record<string, JSONValue>;\n content?: ContentNode[];\n marks?: Array<{ type: string; attrs?: Record<string, JSONValue> }>;\n text?: string;\n}\ninterface ViewEditor {\n /** Block action views: replaces the blocks the action was opened for. */\n replace(content: ContentNode[]): Promise<void>;\n /** Block action views: inserts blocks after those the action was opened for. */\n insertAfter(content: ContentNode[]): Promise<void>;\n /** Element views: updates the element's props; call it from a user interaction. */\n setElementProps(props: Record<string, JSONValue>): Promise<void>;\n /** Block action views: closes the view. */\n close(): Promise<void>;\n}\n\nconst ViewContext = createContext<string>();\n\n/** Edits the document as the member; invalid edits reject with `ExtensionRequestError`. */\nconst useEditor = (): ViewEditor => {\n const viewID = useContext(ViewContext) ?? \"\";\n // Content built from view props holds store proxies, which `postMessage` cannot clone.\n const asJSON = (value: unknown) => JSON.parse(JSON.stringify(value)) as JSONValue;\n\n return {\n replace: async (content) => {\n await request(\"editor.replace\", { viewID, content: asJSON(content) });\n },\n insertAfter: async (content) => {\n await request(\"editor.insertAfter\", { viewID, content: asJSON(content) });\n },\n setElementProps: async (props) => {\n await request(\"editor.setElementProps\", {\n viewID,\n props: asJSON(props) as Record<string, JSONValue>\n });\n },\n close: async () => {\n await request(\"view.close\", { viewID });\n }\n };\n};\n\nexport { ViewContext, useEditor };\nexport type { ContentNode, ViewEditor };\n","import { createSignal } from \"solid-js\";\nimport { type JSONValue } from \"./protocol\";\nimport { ExtensionRequestError, request } from \"./requests\";\n\ninterface ExtensionContext {\n permissions: string[];\n backend: string | null;\n configuration: Record<string, JSONValue>;\n}\ninterface SessionMessage {\n token: string | null;\n extensionID: string;\n instance: string;\n}\ninterface APIFetchResult {\n status: number;\n contentType: string;\n body: string;\n}\n\n/** The origin `apiFetch` accepts; use it as the SDK `baseURL`. */\nconst EXTENSION_API_URL = \"https://api.andesine.invalid\";\nconst SESSION_WAIT = 10_000;\nconst [permissions, setPermissions] = createSignal<string[]>([]);\nconst [backendURL, setBackendURL] = createSignal<string | null>(null);\nconst [session, setSession] = createSignal<string | null>(null);\nconst [configuration, setConfiguration] = createSignal<Record<string, JSONValue>>({});\n\nlet identity = { extensionID: \"\", instance: \"\" };\n\nconst updateContext = (context: ExtensionContext): void => {\n setPermissions(context.permissions);\n setBackendURL(context.backend);\n setConfiguration(context.configuration);\n};\nconst updateSession = (message: SessionMessage): void => {\n identity = { extensionID: message.extensionID, instance: message.instance };\n setSession(message.token);\n};\n/** The non-secret configuration values with defaults (reactive); only the backend reads secrets. */\nconst useConfiguration = <T extends Record<string, JSONValue> = Record<string, JSONValue>>() => {\n return configuration as () => Partial<T>;\n};\n/** The member's effective permissions for this extension (reactive), e.g. `read:entries`. */\nconst usePermissions = () => permissions;\n/** Whether the effective permissions include `permission`; a write permission includes its read. */\nconst hasPermission = (permission: string): boolean => {\n const write = permission.startsWith(\"read:\") ? permission.slice(5) : null;\n\n return permissions().some((granted) => granted === permission || granted === write);\n};\n/**\n * SDK `fetch` that calls Andesine with the member's session; forbidden operations return HTTP 403.\n * @example createClient({ baseURL: EXTENSION_API_URL, fetch: apiFetch })\n */\nconst apiFetch: typeof fetch = async (input, init) => {\n const outgoing = new Request(input, init);\n const url = new URL(outgoing.url);\n const hasBody = ![\"GET\", \"HEAD\"].includes(outgoing.method);\n\n if (url.origin !== EXTENSION_API_URL) {\n throw new TypeError(`apiFetch only accepts ${EXTENSION_API_URL} URLs`);\n }\n\n try {\n const result = (await request(\"api.fetch\", {\n method: outgoing.method,\n path: `${url.pathname}${url.search}`,\n ...(hasBody && { body: await outgoing.text() })\n })) as unknown as APIFetchResult;\n\n return new Response(result.body, {\n status: result.status,\n headers: { \"content-type\": result.contentType }\n });\n } catch (error) {\n if (!(error instanceof ExtensionRequestError) || error.code !== \"forbidden\") throw error;\n\n return Response.json(\n { code: \"FORBIDDEN\", message: \"The extension cannot call this operation\" },\n { status: 403 }\n );\n }\n};\nconst waitForSession = (): Promise<string> => {\n return new Promise((resolve, reject) => {\n const started = Date.now();\n const check = () => {\n const token = session();\n\n if (token) {\n resolve(token);\n } else if (Date.now() - started > SESSION_WAIT) {\n reject(new ExtensionRequestError(\"unavailable\", \"No backend session is available\"));\n } else {\n setTimeout(check, 100);\n }\n };\n\n check();\n });\n};\n/** The extension's own backend, as declared in the manifest. */\nconst backend = {\n /**\n * Fetches a backend path with `Authorization`, `Andesine-Extension`, and `Andesine-Instance`\n * headers (CORS must allow them); verify them with `verifySession` of `@andesine/sdk/extensions`.\n */\n async fetch(path: string, init?: RequestInit): Promise<Response> {\n const base = backendURL();\n\n if (!base) throw new ExtensionRequestError(\"unavailable\", \"The extension has no backend\");\n\n const root = base.endsWith(\"/\") ? base : `${base}/`;\n const url = new URL(path.replace(/^\\/+/, \"\"), root);\n\n if (!url.href.startsWith(root)) throw new TypeError(\"The path is outside the backend URL\");\n\n const headers = new Headers(init?.headers);\n\n headers.set(\"Authorization\", `Bearer ${await waitForSession()}`);\n headers.set(\"Andesine-Extension\", identity.extensionID);\n headers.set(\"Andesine-Instance\", identity.instance);\n\n return fetch(url, { ...init, headers, credentials: \"omit\", referrerPolicy: \"no-referrer\" });\n }\n};\n\nexport {\n EXTENSION_API_URL,\n apiFetch,\n backend,\n hasPermission,\n updateContext,\n updateSession,\n useConfiguration,\n usePermissions\n};\n","import { catchError, type Component } from \"solid-js\";\nimport { ViewContext } from \"./editor\";\nimport { createStore, reconcile } from \"solid-js/store\";\nimport {\n createComponent,\n createViewRoot,\n getCallback,\n removeViewRoot,\n render,\n setPatchSender,\n type ViewNode\n} from \"./renderer\";\nimport type { Envelope, HostMessageBody, WorkerMessageBody } from \"./protocol\";\nimport { setRequestSender, settleRequest } from \"./requests\";\nimport { updateContext, updateSession } from \"./context\";\n\ninterface RunningView {\n root: ViewNode;\n setProps(props: Record<string, unknown>): void;\n dispose(): void;\n}\n\ntype ExtensionViews = Record<string, Component<never>>;\n\nconst PROTOCOL: Envelope[\"protocol\"] = 1;\n// Context providers return their children, which are view nodes in this renderer.\nconst ViewProvider = ViewContext.Provider as unknown as (props: {\n value: string;\n children: ViewNode;\n}) => ViewNode;\nconst views = new Map<string, RunningView>();\n\nlet started = false;\n\nconst getErrorMessage = (error: unknown): string => {\n return String(error instanceof Error ? error.message : error).slice(0, 500);\n};\n/** Starts the frontend worker; `entries` maps manifest view entries to Solid components. */\nconst startExtension = (entries: ExtensionViews): void => {\n if (started) return;\n\n started = true;\n globalThis.addEventListener(\"message\", function handleInit(event: MessageEvent) {\n const isInit = event.data?.type === \"andesine:init\" && event.ports.length === 1;\n\n if (!isInit) return;\n\n const port = event.ports[0];\n const generation = event.data.generation as number;\n\n let sequence = 0;\n\n // The host expects consecutive sequences, so a message that fails to clone must not use one.\n const send = (body: WorkerMessageBody): void => {\n port.postMessage({ ...body, protocol: PROTOCOL, generation, sequence });\n sequence += 1;\n };\n const sendError = (error: unknown, viewID?: string): void => {\n send({ type: \"error\", viewID, message: getErrorMessage(error) });\n };\n const createView = (viewID: string, entry: string, props: Record<string, unknown>): void => {\n // Solid components return JSX elements, which are view nodes in this renderer.\n const component = Object.hasOwn(entries, entry)\n ? (entries[entry] as unknown as (props: object) => ViewNode)\n : undefined;\n\n if (!component || views.has(viewID)) {\n sendError(`Unknown or duplicate view: ${entry}`, viewID);\n\n return;\n }\n\n const root = createViewRoot(viewID);\n const [viewProps, setViewProps] = createStore({ ...props });\n const dispose = render(() => {\n return catchError(\n () => {\n return createComponent(ViewProvider, {\n value: viewID,\n get children() {\n return createComponent(component, viewProps);\n }\n });\n },\n (error) => sendError(error, viewID)\n )!;\n }, root);\n\n views.set(viewID, {\n root,\n setProps: (next) => setViewProps(reconcile(next)),\n dispose\n });\n };\n const disposeView = (viewID: string): void => {\n const view = views.get(viewID);\n\n if (!view) return;\n\n views.delete(viewID);\n view.dispose();\n removeViewRoot(view.root);\n };\n const handleMessage = (message: HostMessageBody): void => {\n if (message.type === \"heartbeat\") {\n send({ type: \"heartbeat\", id: message.id });\n } else if (message.type === \"view.create\") {\n createView(message.viewID, message.entry, message.props);\n } else if (message.type === \"view.update\") {\n views.get(message.viewID)?.setProps(message.props);\n } else if (message.type === \"view.dispose\") {\n disposeView(message.viewID);\n } else if (message.type === \"context\") {\n updateContext(message);\n } else if (message.type === \"session\") {\n updateSession(message);\n } else if (message.type === \"response\") {\n settleRequest(message.id, message.result, message.error);\n } else if (message.type === \"callback\") {\n // Callbacks of removed nodes are released; late calls are ignored.\n getCallback(message.callback)?.(...message.args);\n } else if (message.type === \"dispose\") {\n [...views.keys()].forEach(disposeView);\n port.close();\n globalThis.close();\n }\n };\n\n globalThis.removeEventListener(\"message\", handleInit);\n globalThis.addEventListener(\"error\", (error) => sendError(error.message));\n globalThis.addEventListener(\"unhandledrejection\", (event) => sendError(event.reason));\n setPatchSender((patches) => send({ type: \"patch\", patches }));\n setRequestSender((id, method, params) => send({ type: \"request\", id, method, params }));\n port.addEventListener(\"message\", (event: MessageEvent<HostMessageBody>) => {\n try {\n handleMessage(event.data);\n } catch (error) {\n sendError(error);\n }\n });\n port.start();\n send({ type: \"ready\" });\n });\n};\n\nexport { startExtension };\n","import { request } from \"./requests\";\n\n// Host actions need a recent user interaction, e.g. `onClick`; errors are `ExtensionRequestError`.\n\n/** Copies text; the host shows a fallback dialog if the browser refuses. */\nconst copyText = async (text: string): Promise<void> => {\n await request(\"host.copyText\", { text });\n};\n/** Downloads a text file, for example `downloadFile(\"andesine.config.ts\", source)`. */\nconst downloadFile = async (name: string, text: string, type?: string): Promise<void> => {\n await request(\"host.downloadFile\", type ? { name, text, type } : { name, text });\n};\n/** Opens an `https:` URL in a new tab; URLs outside the declared URLs need confirmation. */\nconst openURL = async (url: string): Promise<void> => {\n await request(\"host.openURL\", { url });\n};\n\n/** Shows a short notification, e.g. the result of a block action without UI; it needs no interaction. */\nconst notify = async (text: string, type?: \"success\" | \"error\"): Promise<void> => {\n await request(\"host.notify\", type ? { text, type } : { text });\n};\n\nexport { copyText, downloadFile, openURL, notify };\n","import { type JSONValue } from \"./protocol\";\nimport { request } from \"./requests\";\n\ninterface StorageEntry<T extends JSONValue = JSONValue> {\n key: string;\n value: T;\n /** An ISO date-time. */\n updatedAt: string;\n}\ninterface StorageListOptions {\n /** Only keys that start with the prefix. */\n prefix?: string;\n /** Entries after this key, from the previous page. */\n after?: string;\n /** At most 100 (the default). */\n limit?: number;\n}\ninterface StoragePage {\n data: StorageEntry[];\n hasMore: boolean;\n}\n\n/**\n * JSON key-value storage shared with the backend; the last write wins.\n * Limits: 256-character keys, 128 KiB values, 10,000 entries, and 10 MiB in total.\n */\nconst storage = {\n /** The entry, or null when the key is not set. */\n async get<T extends JSONValue = JSONValue>(key: string): Promise<StorageEntry<T> | null> {\n return ((await request(\"storage.get\", { key })) ?? null) as StorageEntry<T> | null;\n },\n /** Creates or replaces an entry; resolves with its update time. */\n async set(key: string, value: JSONValue): Promise<{ updatedAt: string }> {\n const { updatedAt } = (await request(\"storage.set\", { key, value })) as { updatedAt: string };\n\n return { updatedAt };\n },\n /** Resolves with whether the key was set. */\n async delete(key: string): Promise<boolean> {\n const { deleted } = (await request(\"storage.delete\", { key })) as { deleted: boolean };\n\n return deleted;\n },\n /** Entries in key order; a page can have fewer entries than `limit` when values are large. */\n async list(options: StorageListOptions = {}): Promise<StoragePage> {\n return (await request(\"storage.list\", { ...options })) as unknown as StoragePage;\n }\n};\n\nexport { storage };\nexport type { StorageEntry, StorageListOptions, StoragePage };\n"],"mappings":"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"}
|