@andesine/extensions 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0
4
+
5
+ - No changes; versioned with the other public Andesine packages.
6
+
7
+ ## 0.6.0
8
+
9
+ - `Button` and `IconButton` take one `variant`: `primary`, `secondary`, `ghost`, `link`, or
10
+ `danger` (`color` is removed). Buttons default to `secondary` and icon buttons to `ghost`.
11
+ `Input` renders with the outlined look of Andesine's own fields.
12
+ - Remove `settingsSections` from the manifest: the host-rendered configuration form
13
+ covers settings, and custom extension UI belongs in panels.
14
+ - Add `description` and `icon` to element views; Andesine shows them in the editor's
15
+ slash menu and in the extension's settings.
16
+ - Block action views decide their UI: a view that renders nothing runs without UI
17
+ (the block menu shows a spinner until it closes), and one root `Dialog` or
18
+ `Menu` opens a modal or a menu where the block menu was.
19
+ - Add `Dialog` and `Menu` for any view. `Menu` is a dropdown menu with
20
+ `MenuTrigger` (or a right-click area with `contextMenu`), `MenuItem` (with
21
+ submenus and a `loading` spinner), `MenuGroup`, `MenuSeparator`, and custom
22
+ `MenuContent` items. Outside block actions, a dialog needs a recent interaction.
23
+ - Add `notify(text, type?)`, a host action that shows a short notification.
24
+ - Block action edits accept elements as `{ type: "element", attrs: { name, props } }`;
25
+ Andesine derives their tag.
26
+ - Fix block action and element view edits built from view props: `editor.replace`,
27
+ `editor.insertAfter`, and `editor.setElementProps` now send plain JSON instead of
28
+ store proxies, which could not be sent and stopped the extension.
29
+
3
30
  ## 0.5.0
4
31
 
5
32
  - 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
- See the extension examples in this repository (`packages/public/extension-examples`): element
83
- views, a block action that calls its backend, a webhook-driven backend, and a secret
84
- configuration field. `npx andesine extensions init` creates a project with samples of every
85
- feature.
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 (draft 2020-12) subset that the host validates without running extension code. */
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 frontend module that exports the manifest's view entries (default: `src/frontend/index.tsx`). */
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 (draft 2020-12) subset that the host validates without running extension code. */
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
- variant?: "text" | "solid" | "outlined";
29
- color?: "base" | "contrast" | "primary" | "danger" | "success";
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
- /** A document block in editor JSON, e.g. `{ type: "paragraph", content: [{ type: "text", text: "Hi" }] }`. */
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
- * The extension's JSON key-value storage, shared with its backend. Keys have up to 256
310
- * characters; values up to 128 KiB; the storage up to 10,000 entries and 10 MiB. The last write
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
- * A fetch implementation for the Andesine SDK: requests go through Andesine with the member's
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 path of the backend URL with the member's session token in `Authorization`, and
348
- * the extension ID and instance API URL in `Andesine-Extension` and `Andesine-Instance`; the
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 = (content) => content;
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
- * A fetch implementation for the Andesine SDK: requests go through Andesine with the member's
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 path of the backend URL with the member's session token in `Authorization`, and
371
- * the extension ID and instance API URL in `Andesine-Extension` and `Andesine-Instance`; the
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: 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
- * The extension's JSON key-value storage, shared with its backend. Keys have up to 256
526
- * characters; values up to 128 KiB; the storage up to 10,000 entries and 10 MiB. The last write
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":";;;;AAcA,MAAM,0BAA0B;AAChC,MAAM,iBAAiB;AACvB,MAAM,4BAAY,IAAI,IAA6C;AAGnE,MAAM,+BAAe,IAAI,IAAc;AAEvC,IAAIA,WAAS;AACb,IAAI,QAAqB,CAAC;AAC1B,IAAI,iBAAiB;AACrB,IAAI,oBAAiC,CAAC;AAEtC,MAAM,oBAAoB,SAAyB;CACjD,KAAK,MAAM,YAAY,KAAK,UAAU,OAAO,GAAG,UAAU,OAAO,QAAQ;CAEzE,KAAK,UAAU,MAAM;CACrB,KAAK,SAAS,QAAQ,gBAAgB;AACxC;AACA,MAAM,cAAoB;CACxB,KAAK,MAAM,QAAQ,cAAc;EAC/B,IAAI,KAAK,QAAQ;EAEjB,iBAAiB,IAAI;EACrB,MAAM,KAAK;GAAE,IAAI;GAAU,IAAI,KAAK;EAAG,CAAC;CAC1C;CAEA,aAAa,MAAM;CAEnB,MAAM,UAAU;CAEhB,iBAAiB;CACjB,QAAQ,CAAC;CAET,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,yBACnD,YAAY,QAAQ,MAAM,OAAO,QAAQ,uBAAuB,CAAC;AAErE;AACA,MAAM,sBAA4B;CAChC,IAAI,gBAAgB;CAEpB,iBAAiB;CACjB,eAAe,KAAK;AACtB;AACA,MAAM,cAAc,UAA2B;CAC7C,MAAM,KAAK,KAAK;CAChB,cAAc;AAChB;AACA,MAAM,cAAc,SAA2B;CAC7C,OAAO;EAAE,IAAI;EAAU;EAAM,UAAU,CAAC;EAAG,2BAAW,IAAI,IAAI;CAAE;AAClE;AACA,MAAM,eAAe,OAAgB,QAAQ,MAAiB;CAO5D,IALE,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,aAChB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GAEpC,OAAO;CAExB,IAAI,SAAS,gBAAgB,MAAM,IAAI,UAAU,uCAAuC;CAExF,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,KAAK,SAAS,YAAY,MAAM,QAAQ,CAAC,CAAC;CAEjF,IAAI,OAAO,eAAe,KAAK,MAAM,OAAO,WAC1C,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAe,CAAC,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,KAAK,YAAY,MAAM,QAAQ,CAAC,CAAC,CAAC,CAC1F;CAGF,MAAM,IAAI,UAAU,kDAAkD;AACxE;AACA,MAAM,UAAU,SAAyB;CACvC,MAAM,WAAW,KAAK,QAAQ;CAE9B,IAAI,UAAU,SAAS,OAAO,SAAS,QAAQ,IAAI,GAAG,CAAC;CAEvD,KAAK,SAAS,KAAA;AAChB;AACA,MAAM,WAAW,eAAyB;CACxC,cAAc,MAAM;EAClB,MAAM,OAAO,WAAW,IAAI;EAE5B,WAAW;GAAE,IAAI;GAAU,IAAI,KAAK;GAAI,WAAW;EAAK,CAAC;EAEzD,OAAO;CACT;CAEA,eAAe,OAAO;EACpB,MAAM,OAAO,WAAW,OAAO;EAE/B,WAAW;GAAE,IAAI;GAAQ,IAAI,KAAK;GAAI,OAAO,OAAO,KAAK;EAAE,CAAC;EAE5D,OAAO;CACT;CACA,YAAY,MAAM,OAAO;EACvB,WAAW;GAAE,IAAI;GAAW,IAAI,KAAK;GAAI,OAAO,OAAO,KAAK;EAAE,CAAC;CACjE;CACA,WAAW,MAAM;EACf,OAAO,KAAK,SAAS;CACvB;CACA,YAAY,MAAM,MAAM,OAAO;EAC7B,MAAM,WAAW,KAAK,UAAU,IAAI,IAAI;EAExC,IAAI,UAAU;GACZ,UAAU,OAAO,QAAQ;GACzB,KAAK,UAAU,OAAO,IAAI;EAC5B;EAEA,IAAI,OAAO,UAAU,YAAY;GAC/B,MAAM,WAAW;GAEjB,UAAU,IAAI,UAAU,KAAwC;GAChE,KAAK,UAAU,IAAI,MAAM,QAAQ;GACjC,WAAW;IAAE,IAAI;IAAY,IAAI,KAAK;IAAI;IAAM;GAAS,CAAC;EAC5D,OAAO,IAAI,UAAU,KAAA,GACnB,WAAW;GAAE,IAAI;GAAS,IAAI,KAAK;GAAI;EAAK,CAAC;OAE7C,WAAW;GAAE,IAAI;GAAQ,IAAI,KAAK;GAAI;GAAM,OAAO,YAAY,KAAK;EAAE,CAAC;CAE3E;CACA,WAAW,QAAQ,MAAM,QAAQ;EAC/B,MAAM,QAAQ,SAAS,OAAO,SAAS,QAAQ,MAAM,IAAI;EAEzD,OAAO,IAAI;EACX,KAAK,SAAS;EAEd,IAAI,SAAS,GACX,OAAO,SAAS,OAAO,OAAO,GAAG,IAAI;OAErC,OAAO,SAAS,KAAK,IAAI;EAG3B,WAAW;GAAE,IAAI;GAAU,QAAQ,OAAO;GAAI,IAAI,KAAK;GAAI,QAAQ,QAAQ,MAAM;EAAK,CAAC;CACzF;CACA,WAAW,SAAS,MAAM;EACxB,OAAO,IAAI;EACX,aAAa,IAAI,IAAI;EACrB,cAAc;CAChB;CACA,cAAc,MAAM;EAClB,OAAO,KAAK;CACd;CACA,cAAc,MAAM;EAClB,OAAO,KAAK,SAAS;CACvB;CACA,eAAe,MAAM;EACnB,MAAM,WAAW,KAAK,QAAQ;EAE9B,OAAO,WAAW,SAAS,QAAQ,IAAI,IAAI;CAC7C;AACF,CAAC;AACD,MAAM,kBAAkB,WAA6B;CACnD,MAAM,OAAO,WAAW,OAAO;CAE/B,WAAW;EAAE,IAAI;EAAQ,IAAI,KAAK;EAAI;CAAO,CAAC;CAE9C,OAAO;AACT;AACA,MAAM,kBAAkB,SAAyB;CAC/C,iBAAiB,IAAI;CACrB,WAAW;EAAE,IAAI;EAAU,IAAI,KAAK;CAAG,CAAC;AAC1C;AACA,MAAM,kBAAkB,WAA8B;CACpD,cAAc;AAChB;AACA,MAAM,eAAe,aAAoE;CACvF,OAAO,UAAU,IAAI,QAAQ;AAC/B;AAEA,MAAa,EACX,QACA,QACA,MACA,iBACA,eACA,gBACA,YACA,QACA,QACA,SACA,YACA,QACE;;;ACZJ,MAAM,iBAAmC,SAAiB;CACxD,QAAQ,UAA0B;EAChC,MAAM,OAAO,cAAc,IAAI;EAE/B,OAAO,MAAM,KAAK;EAElB,OAAO;CACT;AACF;AACA,MAAM,QAAQ,cAA0B,OAAO;AAC/C,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,MAAM,cAAwB,KAAK;AACzC,MAAM,UAAU,cAA0B,SAAS;AACnD,MAAM,aAAa,cAA+B,YAAY;AAC9D,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,UAAU,cAA4B,SAAS;AACrD,MAAM,OAAO,cAA2B,MAAM;AAC9C,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,QAAQ,cAA0B,OAAO;AAC/C,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,SAAS,cAA2B,QAAQ;AAClD,MAAM,aAAa,cAA+B,YAAY;AAC9D,MAAM,QAAQ,cAA0B,OAAO;AAC/C,MAAM,WAAW,cAA6B,UAAU;AACxD,MAAM,SAAS,cAA2B,QAAQ;AAClD,MAAM,WAAW,cAA2B,UAAU;AACtD,MAAM,WAAW,cAA2B,UAAU;AACtD,MAAM,SAAS,cAA2B,QAAQ;AAClD,MAAM,cAAc,cAAgC,aAAa;AACjE,MAAM,aAAa,cAA+B,YAAY;AAC9D,MAAM,UAAU,cAA4B,SAAS;AACrD,MAAM,UAAU,cAA4B,SAAS;AACrD,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,MAAM,cAAwB,KAAK;AACzC,MAAM,aAAa,cAA+B,YAAY;AAC9D,MAAM,UAAU,cAA4B,SAAS;AACrD,MAAM,QAAQ,cAA2B,OAAO;AAChD,MAAM,OAAO,cAAyB,MAAM;AAC5C,MAAM,SAAS,cAA2B,QAAQ;AAClD,MAAM,YAAY,cAA8B,WAAW;AAC3D,MAAM,UAAU,cAA4B,SAAS;AACrD,MAAM,UAAU,cAA4B,SAAS;AACrD,MAAM,WAAW,cAA6B,UAAU;;AAExD,MAAM,cAAc,cAA0B,aAAa;;;AC9N3D,MAAM,0BAAU,IAAI,IAA4B;AAEhD,IAAI,SAAS;AACb,IAAI,cAAoC;;AAGxC,IAAM,wBAAN,cAAoC,MAAM;CAE7B;CADX,YACE,MACA,SACA;EACA,MAAM,OAAO;EAHJ,KAAA,OAAA;CAIX;AACF;AAEA,MAAM,oBAAoB,WAAgC;CACxD,cAAc;AAChB;AACA,MAAM,WAAW,QAAuB,WAAsD;CAC5F,IAAI,CAAC,aACH,OAAO,QAAQ,OAAO,IAAI,sBAAsB,eAAe,8BAA8B,CAAC;CAGhG,MAAM,KAAK;CACX,MAAM,OAAO;CAEb,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,QAAQ,IAAI,IAAI;GAAE;GAAS;EAAO,CAAC;EACnC,KAAK,IAAI,QAAQ,MAAM;CACzB,CAAC;AACH;AACA,MAAM,iBACJ,IACA,QACA,UACS;CACT,MAAM,UAAU,QAAQ,IAAI,EAAE;CAE9B,IAAI,CAAC,SAAS;CAEd,QAAQ,OAAO,EAAE;CAEjB,IAAI,OACF,QAAQ,OAAO,IAAI,sBAAsB,MAAM,MAAM,MAAM,OAAO,CAAC;MAEnE,QAAQ,QAAQ,MAAM;AAE1B;;;ACjCA,MAAM,cAAc,cAAsB;;;;;AAM1C,MAAM,kBAA8B;CAClC,MAAM,SAAS,WAAW,WAAW,KAAK;CAC1C,MAAM,UAAU,YAA2B;CAE3C,OAAO;EACL,SAAS,OAAO,YAAY;GAC1B,MAAM,QAAQ,kBAAkB;IAAE;IAAQ,SAAS,OAAO,OAAO;GAAE,CAAC;EACtE;EACA,aAAa,OAAO,YAAY;GAC9B,MAAM,QAAQ,sBAAsB;IAAE;IAAQ,SAAS,OAAO,OAAO;GAAE,CAAC;EAC1E;EACA,iBAAiB,OAAO,UAAU;GAChC,MAAM,QAAQ,0BAA0B;IAAE;IAAQ;GAAM,CAAC;EAC3D;EACA,OAAO,YAAY;GACjB,MAAM,QAAQ,cAAc,EAAE,OAAO,CAAC;EACxC;CACF;AACF;;;;AC1BA,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,CAAC,aAAa,kBAAkB,aAAuB,CAAC,CAAC;AAC/D,MAAM,CAAC,YAAY,iBAAiB,aAA4B,IAAI;AACpE,MAAM,CAAC,SAAS,cAAc,aAA4B,IAAI;AAC9D,MAAM,CAAC,eAAe,oBAAoB,aAAwC,CAAC,CAAC;AAEpF,IAAI,WAAW;CAAE,aAAa;CAAI,UAAU;AAAG;AAE/C,MAAM,iBAAiB,YAAoC;CACzD,eAAe,QAAQ,WAAW;CAClC,cAAc,QAAQ,OAAO;CAC7B,iBAAiB,QAAQ,aAAa;AACxC;AACA,MAAM,iBAAiB,YAAkC;CACvD,WAAW;EAAE,aAAa,QAAQ;EAAa,UAAU,QAAQ;CAAS;CAC1E,WAAW,QAAQ,KAAK;AAC1B;;;;;AAKA,MAAM,yBAA0F;CAC9F,OAAO;AACT;;AAEA,MAAM,uBAAuB;;AAE7B,MAAM,iBAAiB,eAAgC;CACrD,MAAM,QAAQ,WAAW,WAAW,OAAO,IAAI,WAAW,MAAM,CAAC,IAAI;CAErE,OAAO,YAAY,CAAC,CAAC,MAAM,YAAY,YAAY,cAAc,YAAY,KAAK;AACpF;;;;;;AAMA,MAAM,WAAyB,OAAO,OAAO,SAAS;CACpD,MAAM,WAAW,IAAI,QAAQ,OAAO,IAAI;CACxC,MAAM,MAAM,IAAI,IAAI,SAAS,GAAG;CAChC,MAAM,UAAU,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,SAAS,MAAM;CAEzD,IAAI,IAAI,WAAA,gCACN,MAAM,IAAI,UAAU,yBAAyB,kBAAkB,MAAM;CAGvE,IAAI;EACF,MAAM,SAAU,MAAM,QAAQ,aAAa;GACzC,QAAQ,SAAS;GACjB,MAAM,GAAG,IAAI,WAAW,IAAI;GAC5B,GAAI,WAAW,EAAE,MAAM,MAAM,SAAS,KAAK,EAAE;EAC/C,CAAC;EAED,OAAO,IAAI,SAAS,OAAO,MAAM;GAC/B,QAAQ,OAAO;GACf,SAAS,EAAE,gBAAgB,OAAO,YAAY;EAChD,CAAC;CACH,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,0BAA0B,MAAM,SAAS,aAAa,MAAM;EAEnF,OAAO,SAAS,KACd;GAAE,MAAM;GAAa,SAAS;EAA2C,GACzE,EAAE,QAAQ,IAAI,CAChB;CACF;AACF;AACA,MAAM,uBAAwC;CAC5C,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,UAAU,KAAK,IAAI;EACzB,MAAM,cAAc;GAClB,MAAM,QAAQ,QAAQ;GAEtB,IAAI,OACF,QAAQ,KAAK;QACR,IAAI,KAAK,IAAI,IAAI,UAAU,cAChC,OAAO,IAAI,sBAAsB,eAAe,iCAAiC,CAAC;QAElF,WAAW,OAAO,GAAG;EAEzB;EAEA,MAAM;CACR,CAAC;AACH;;AAEA,MAAM,UAAU;;;;;;;AAOd,MAAM,MAAM,MAAc,MAAuC;CAC/D,MAAM,OAAO,WAAW;CAExB,IAAI,CAAC,MAAM,MAAM,IAAI,sBAAsB,eAAe,8BAA8B;CAExF,MAAM,OAAO,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,KAAK;CACjD,MAAM,MAAM,IAAI,IAAI,KAAK,QAAQ,QAAQ,EAAE,GAAG,IAAI;CAElD,IAAI,CAAC,IAAI,KAAK,WAAW,IAAI,GAAG,MAAM,IAAI,UAAU,qCAAqC;CAEzF,MAAM,UAAU,IAAI,QAAQ,MAAM,OAAO;CAEzC,QAAQ,IAAI,iBAAiB,UAAU,MAAM,eAAe,GAAG;CAC/D,QAAQ,IAAI,sBAAsB,SAAS,WAAW;CACtD,QAAQ,IAAI,qBAAqB,SAAS,QAAQ;CAElD,OAAO,MAAM,KAAK;EAAE,GAAG;EAAM;EAAS,aAAa;EAAQ,gBAAgB;CAAc,CAAC;AAC5F,EACF;;;AC5GA,MAAM,WAAiC;AAEvC,MAAM,eAAe,YAAY;AAIjC,MAAM,wBAAQ,IAAI,IAAyB;AAE3C,IAAI,UAAU;AAEd,MAAM,mBAAmB,UAA2B;CAClD,OAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG;AAC5E;;;;;AAKA,MAAM,kBAAkB,YAAkC;CACxD,IAAI,SAAS;CAEb,UAAU;CACV,WAAW,iBAAiB,WAAW,SAAS,WAAW,OAAqB;EAG9E,IAAI,EAFW,MAAM,MAAM,SAAS,mBAAmB,MAAM,MAAM,WAAW,IAEjE;EAEb,MAAM,OAAO,MAAM,MAAM;EACzB,MAAM,aAAa,MAAM,KAAK;EAE9B,IAAI,WAAW;EAEf,MAAM,QAAQ,SAAkC;GAC9C,KAAK,YAAY;IAAE,GAAG;IAAM,UAAU;IAAU;IAAY,UAAU;GAAW,CAAC;EACpF;EACA,MAAM,aAAa,OAAgB,WAA0B;GAC3D,KAAK;IAAE,MAAM;IAAS;IAAQ,SAAS,gBAAgB,KAAK;GAAE,CAAC;EACjE;EACA,MAAM,cAAc,QAAgB,OAAe,UAAyC;GAE1F,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,IACzC,QAAQ,SACT,KAAA;GAEJ,IAAI,CAAC,aAAa,MAAM,IAAI,MAAM,GAAG;IACnC,UAAU,8BAA8B,SAAS,MAAM;IAEvD;GACF;GAEA,MAAM,OAAO,eAAe,MAAM;GAClC,MAAM,CAAC,WAAW,gBAAgB,YAAY,EAAE,GAAG,MAAM,CAAC;GAC1D,MAAM,UAAU,aAAa;IAC3B,OAAO,iBACC;KACJ,OAAO,gBAAgB,cAAc;MACnC,OAAO;MACP,IAAI,WAAW;OACb,OAAO,gBAAgB,WAAW,SAAS;MAC7C;KACF,CAAC;IACH,IACC,UAAU,UAAU,OAAO,MAAM,CACpC;GACF,GAAG,IAAI;GAEP,MAAM,IAAI,QAAQ;IAChB;IACA,WAAW,SAAS,aAAa,UAAU,IAAI,CAAC;IAChD;GACF,CAAC;EACH;EACA,MAAM,eAAe,WAAyB;GAC5C,MAAM,OAAO,MAAM,IAAI,MAAM;GAE7B,IAAI,CAAC,MAAM;GAEX,MAAM,OAAO,MAAM;GACnB,KAAK,QAAQ;GACb,eAAe,KAAK,IAAI;EAC1B;EACA,MAAM,iBAAiB,YAAmC;GACxD,IAAI,QAAQ,SAAS,aACnB,KAAK;IAAE,MAAM;IAAa,IAAI,QAAQ;GAAG,CAAC;QACrC,IAAI,QAAQ,SAAS,eAC1B,WAAW,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,KAAK;QAClD,IAAI,QAAQ,SAAS,eAC1B,MAAM,IAAI,QAAQ,MAAM,CAAC,EAAE,SAAS,QAAQ,KAAK;QAC5C,IAAI,QAAQ,SAAS,gBAC1B,YAAY,QAAQ,MAAM;QACrB,IAAI,QAAQ,SAAS,WAC1B,cAAc,OAAO;QAChB,IAAI,QAAQ,SAAS,WAC1B,cAAc,OAAO;QAChB,IAAI,QAAQ,SAAS,YAC1B,cAAc,QAAQ,IAAI,QAAQ,QAAQ,QAAQ,KAAK;QAClD,IAAI,QAAQ,SAAS,YAE1B,YAAY,QAAQ,QAAQ,CAAC,GAAG,GAAG,QAAQ,IAAI;QAC1C,IAAI,QAAQ,SAAS,WAAW;IACrC,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,QAAQ,WAAW;IACrC,KAAK,MAAM;IACX,WAAW,MAAM;GACnB;EACF;EAEA,WAAW,oBAAoB,WAAW,UAAU;EACpD,WAAW,iBAAiB,UAAU,UAAU,UAAU,MAAM,OAAO,CAAC;EACxE,WAAW,iBAAiB,uBAAuB,UAAU,UAAU,MAAM,MAAM,CAAC;EACpF,gBAAgB,YAAY,KAAK;GAAE,MAAM;GAAS;EAAQ,CAAC,CAAC;EAC5D,kBAAkB,IAAI,QAAQ,WAAW,KAAK;GAAE,MAAM;GAAW;GAAI;GAAQ;EAAO,CAAC,CAAC;EACtF,KAAK,iBAAiB,YAAY,UAAyC;GACzE,IAAI;IACF,cAAc,MAAM,IAAI;GAC1B,SAAS,OAAO;IACd,UAAU,KAAK;GACjB;EACF,CAAC;EACD,KAAK,MAAM;EACX,KAAK,EAAE,MAAM,QAAQ,CAAC;CACxB,CAAC;AACH;;;;AC1IA,MAAM,WAAW,OAAO,SAAgC;CACtD,MAAM,QAAQ,iBAAiB,EAAE,KAAK,CAAC;AACzC;;AAEA,MAAM,eAAe,OAAO,MAAc,MAAc,SAAiC;CACvF,MAAM,QAAQ,qBAAqB,OAAO;EAAE;EAAM;EAAM;CAAK,IAAI;EAAE;EAAM;CAAK,CAAC;AACjF;;AAEA,MAAM,UAAU,OAAO,QAA+B;CACpD,MAAM,QAAQ,gBAAgB,EAAE,IAAI,CAAC;AACvC;;;;;;;;ACWA,MAAM,UAAU;;CAEd,MAAM,IAAqC,KAA8C;EACvF,OAAS,MAAM,QAAQ,eAAe,EAAE,IAAI,CAAC,KAAM;CACrD;;CAEA,MAAM,IAAI,KAAa,OAAkD;EACvE,MAAM,EAAE,cAAe,MAAM,QAAQ,eAAe;GAAE;GAAK;EAAM,CAAC;EAElE,OAAO,EAAE,UAAU;CACrB;;CAEA,MAAM,OAAO,KAA+B;EAC1C,MAAM,EAAE,YAAa,MAAM,QAAQ,kBAAkB,EAAE,IAAI,CAAC;EAE5D,OAAO;CACT;;CAEA,MAAM,KAAK,UAA8B,CAAC,GAAyB;EACjE,OAAQ,MAAM,QAAQ,gBAAgB,EAAE,GAAG,QAAQ,CAAC;CACtD;AACF"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andesine/extensions",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Authoring tools for Andesine extensions",
5
5
  "type": "module",
6
6
  "license": "MIT",