@rsc-kit/press 0.30.50 → 0.30.51

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.
@@ -20,5 +20,15 @@ export declare function targetOf(action: HostAction): string | null;
20
20
  * the form first draws, by which time the server entry has run.
21
21
  */
22
22
  export declare function hostSchema(action: HostAction): FormSchema;
23
+ /**
24
+ * What calls an action: a public form or a panel's create, with the values
25
+ * alone, or a panel's update, with the record's key after them.
26
+ */
27
+ export interface Caller {
28
+ /** How errors name it: "[forms] the form contact", "[admin] products' update". */
29
+ name: string;
30
+ /** Called as update(values, id), so the backend must take the key second. */
31
+ key: boolean;
32
+ }
23
33
  /** A form must be able to draw what it was given, or say so rather than draw a text box. */
24
- export declare function requireDrawable(form: string, action: HostAction, overrides: Record<string, FieldOverride>): void;
34
+ export declare function requireDrawable(caller: string | Caller, action: HostAction, overrides: Record<string, FieldOverride | undefined>): void;
@@ -25,22 +25,32 @@ export function hostSchema(action) {
25
25
  };
26
26
  }
27
27
  /** A form must be able to draw what it was given, or say so rather than draw a text box. */
28
- export function requireDrawable(form, action, overrides) {
28
+ export function requireDrawable(caller, action, overrides) {
29
+ const { name, key } = typeof caller === 'string' ? { name: `[forms] the form ${caller}`, key: false } : caller;
29
30
  const target = targetOf(action);
30
31
  if (target === null) {
31
- throw new Error(`[forms] the form ${form}'s action ${action.name || 'function'} is not one the build generated for the backend. ` +
32
+ throw new Error(`${name}'s action ${action.name || 'function'} is not one the build generated for the backend. ` +
32
33
  "Import it from the generated server-actions module: import { contactSend } from '../server-actions.generated'.");
33
34
  }
34
35
  const input = hostForms().inputs[target];
35
36
  if (!input) {
36
- if (Object.keys(overrides).length > 0)
37
+ if (Object.keys(overrides).length > 0 && !key)
37
38
  return;
38
- throw new Error(`[forms] the form ${form} calls ${target}, and the backend does not say it takes one object with named fields. ` +
39
- 'Take a FormRequest in Laravel or a struct in Go, or list every field in fields.');
39
+ throw new Error(`${name} calls ${target}, and the backend does not say it takes one object with named fields` +
40
+ (key ? " and the record's key after it" : '') +
41
+ '. Take a FormRequest in Laravel or a struct in Go' +
42
+ (key ? ', then the key: update(UpdateProductRequest $request, string $product).' : ', or list every field in fields.'));
43
+ }
44
+ if (key && !input['x-key']) {
45
+ throw new Error(`${name} calls ${target} with the record's key after the values, and it takes only the values. ` +
46
+ 'Take the key second: update(UpdateProductRequest $request, string $product) in Laravel, func(ctx, in ProductInput, id string) in Go.');
47
+ }
48
+ if (!key && input['x-key']) {
49
+ throw new Error(`${name} calls ${target} with the values alone, and it takes a record's key after them. Use it as a panel's update.`);
40
50
  }
41
51
  for (const [field, schema] of Object.entries(input.properties ?? {})) {
42
52
  if (!schema.type && !schema.enum && !schema.anyOf && !overrides[field]?.type) {
43
- throw new Error(`[forms] the form ${form} cannot tell what ${target}'s ${field} is, which a file or an open type would be. Say it in fields.`);
53
+ throw new Error(`${name} cannot tell what ${target}'s ${field} is, which a file or an open type would be. Say it in fields.`);
44
54
  }
45
55
  }
46
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/forms/host.ts"],"names":[],"mappings":"AA4BA,SAAS,SAAS;IAChB,MAAM,KAAK,GAAI,UAAsC,CAAC,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAA0B,CAAA;IAEhH,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,iGAAiG,CAAC,CAAA;IAE9H,OAAO,KAAK,CAAA;AACd,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,QAAQ,CAAC,MAAkB;IACzC,OAAO,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAA;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAkB;IAC3C,OAAO;QACL,WAAW,EAAE;YACX,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAgC,EAAE,CAAC;YAC3E,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE;SACxE;KACuB,CAAA;AAC5B,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,MAAkB,EAAE,SAAwC;IACxG,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAA;IAE/B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,oBAAoB,IAAI,aAAa,MAAM,CAAC,IAAI,IAAI,UAAU,mDAAmD;YAC/G,gHAAgH,CACnH,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAExC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,OAAM;QAE7C,MAAM,IAAI,KAAK,CACb,oBAAoB,IAAI,UAAU,MAAM,wEAAwE;YAC9G,iFAAiF,CACpF,CAAA;IACH,CAAC;IAED,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;QACrE,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,MAAM,MAAM,KAAK,+DAA+D,CAAC,CAAA;QAChJ,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import type { FormFields } from '@rsc-kit/core/form'\nimport type { FieldOverride, FormSchema } from '../resource.js'\n\n/**\n * A form with no schema of its own: defineForm({ action: contactSend }), with\n * the action the build generated for the backend's Contact.send. The backend\n * said what the function takes when it wrote rsc-host.json, and the build put\n * that in the server entry, so the fields come from there and every check is\n * the backend's.\n */\n\n/** A backend action the build generated: import { contactSend } from '../server-actions.generated'. */\nexport type HostAction = (...args: never[]) => Promise<unknown>\n\n/** The names of the fields an action's form has, as its generated stub declares them. */\nexport type HostFields<Action> = Action extends (form: FormFields<infer Name>) => Promise<unknown>\n ? string extends Name\n ? string\n : Name\n : string\n\ninterface HostForms {\n /** Each generated action, and the backend function it calls. */\n actions: Map<unknown, string>\n /** What each backend function takes, by its name. */\n inputs: Record<string, { properties?: Record<string, Record<string, unknown>> }>\n}\n\nfunction hostForms(): HostForms {\n const forms = (globalThis as Record<symbol, unknown>)[Symbol.for('rsc-kit.host-forms')] as HostForms | undefined\n\n if (!forms) throw new Error('[forms] a form with an action needs the rsc-kit server entry, which says what the backend takes')\n\n return forms\n}\n\n/** The backend function a generated action calls, or null for any other function. */\nexport function targetOf(action: HostAction): string | null {\n return hostForms().actions.get(action) ?? null\n}\n\n/**\n * A Standard Schema that describes the backend's input and refuses nothing:\n * the rest of the form is the same as for any schema, and the checking is the\n * function's. The browser's checks come from the same description. Read when\n * the form first draws, by which time the server entry has run.\n */\nexport function hostSchema(action: HostAction): FormSchema {\n return {\n '~standard': {\n version: 1,\n vendor: 'rsc-kit',\n validate: (value: unknown) => ({ value: value as Record<string, unknown> }),\n jsonSchema: { input: () => hostForms().inputs[targetOf(action) ?? ''] },\n },\n } as unknown as FormSchema\n}\n\n/** A form must be able to draw what it was given, or say so rather than draw a text box. */\nexport function requireDrawable(form: string, action: HostAction, overrides: Record<string, FieldOverride>): void {\n const target = targetOf(action)\n\n if (target === null) {\n throw new Error(\n `[forms] the form ${form}'s action ${action.name || 'function'} is not one the build generated for the backend. ` +\n \"Import it from the generated server-actions module: import { contactSend } from '../server-actions.generated'.\",\n )\n }\n\n const input = hostForms().inputs[target]\n\n if (!input) {\n if (Object.keys(overrides).length > 0) return\n\n throw new Error(\n `[forms] the form ${form} calls ${target}, and the backend does not say it takes one object with named fields. ` +\n 'Take a FormRequest in Laravel or a struct in Go, or list every field in fields.',\n )\n }\n\n for (const [field, schema] of Object.entries(input.properties ?? {})) {\n if (!schema.type && !schema.enum && !schema.anyOf && !overrides[field]?.type) {\n throw new Error(`[forms] the form ${form} cannot tell what ${target}'s ${field} is, which a file or an open type would be. Say it in fields.`)\n }\n }\n}\n"]}
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/forms/host.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FormFields } from '@rsc-kit/core/form'\nimport type { FieldOverride, FormSchema } from '../resource.js'\n\n/**\n * A form with no schema of its own: defineForm({ action: contactSend }), with\n * the action the build generated for the backend's Contact.send. The backend\n * said what the function takes when it wrote rsc-host.json, and the build put\n * that in the server entry, so the fields come from there and every check is\n * the backend's.\n */\n\n/** A backend action the build generated: import { contactSend } from '../server-actions.generated'. */\nexport type HostAction = (...args: never[]) => Promise<unknown>\n\n/** The names of the fields an action's form has, as its generated stub declares them. */\nexport type HostFields<Action> = Action extends (form: FormFields<infer Name>) => Promise<unknown>\n ? string extends Name\n ? string\n : Name\n : string\n\ninterface HostForms {\n /** Each generated action, and the backend function it calls. */\n actions: Map<unknown, string>\n /** What each backend function takes, by its name. */\n inputs: Record<string, { properties?: Record<string, Record<string, unknown>>; 'x-key'?: boolean }>\n}\n\nfunction hostForms(): HostForms {\n const forms = (globalThis as Record<symbol, unknown>)[Symbol.for('rsc-kit.host-forms')] as HostForms | undefined\n\n if (!forms) throw new Error('[forms] a form with an action needs the rsc-kit server entry, which says what the backend takes')\n\n return forms\n}\n\n/** The backend function a generated action calls, or null for any other function. */\nexport function targetOf(action: HostAction): string | null {\n return hostForms().actions.get(action) ?? null\n}\n\n/**\n * A Standard Schema that describes the backend's input and refuses nothing:\n * the rest of the form is the same as for any schema, and the checking is the\n * function's. The browser's checks come from the same description. Read when\n * the form first draws, by which time the server entry has run.\n */\nexport function hostSchema(action: HostAction): FormSchema {\n return {\n '~standard': {\n version: 1,\n vendor: 'rsc-kit',\n validate: (value: unknown) => ({ value: value as Record<string, unknown> }),\n jsonSchema: { input: () => hostForms().inputs[targetOf(action) ?? ''] },\n },\n } as unknown as FormSchema\n}\n\n/**\n * What calls an action: a public form or a panel's create, with the values\n * alone, or a panel's update, with the record's key after them.\n */\nexport interface Caller {\n /** How errors name it: \"[forms] the form contact\", \"[admin] products' update\". */\n name: string\n /** Called as update(values, id), so the backend must take the key second. */\n key: boolean\n}\n\n/** A form must be able to draw what it was given, or say so rather than draw a text box. */\nexport function requireDrawable(caller: string | Caller, action: HostAction, overrides: Record<string, FieldOverride | undefined>): void {\n const { name, key } = typeof caller === 'string' ? { name: `[forms] the form ${caller}`, key: false } : caller\n const target = targetOf(action)\n\n if (target === null) {\n throw new Error(\n `${name}'s action ${action.name || 'function'} is not one the build generated for the backend. ` +\n \"Import it from the generated server-actions module: import { contactSend } from '../server-actions.generated'.\",\n )\n }\n\n const input = hostForms().inputs[target] as { properties?: Record<string, Record<string, unknown>>; 'x-key'?: boolean } | undefined\n\n if (!input) {\n if (Object.keys(overrides).length > 0 && !key) return\n\n throw new Error(\n `${name} calls ${target}, and the backend does not say it takes one object with named fields` +\n (key ? \" and the record's key after it\" : '') +\n '. Take a FormRequest in Laravel or a struct in Go' +\n (key ? ', then the key: update(UpdateProductRequest $request, string $product).' : ', or list every field in fields.'),\n )\n }\n\n if (key && !input['x-key']) {\n throw new Error(\n `${name} calls ${target} with the record's key after the values, and it takes only the values. ` +\n 'Take the key second: update(UpdateProductRequest $request, string $product) in Laravel, func(ctx, in ProductInput, id string) in Go.',\n )\n }\n\n if (!key && input['x-key']) {\n throw new Error(`${name} calls ${target} with the values alone, and it takes a record's key after them. Use it as a panel's update.`)\n }\n\n for (const [field, schema] of Object.entries(input.properties ?? {})) {\n if (!schema.type && !schema.enum && !schema.anyOf && !overrides[field]?.type) {\n throw new Error(`${name} cannot tell what ${target}'s ${field} is, which a file or an open type would be. Say it in fields.`)\n }\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { defineResource, fieldsOf, humanize, parse, valuesFrom } from './resource.js';
2
- export type { BulkAction, Column, ColumnBuilder, Columns, DataSource, BlockSpec, Field, FieldType, Filter, FieldOverride, FormSchema, Format, ListQuery, Option, Parsed, ParseOptions, Resource, ResourceDefinition, SourceContext, TableMode, ValuesOf, Tenant, } from './resource.js';
2
+ export type { BulkAction, Column, ColumnBuilder, Columns, DataSource, BlockSpec, Field, FieldType, Filter, FieldOverride, FormSchema, Format, ListPage, ListQuery, Option, Parsed, ParseOptions, Resource, ResourceDefinition, SourceContext, TableMode, ValuesOf, Tenant, } from './resource.js';
3
3
  export { IMAGE_TYPES } from './uploads.js';
4
4
  export type { Storage, StorageContext } from './uploads.js';
5
5
  export { Blocks, defineBlock } from './blocks.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAyBrF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA","sourcesContent":["export { defineResource, fieldsOf, humanize, parse, valuesFrom } from './resource.js'\nexport type {\n BulkAction,\n Column,\n ColumnBuilder,\n Columns,\n DataSource,\n BlockSpec,\n Field,\n FieldType,\n Filter,\n FieldOverride,\n FormSchema,\n Format,\n ListQuery,\n Option,\n Parsed,\n ParseOptions,\n Resource,\n ResourceDefinition,\n SourceContext,\n TableMode,\n ValuesOf,\n Tenant,\n} from './resource.js'\nexport { IMAGE_TYPES } from './uploads.js'\nexport type { Storage, StorageContext } from './uploads.js'\nexport { Blocks, defineBlock } from './blocks.js'\nexport type { Block, BlockDefinition, BlockValue } from './blocks.js'\nexport { memorySource } from './memory.js'\nexport { perTenant } from './tenant.js'\nexport { definePlugin } from './plugin.js'\nexport type { PanelPlugin, RecordChange, Widget } from './plugin.js'\nexport { memoryInstallations, manifestProblems, publicHttps } from './installable.js'\nexport type { Installation, Installations, Installs, ManifestColumn, ManifestProperty, ManifestResource, PluginManifest, PluginRequest } from './installable.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AA0BrF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA","sourcesContent":["export { defineResource, fieldsOf, humanize, parse, valuesFrom } from './resource.js'\nexport type {\n BulkAction,\n Column,\n ColumnBuilder,\n Columns,\n DataSource,\n BlockSpec,\n Field,\n FieldType,\n Filter,\n FieldOverride,\n FormSchema,\n Format,\n ListPage,\n ListQuery,\n Option,\n Parsed,\n ParseOptions,\n Resource,\n ResourceDefinition,\n SourceContext,\n TableMode,\n ValuesOf,\n Tenant,\n} from './resource.js'\nexport { IMAGE_TYPES } from './uploads.js'\nexport type { Storage, StorageContext } from './uploads.js'\nexport { Blocks, defineBlock } from './blocks.js'\nexport type { Block, BlockDefinition, BlockValue } from './blocks.js'\nexport { memorySource } from './memory.js'\nexport { perTenant } from './tenant.js'\nexport { definePlugin } from './plugin.js'\nexport type { PanelPlugin, RecordChange, Widget } from './plugin.js'\nexport { memoryInstallations, manifestProblems, publicHttps } from './installable.js'\nexport type { Installation, Installations, Installs, ManifestColumn, ManifestProperty, ManifestResource, PluginManifest, PluginRequest } from './installable.js'\n"]}
@@ -345,10 +345,15 @@ function remoteSource(installation, resource, allow) {
345
345
  return {
346
346
  async list(query, context) {
347
347
  const answer = await call({ type: 'list', resource, query }, context);
348
- if (!isObject(answer) || !Array.isArray(answer.rows) || typeof answer.total !== 'number') {
349
- throw new Error(`[admin] the plugin ${plugin} answered a list without { rows, total }`);
348
+ const cursor = (value) => typeof value === 'string' || value === null || value === undefined;
349
+ const paged = isObject(answer) && 'next' in answer && cursor(answer.next) && cursor(answer.prev);
350
+ if (!isObject(answer) || !Array.isArray(answer.rows) || (typeof answer.total !== 'number' && !paged)) {
351
+ throw new Error(`[admin] the plugin ${plugin} answered a list without { rows, total } or { rows, next }`);
350
352
  }
351
- return { rows: answer.rows.map((value) => row(value, plugin)), total: answer.total };
353
+ const rows = answer.rows.map((value) => row(value, plugin));
354
+ return paged
355
+ ? { rows, next: answer.next ?? null, prev: answer.prev ?? null }
356
+ : { rows, total: answer.total };
352
357
  },
353
358
  async find(id, context) {
354
359
  const answer = await call({ type: 'find', resource, id }, context);
@@ -1 +1 @@
1
- {"version":3,"file":"installable.js","sourceRoot":"","sources":["../src/installable.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugins installed while the panel runs, with no rebuild: a manifest that\n * says what the plugin adds, and an endpoint of its own that answers for it.\n *\n * Nothing a plugin wrote runs here. Its resources are drawn by the panel's\n * own components from data, its rows arrive as JSON and are shown as text,\n * and every call to it is signed with a secret made at install and scoped to\n * the tenant that installed it. What it may see of the app's own records is\n * what its scopes say, granted by a person who read them on the review page:\n *\n * read:products told when a product changes, and which product one of\n * its own records points to\n *\n * A build-time plugin (definePlugin) is code, trusted like the app's own.\n * This is the other kind: installed by a person, trusted with what they\n * granted and nothing more. design/installable-admin-plugins.md has the\n * reasoning and what is left out.\n */\nimport { ActionValidationError } from '@rsc-kit/core/action'\nimport type { RecordChange } from './plugin.js'\nimport { defineResource } from './resource.js'\nimport type { DataSource, Format, FormSchema, ListQuery, Resource, SourceContext, Tenant } from './resource.js'\nimport { newSecret, signedHeaders } from './signing.js'\n\n/** A plugin, as it describes itself at its manifest url. */\nexport interface PluginManifest {\n /** Lowercase letters, digits and dashes; unique within a tenant. */\n name: string\n label: string\n description?: string\n version?: string\n /** Where the panel POSTs every call. Same origin as the manifest. */\n endpoint: string\n /** What it may see of the app's own resources: read:<resource>. */\n scopes: string[]\n resources: ManifestResource[]\n /** Resources whose changes it is told about: its own, or ones it may read. */\n events?: string[]\n}\n\nexport type ManifestFormat = Exclude<Format, 'relation'>\n\nexport interface ManifestColumn {\n key: string\n label?: string\n format?: ManifestFormat\n /** Shown by that resource's title: one of the plugin's own, or one it may read. */\n relation?: string\n sortable?: boolean\n searchable?: boolean\n}\n\n/** The JSON Schema a manifest's form is written in: flat, one level, scalar fields. */\nexport interface ManifestProperty {\n type: 'string' | 'number' | 'integer' | 'boolean'\n format?: 'email' | 'date' | 'date-time' | 'textarea'\n enum?: string[]\n minLength?: number\n maxLength?: number\n minimum?: number\n maximum?: number\n /** For the browser's pattern attribute only: the plugin checks it, not the panel. */\n pattern?: string\n title?: string\n description?: string\n /** A belongsTo, drawn as a select of that resource's rows. */\n relation?: string\n}\n\nexport interface ManifestResource {\n name: string\n label?: string\n singular?: string\n key: string\n title: string\n description?: string\n columns: ManifestColumn[]\n form: { type: 'object'; properties: Record<string, ManifestProperty>; required?: string[] }\n filters?: string[]\n perPage?: number\n mode?: 'server' | 'client'\n readOnly?: boolean\n bulkActions?: { name: string; label: string; done?: string; destructive?: boolean }[]\n}\n\n/** One plugin installed for one tenant. The secret signs every call to it. */\nexport interface Installation {\n /** Random; the plugin finds this installation's secret by it. */\n id: string\n manifestUrl: string\n manifest: PluginManifest\n secret: string\n /** Until someone confirms they copied the secret into the plugin, the plugins page shows it. */\n secretSeen: boolean\n installedAt: string\n}\n\n/**\n * Where installations are kept: the app's database, in a real app. Each call\n * names the tenant, and a store answers for that tenant alone - one team's\n * installs never reach another's panel. Keep `secret` encrypted at rest.\n */\nexport interface Installations {\n list(tenant: Tenant | null): Promise<Installation[]>\n add(tenant: Tenant | null, installation: Installation): Promise<void>\n remove(tenant: Tenant | null, id: string): Promise<void>\n /** The secret was copied into the plugin; stop showing it. */\n seen(tenant: Tenant | null, id: string): Promise<void>\n}\n\nexport interface Installs {\n store: Installations\n /**\n * Whether the panel may call this url: a manifest's, and so its endpoint's.\n * By default https to a public host name - not localhost, not an address.\n * The panel's server makes the request, so a url that reaches your own\n * network is the thing to refuse; allow what your plugins are, and in\n * development, localhost.\n */\n allow?: (url: URL) => boolean\n /** Who may install and uninstall. The panel's authorize by default; an owner, usually. */\n manage?: () => boolean | Promise<boolean>\n}\n\n/** Keyed by the tenant; on globalThis because a dev server can evaluate this module twice. */\nconst MEMORY = Symbol.for('@rsc-kit/press.installations')\n\n/** Installations in memory: for a demo, a test, a single process. Gone on restart. */\nexport function memoryInstallations(name = 'default'): Installations {\n const all = ((globalThis as Record<symbol, unknown>)[MEMORY] ??= new Map()) as Map<string, Map<string, Installation[]>>\n const kept = all.get(name) ?? new Map<string, Installation[]>()\n\n all.set(name, kept)\n\n const of = (tenant: Tenant | null) => tenant?.id ?? ''\n\n return {\n list: async (tenant) => [...(kept.get(of(tenant)) ?? [])],\n add: async (tenant, installation) => void kept.set(of(tenant), [...(kept.get(of(tenant)) ?? []), installation]),\n remove: async (tenant, id) => void kept.set(of(tenant), (kept.get(of(tenant)) ?? []).filter((candidate) => candidate.id !== id)),\n seen: async (tenant, id) =>\n void kept.set(\n of(tenant),\n (kept.get(of(tenant)) ?? []).map((candidate) => (candidate.id === id ? { ...candidate, secretSeen: true } : candidate)),\n ),\n }\n}\n\n// ---------------------------------------------------------------- the manifest\n\nconst NAME = /^[a-z0-9][a-z0-9-]{0,39}$/\nconst KEY = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/\nconst FORMATS: ManifestFormat[] = ['text', 'money', 'badge', 'boolean', 'date']\n/** The panel's own url segments under its path, which no resource may take. */\nexport const RESERVED = ['plugins', 'new']\n\nconst isObject = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)\n\nfunction text(value: unknown, max: number): value is string {\n return typeof value === 'string' && value.trim() !== '' && value.length <= max\n}\n\n/** Keys a manifest object may have. One it does not know is refused: a newer field read as nothing could be a permission ignored. */\nfunction only(where: string, value: Record<string, unknown>, keys: string[], problems: string[]) {\n for (const key of Object.keys(value)) if (!keys.includes(key)) problems.push(`${where}: unknown field ${JSON.stringify(key)}`)\n}\n\n/**\n * Every problem with a manifest, or none. Checked when it is reviewed, when it\n * is installed, and on every request after - the app's resources can change\n * under an installed plugin, and one that no longer fits is left out.\n */\nexport function manifestProblems(manifest: unknown, manifestUrl: string, host: string[]): string[] {\n const problems: string[] = []\n\n if (!isObject(manifest)) return ['the manifest is not a JSON object']\n\n only('manifest', manifest, ['name', 'label', 'description', 'version', 'endpoint', 'scopes', 'resources', 'events'], problems)\n\n if (typeof manifest.name !== 'string' || !NAME.test(manifest.name)) problems.push('name: lowercase letters, digits and dashes, at most 40')\n if (!text(manifest.label, 60)) problems.push('label: required, at most 60 characters')\n if (manifest.description !== undefined && !text(manifest.description, 300)) problems.push('description: at most 300 characters')\n if (manifest.version !== undefined && !text(manifest.version, 30)) problems.push('version: at most 30 characters')\n\n try {\n const endpoint = new URL(String(manifest.endpoint), manifestUrl)\n\n if (endpoint.origin !== new URL(manifestUrl).origin) problems.push('endpoint: must be on the same origin as the manifest')\n } catch {\n problems.push('endpoint: not a url')\n }\n\n const scopes = Array.isArray(manifest.scopes) ? manifest.scopes : null\n const readable: string[] = []\n\n if (!scopes) problems.push('scopes: a list, empty if it reads nothing of yours')\n else {\n for (const scope of scopes) {\n const match = typeof scope === 'string' ? /^read:([a-z0-9][a-z0-9-]*)$/.exec(scope) : null\n\n if (!match) problems.push(`scopes: ${JSON.stringify(scope)} is not read:<resource>`)\n else if (!host.includes(match[1]!)) problems.push(`scopes: ${scope} names a resource this panel does not have`)\n else readable.push(match[1]!)\n }\n }\n\n const resources = Array.isArray(manifest.resources) ? manifest.resources : null\n\n if (!resources || resources.length === 0 || resources.length > 20) {\n problems.push('resources: a list of 1 to 20')\n\n return problems\n }\n\n const own = resources.flatMap((resource) => (isObject(resource) && typeof resource.name === 'string' ? [resource.name] : []))\n const reachable = (name: string) => own.includes(name) || readable.includes(name)\n\n for (const [index, resource] of resources.entries()) {\n const where = `resources[${index}]`\n\n if (!isObject(resource)) {\n problems.push(`${where}: not an object`)\n continue\n }\n\n only(\n where,\n resource,\n ['name', 'label', 'singular', 'key', 'title', 'description', 'columns', 'form', 'filters', 'perPage', 'mode', 'readOnly', 'bulkActions'],\n problems,\n )\n\n const name = typeof resource.name === 'string' && NAME.test(resource.name) ? resource.name : null\n\n if (!name) problems.push(`${where}.name: lowercase letters, digits and dashes`)\n else if (RESERVED.includes(name)) problems.push(`${where}.name: ${name} is the panel's own`)\n else if (host.includes(name)) problems.push(`${where}.name: this panel already has a resource named ${name}`)\n else if (own.indexOf(name) !== index) problems.push(`${where}.name: two resources are named ${name}`)\n\n for (const key of ['label', 'singular', 'description'] as const) {\n if (resource[key] !== undefined && !text(resource[key], key === 'description' ? 300 : 60)) problems.push(`${where}.${key}: too long, or empty`)\n }\n\n const form = resource.form\n const properties = isObject(form) && form.type === 'object' && isObject(form.properties) ? form.properties : null\n\n if (!properties || Object.keys(properties).length === 0 || Object.keys(properties).length > 50) {\n problems.push(`${where}.form: a JSON Schema object with 1 to 50 properties`)\n continue\n }\n\n only(`${where}.form`, form as Record<string, unknown>, ['type', 'properties', 'required'], problems)\n\n const fieldNames = Object.keys(properties)\n\n if (form && (form as { required?: unknown }).required !== undefined) {\n const required = (form as { required?: unknown }).required\n\n if (!Array.isArray(required) || required.some((field) => !fieldNames.includes(field as string))) {\n problems.push(`${where}.form.required: names that are properties`)\n }\n }\n\n for (const [field, property] of Object.entries(properties)) {\n const at = `${where}.form.properties.${field}`\n\n if (!KEY.test(field)) problems.push(`${at}: not a field name`)\n if (!isObject(property)) {\n problems.push(`${at}: not an object`)\n continue\n }\n\n only(at, property, ['type', 'format', 'enum', 'minLength', 'maxLength', 'minimum', 'maximum', 'pattern', 'title', 'description', 'relation'], problems)\n\n if (!['string', 'number', 'integer', 'boolean'].includes(property.type as string)) problems.push(`${at}.type: string, number, integer or boolean`)\n if (property.format !== undefined && !['email', 'date', 'date-time', 'textarea'].includes(property.format as string)) {\n problems.push(`${at}.format: email, date, date-time or textarea`)\n }\n if (property.enum !== undefined && (!Array.isArray(property.enum) || property.enum.length === 0 || property.enum.some((option) => !text(option, 60)))) {\n problems.push(`${at}.enum: a list of strings`)\n }\n for (const bound of ['minLength', 'maxLength', 'minimum', 'maximum'] as const) {\n if (property[bound] !== undefined && typeof property[bound] !== 'number') problems.push(`${at}.${bound}: a number`)\n }\n // Only the browser runs it - a pattern run on the server is a regular\n // expression a stranger wrote, and one can be made to take minutes.\n if (property.pattern !== undefined && !text(property.pattern, 200)) problems.push(`${at}.pattern: at most 200 characters`)\n if (property.relation !== undefined && !(typeof property.relation === 'string' && reachable(property.relation))) {\n problems.push(`${at}.relation: ${String(property.relation)} is neither its own resource nor one its scopes read`)\n }\n }\n\n const fieldOf = (key: unknown) => typeof key === 'string' && (fieldNames.includes(key) || key === resource.key)\n\n if (typeof resource.key !== 'string' || !KEY.test(resource.key)) problems.push(`${where}.key: a field name`)\n if (!fieldOf(resource.title)) problems.push(`${where}.title: one of the form's fields, or the key`)\n\n const columns = Array.isArray(resource.columns) ? resource.columns : null\n\n if (!columns || columns.length === 0 || columns.length > 30) problems.push(`${where}.columns: a list of 1 to 30`)\n else {\n for (const [at, column] of columns.entries()) {\n const where2 = `${where}.columns[${at}]`\n\n if (!isObject(column)) {\n problems.push(`${where2}: not an object`)\n continue\n }\n\n only(where2, column, ['key', 'label', 'format', 'relation', 'sortable', 'searchable'], problems)\n\n if (typeof column.key !== 'string' || !KEY.test(column.key)) problems.push(`${where2}.key: a field name`)\n if (column.label !== undefined && !text(column.label, 60)) problems.push(`${where2}.label: at most 60 characters`)\n if (column.format !== undefined && !FORMATS.includes(column.format as ManifestFormat)) problems.push(`${where2}.format: ${FORMATS.join(', ')}`)\n if (column.relation !== undefined && !(typeof column.relation === 'string' && reachable(column.relation))) {\n problems.push(`${where2}.relation: ${String(column.relation)} is neither its own resource nor one its scopes read`)\n }\n }\n }\n\n if (resource.filters !== undefined) {\n const filters = Array.isArray(resource.filters) ? resource.filters : []\n\n for (const filter of filters) {\n if (typeof filter !== 'string' || !(properties[filter] as ManifestProperty | undefined)?.enum) problems.push(`${where}.filters: ${String(filter)} is not an enum field`)\n }\n }\n\n if (resource.perPage !== undefined && !(Number.isInteger(resource.perPage) && (resource.perPage as number) >= 1 && (resource.perPage as number) <= 100)) {\n problems.push(`${where}.perPage: 1 to 100`)\n }\n if (resource.mode !== undefined && resource.mode !== 'server' && resource.mode !== 'client') problems.push(`${where}.mode: server or client`)\n if (resource.readOnly !== undefined && typeof resource.readOnly !== 'boolean') problems.push(`${where}.readOnly: true or false`)\n\n if (resource.bulkActions !== undefined) {\n const actions = Array.isArray(resource.bulkActions) ? resource.bulkActions : null\n\n if (!actions || actions.length > 10) problems.push(`${where}.bulkActions: a list of at most 10`)\n else if (resource.readOnly === true && actions.length > 0) problems.push(`${where}.bulkActions: a read-only resource has none`)\n else {\n for (const action of actions) {\n if (!isObject(action) || typeof action.name !== 'string' || !NAME.test(action.name) || action.name === 'delete' || !text(action.label, 40)) {\n problems.push(`${where}.bulkActions: each needs a name (not delete) and a label`)\n }\n }\n }\n }\n }\n\n if (manifest.events !== undefined) {\n const events = Array.isArray(manifest.events) ? manifest.events : null\n\n if (!events) problems.push('events: a list of resource names')\n else {\n for (const event of events) {\n if (typeof event !== 'string' || !reachable(event)) problems.push(`events: ${String(event)} is neither its own resource nor one its scopes read`)\n }\n }\n }\n\n return problems\n}\n\n/** The scopes, as the person installing reads them. */\nexport function scopeWords(manifest: PluginManifest, labelOf: (resource: string) => string): string[] {\n return manifest.scopes.map((scope) => {\n const resource = labelOf(scope.slice('read:'.length)).toLowerCase()\n\n return `See your ${resource}: be told when one changes, and which one its own records point to.`\n })\n}\n\n// ---------------------------------------------------------- reaching a plugin\n\n/** Https to a name: not localhost, and not an address, which is how a private network is reached by url. */\nexport function publicHttps(url: URL): boolean {\n if (url.protocol !== 'https:') return false\n\n const host = url.hostname.replace(/^\\[|\\]$/g, '')\n\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local') || host.endsWith('.internal')) return false\n if (/^[\\d.]+$/.test(host) || host.includes(':')) return false\n\n return true\n}\n\nconst MANIFEST_BYTES = 256 * 1024\nconst ANSWER_BYTES = 5 * 1024 * 1024\n\n/** A response's body, up to a limit: a plugin that answers with gigabytes is a plugin that is wrong. */\nasync function bounded(response: Response, limit: number): Promise<string> {\n const declared = Number(response.headers.get('content-length') ?? 0)\n\n if (declared > limit) throw new Error(`[admin] the answer is ${declared} bytes, over ${limit}`)\n\n const body = await response.text()\n\n if (body.length > limit) throw new Error(`[admin] the answer is over ${limit} bytes`)\n\n return body\n}\n\n/** A manifest, fetched and checked. Redirects are refused: a url allowed once must not lead somewhere that was not. */\nexport async function fetchManifest(manifestUrl: string, allow: (url: URL) => boolean, host: string[]): Promise<PluginManifest> {\n let url: URL\n\n try {\n url = new URL(manifestUrl)\n } catch {\n throw new Error('That is not a url.')\n }\n\n if (!allow(url)) throw new Error('This panel does not install plugins from that address.')\n\n const response = await fetch(url, { headers: { accept: 'application/json' }, redirect: 'error', signal: AbortSignal.timeout(5000) }).catch(() => null)\n\n if (!response?.ok) throw new Error(`The manifest could not be fetched${response ? ` (${response.status})` : ''}.`)\n\n let manifest: unknown\n\n try {\n manifest = JSON.parse(await bounded(response, MANIFEST_BYTES))\n } catch {\n throw new Error('The manifest is not JSON.')\n }\n\n const problems = manifestProblems(manifest, url.href, host)\n\n if (problems.length > 0) throw new Error(`The manifest has problems: ${problems.join('; ')}.`)\n\n const checked = manifest as PluginManifest\n\n return { ...checked, endpoint: new URL(checked.endpoint, url).href }\n}\n\n/** A stable digest of a manifest: what was reviewed is what is installed, or the install is refused. */\nexport async function digestOf(manifest: PluginManifest): Promise<string> {\n const bytes = new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(manifest))))\n\n return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\nexport function newInstallation(manifestUrl: string, manifest: PluginManifest): Installation {\n return { id: crypto.randomUUID(), manifestUrl, manifest, secret: newSecret(), secretSeen: false, installedAt: new Date().toISOString() }\n}\n\n/** What a plugin's endpoint is sent. `tenant` is the installation's, never one the browser named. */\nexport type PluginCall =\n | { type: 'list'; resource: string; query: ListQuery }\n | { type: 'find'; resource: string; id: string }\n | { type: 'create'; resource: string; values: Record<string, unknown> }\n | { type: 'update'; resource: string; id: string; values: Record<string, unknown> }\n | { type: 'delete'; resource: string; id: string }\n | { type: 'bulk'; resource: string; action: string; ids: string[] }\n | { type: 'change'; change: RecordChange }\n\nexport type PluginRequest = PluginCall & { tenant: Tenant | null }\n\n/**\n * One signed call. A 404 is null; a 422 with `{ errors: { field: [message] } }`\n * is the form's field errors, exactly as Laravel's are; anything else that is\n * not a 2xx is a failure with the plugin's name on it.\n */\nexport async function callPlugin(\n installation: Installation,\n call: PluginCall,\n context: SourceContext,\n allow: (url: URL) => boolean,\n timeout = 10_000,\n): Promise<unknown> {\n const endpoint = new URL(installation.manifest.endpoint)\n const name = installation.manifest.name\n\n // Checked again on every call: the rule can tighten after the install.\n if (!allow(endpoint)) throw new Error(`[admin] the plugin ${name}'s endpoint is no longer allowed`)\n\n const body = JSON.stringify({ ...call, tenant: context.tenant ? { id: context.tenant.id, name: context.tenant.name } : null })\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json', accept: 'application/json', ...(await signedHeaders(installation.id, installation.secret, body)) },\n body,\n redirect: 'error',\n signal: AbortSignal.timeout(timeout),\n })\n\n if (response.status === 404) return null\n\n const answer = await bounded(response, ANSWER_BYTES)\n let json: unknown = null\n\n try {\n json = answer === '' ? null : JSON.parse(answer)\n } catch {\n throw new Error(`[admin] the plugin ${name} answered ${response.status} with something that is not JSON`)\n }\n\n if (response.status === 422 && isObject(json) && isObject(json.errors)) {\n const errors = Object.fromEntries(\n Object.entries(json.errors).map(([field, messages]) => [field, ([] as unknown[]).concat(messages).map(String).slice(0, 5)]),\n )\n\n throw new ActionValidationError(errors)\n }\n\n if (!response.ok) throw new Error(`[admin] the plugin ${name} answered ${response.status}`)\n\n return json\n}\n\nconst row = (value: unknown, plugin: string): Record<string, unknown> => {\n if (!isObject(value)) throw new Error(`[admin] the plugin ${plugin} answered a row that is not an object`)\n\n return value\n}\n\n/** A resource's five calls, made to the plugin. */\nfunction remoteSource(installation: Installation, resource: string, allow: (url: URL) => boolean): DataSource<Record<string, unknown>, Record<string, unknown>> {\n const plugin = installation.manifest.name\n const call = (body: PluginCall, context: SourceContext) => callPlugin(installation, body, context, allow)\n\n return {\n async list(query, context) {\n const answer = await call({ type: 'list', resource, query }, context)\n\n if (!isObject(answer) || !Array.isArray(answer.rows) || typeof answer.total !== 'number') {\n throw new Error(`[admin] the plugin ${plugin} answered a list without { rows, total }`)\n }\n\n return { rows: answer.rows.map((value) => row(value, plugin)), total: answer.total }\n },\n async find(id, context) {\n const answer = await call({ type: 'find', resource, id }, context)\n\n return isObject(answer) && answer.row !== undefined && answer.row !== null ? row(answer.row, plugin) : null\n },\n async create(values, context) {\n return row((await call({ type: 'create', resource, values }, context)) as { row?: unknown } | null, plugin).row as Record<string, unknown>\n },\n async update(id, values, context) {\n return row((await call({ type: 'update', resource, id, values }, context)) as { row?: unknown } | null, plugin).row as Record<string, unknown>\n },\n async delete(id, context) {\n await call({ type: 'delete', resource, id }, context)\n },\n }\n}\n\n// ------------------------------------------------------------------ the form\n\n/**\n * A Standard Schema from the manifest's JSON Schema, so a plugin's form goes\n * through the same parse as any other. It checks what is cheap and safe to\n * check here - types, required, enums, lengths, bounds - and never `pattern`;\n * the plugin's endpoint checks everything again and answers 422.\n */\nexport function jsonSchemaForm(form: ManifestResource['form']): FormSchema {\n const required = new Set(form.required ?? [])\n\n const validate = (value: unknown): { value: Record<string, unknown> } | { issues: { message: string; path: string[] }[] } => {\n if (!isObject(value)) return { issues: [{ message: 'Expected an object', path: [] }] }\n\n const out: Record<string, unknown> = {}\n const issues: { message: string; path: string[] }[] = []\n\n for (const [name, property] of Object.entries(form.properties)) {\n const given = value[name]\n const fail = (message: string) => issues.push({ message, path: [name] })\n\n if (given === undefined || given === null || given === '') {\n if (required.has(name) && property.type !== 'boolean') fail('Required')\n continue\n }\n\n if (property.type === 'boolean') {\n if (typeof given !== 'boolean') fail('Expected true or false')\n else out[name] = given\n continue\n }\n\n if (property.type === 'number' || property.type === 'integer') {\n const number = typeof given === 'number' ? given : Number(given)\n\n if (!Number.isFinite(number)) fail('Expected a number')\n else if (property.type === 'integer' && !Number.isInteger(number)) fail('Expected a whole number')\n else if (property.minimum !== undefined && number < property.minimum) fail(`At least ${property.minimum}`)\n else if (property.maximum !== undefined && number > property.maximum) fail(`At most ${property.maximum}`)\n else out[name] = number\n continue\n }\n\n const string = String(given)\n\n if (property.enum && !property.enum.includes(string)) fail(`One of ${property.enum.join(', ')}`)\n else if (property.minLength !== undefined && string.length < property.minLength) fail(`At least ${property.minLength} characters`)\n else if (property.maxLength !== undefined && string.length > property.maxLength) fail(`At most ${property.maxLength} characters`)\n else out[name] = string\n }\n\n return issues.length > 0 ? { issues } : { value: out }\n }\n\n const json = {\n type: 'object',\n required: form.required ?? [],\n properties: Object.fromEntries(\n Object.entries(form.properties).map(([name, { relation: _relation, title: _title, format, ...property }]) => [\n name,\n { ...property, ...(format && format !== 'textarea' ? { format } : {}) },\n ]),\n ),\n }\n\n return {\n '~standard': {\n version: 1,\n vendor: '@rsc-kit/press/plugin',\n validate,\n jsonSchema: { input: () => json, output: () => json },\n },\n } as unknown as FormSchema\n}\n\n/** An installation's resources, as the panel's own. */\nexport function resourcesOf(installation: Installation, allow: (url: URL) => boolean): Resource[] {\n return installation.manifest.resources.map((described) =>\n defineResource<Record<string, unknown>>()({\n name: described.name,\n ...(described.label ? { label: described.label } : {}),\n ...(described.singular ? { singular: described.singular } : {}),\n key: described.key,\n title: described.title,\n source: remoteSource(installation, described.name, allow),\n table: (col) =>\n described.columns.map((column) => {\n const built = column.relation ? col.relation(column.key, column.relation) : col[column.format ?? 'text'](column.key)\n\n if (column.label) built.label(column.label)\n if (column.sortable) built.sortable()\n if (column.searchable) built.searchable()\n\n return built\n }),\n form: jsonSchemaForm(described.form),\n fields: Object.fromEntries(\n Object.entries(described.form.properties).flatMap(([name, property]) => {\n const override = {\n ...(property.title ? { label: property.title } : {}),\n ...(property.description ? { description: property.description } : {}),\n ...(property.format === 'textarea' ? { type: 'textarea' as const } : {}),\n ...(property.relation ? { relation: property.relation } : {}),\n }\n\n return Object.keys(override).length > 0 ? [[name, override]] : []\n }),\n ),\n ...(described.filters ? { filters: described.filters } : {}),\n ...(described.perPage ? { perPage: described.perPage } : {}),\n ...(described.mode ? { mode: described.mode } : {}),\n ...(described.readOnly ? { readOnly: true } : {}),\n ...(described.description ? { description: described.description } : {}),\n bulkActions: (described.bulkActions ?? []).map((action) => ({\n name: action.name,\n label: action.label,\n ...(action.done ? { done: action.done } : {}),\n ...(action.destructive ? { destructive: true } : {}),\n run: async (rows: Record<string, unknown>[], context: SourceContext) => {\n const answer = await callPlugin(\n installation,\n { type: 'bulk', resource: described.name, action: action.name, ids: rows.map((found) => String(found[described.key])) },\n context,\n allow,\n )\n\n return isObject(answer) && typeof answer.message === 'string' ? answer.message.slice(0, 200) : undefined\n },\n })),\n }),\n )\n}\n\n/** Whether a plugin is told about a change: one of its events, which its scopes or ownership allow. */\nexport function wantsChange(installation: Installation, change: RecordChange): boolean {\n return installation.manifest.events?.includes(change.resource) ?? false\n}\n"]}
1
+ {"version":3,"file":"installable.js","sourceRoot":"","sources":["../src/installable.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugins installed while the panel runs, with no rebuild: a manifest that\n * says what the plugin adds, and an endpoint of its own that answers for it.\n *\n * Nothing a plugin wrote runs here. Its resources are drawn by the panel's\n * own components from data, its rows arrive as JSON and are shown as text,\n * and every call to it is signed with a secret made at install and scoped to\n * the tenant that installed it. What it may see of the app's own records is\n * what its scopes say, granted by a person who read them on the review page:\n *\n * read:products told when a product changes, and which product one of\n * its own records points to\n *\n * A build-time plugin (definePlugin) is code, trusted like the app's own.\n * This is the other kind: installed by a person, trusted with what they\n * granted and nothing more. design/installable-admin-plugins.md has the\n * reasoning and what is left out.\n */\nimport { ActionValidationError } from '@rsc-kit/core/action'\nimport type { RecordChange } from './plugin.js'\nimport { defineResource } from './resource.js'\nimport type { DataSource, Format, FormSchema, ListQuery, Resource, SourceContext, Tenant } from './resource.js'\nimport { newSecret, signedHeaders } from './signing.js'\n\n/** A plugin, as it describes itself at its manifest url. */\nexport interface PluginManifest {\n /** Lowercase letters, digits and dashes; unique within a tenant. */\n name: string\n label: string\n description?: string\n version?: string\n /** Where the panel POSTs every call. Same origin as the manifest. */\n endpoint: string\n /** What it may see of the app's own resources: read:<resource>. */\n scopes: string[]\n resources: ManifestResource[]\n /** Resources whose changes it is told about: its own, or ones it may read. */\n events?: string[]\n}\n\nexport type ManifestFormat = Exclude<Format, 'relation'>\n\nexport interface ManifestColumn {\n key: string\n label?: string\n format?: ManifestFormat\n /** Shown by that resource's title: one of the plugin's own, or one it may read. */\n relation?: string\n sortable?: boolean\n searchable?: boolean\n}\n\n/** The JSON Schema a manifest's form is written in: flat, one level, scalar fields. */\nexport interface ManifestProperty {\n type: 'string' | 'number' | 'integer' | 'boolean'\n format?: 'email' | 'date' | 'date-time' | 'textarea'\n enum?: string[]\n minLength?: number\n maxLength?: number\n minimum?: number\n maximum?: number\n /** For the browser's pattern attribute only: the plugin checks it, not the panel. */\n pattern?: string\n title?: string\n description?: string\n /** A belongsTo, drawn as a select of that resource's rows. */\n relation?: string\n}\n\nexport interface ManifestResource {\n name: string\n label?: string\n singular?: string\n key: string\n title: string\n description?: string\n columns: ManifestColumn[]\n form: { type: 'object'; properties: Record<string, ManifestProperty>; required?: string[] }\n filters?: string[]\n perPage?: number\n mode?: 'server' | 'client'\n readOnly?: boolean\n bulkActions?: { name: string; label: string; done?: string; destructive?: boolean }[]\n}\n\n/** One plugin installed for one tenant. The secret signs every call to it. */\nexport interface Installation {\n /** Random; the plugin finds this installation's secret by it. */\n id: string\n manifestUrl: string\n manifest: PluginManifest\n secret: string\n /** Until someone confirms they copied the secret into the plugin, the plugins page shows it. */\n secretSeen: boolean\n installedAt: string\n}\n\n/**\n * Where installations are kept: the app's database, in a real app. Each call\n * names the tenant, and a store answers for that tenant alone - one team's\n * installs never reach another's panel. Keep `secret` encrypted at rest.\n */\nexport interface Installations {\n list(tenant: Tenant | null): Promise<Installation[]>\n add(tenant: Tenant | null, installation: Installation): Promise<void>\n remove(tenant: Tenant | null, id: string): Promise<void>\n /** The secret was copied into the plugin; stop showing it. */\n seen(tenant: Tenant | null, id: string): Promise<void>\n}\n\nexport interface Installs {\n store: Installations\n /**\n * Whether the panel may call this url: a manifest's, and so its endpoint's.\n * By default https to a public host name - not localhost, not an address.\n * The panel's server makes the request, so a url that reaches your own\n * network is the thing to refuse; allow what your plugins are, and in\n * development, localhost.\n */\n allow?: (url: URL) => boolean\n /** Who may install and uninstall. The panel's authorize by default; an owner, usually. */\n manage?: () => boolean | Promise<boolean>\n}\n\n/** Keyed by the tenant; on globalThis because a dev server can evaluate this module twice. */\nconst MEMORY = Symbol.for('@rsc-kit/press.installations')\n\n/** Installations in memory: for a demo, a test, a single process. Gone on restart. */\nexport function memoryInstallations(name = 'default'): Installations {\n const all = ((globalThis as Record<symbol, unknown>)[MEMORY] ??= new Map()) as Map<string, Map<string, Installation[]>>\n const kept = all.get(name) ?? new Map<string, Installation[]>()\n\n all.set(name, kept)\n\n const of = (tenant: Tenant | null) => tenant?.id ?? ''\n\n return {\n list: async (tenant) => [...(kept.get(of(tenant)) ?? [])],\n add: async (tenant, installation) => void kept.set(of(tenant), [...(kept.get(of(tenant)) ?? []), installation]),\n remove: async (tenant, id) => void kept.set(of(tenant), (kept.get(of(tenant)) ?? []).filter((candidate) => candidate.id !== id)),\n seen: async (tenant, id) =>\n void kept.set(\n of(tenant),\n (kept.get(of(tenant)) ?? []).map((candidate) => (candidate.id === id ? { ...candidate, secretSeen: true } : candidate)),\n ),\n }\n}\n\n// ---------------------------------------------------------------- the manifest\n\nconst NAME = /^[a-z0-9][a-z0-9-]{0,39}$/\nconst KEY = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/\nconst FORMATS: ManifestFormat[] = ['text', 'money', 'badge', 'boolean', 'date']\n/** The panel's own url segments under its path, which no resource may take. */\nexport const RESERVED = ['plugins', 'new']\n\nconst isObject = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)\n\nfunction text(value: unknown, max: number): value is string {\n return typeof value === 'string' && value.trim() !== '' && value.length <= max\n}\n\n/** Keys a manifest object may have. One it does not know is refused: a newer field read as nothing could be a permission ignored. */\nfunction only(where: string, value: Record<string, unknown>, keys: string[], problems: string[]) {\n for (const key of Object.keys(value)) if (!keys.includes(key)) problems.push(`${where}: unknown field ${JSON.stringify(key)}`)\n}\n\n/**\n * Every problem with a manifest, or none. Checked when it is reviewed, when it\n * is installed, and on every request after - the app's resources can change\n * under an installed plugin, and one that no longer fits is left out.\n */\nexport function manifestProblems(manifest: unknown, manifestUrl: string, host: string[]): string[] {\n const problems: string[] = []\n\n if (!isObject(manifest)) return ['the manifest is not a JSON object']\n\n only('manifest', manifest, ['name', 'label', 'description', 'version', 'endpoint', 'scopes', 'resources', 'events'], problems)\n\n if (typeof manifest.name !== 'string' || !NAME.test(manifest.name)) problems.push('name: lowercase letters, digits and dashes, at most 40')\n if (!text(manifest.label, 60)) problems.push('label: required, at most 60 characters')\n if (manifest.description !== undefined && !text(manifest.description, 300)) problems.push('description: at most 300 characters')\n if (manifest.version !== undefined && !text(manifest.version, 30)) problems.push('version: at most 30 characters')\n\n try {\n const endpoint = new URL(String(manifest.endpoint), manifestUrl)\n\n if (endpoint.origin !== new URL(manifestUrl).origin) problems.push('endpoint: must be on the same origin as the manifest')\n } catch {\n problems.push('endpoint: not a url')\n }\n\n const scopes = Array.isArray(manifest.scopes) ? manifest.scopes : null\n const readable: string[] = []\n\n if (!scopes) problems.push('scopes: a list, empty if it reads nothing of yours')\n else {\n for (const scope of scopes) {\n const match = typeof scope === 'string' ? /^read:([a-z0-9][a-z0-9-]*)$/.exec(scope) : null\n\n if (!match) problems.push(`scopes: ${JSON.stringify(scope)} is not read:<resource>`)\n else if (!host.includes(match[1]!)) problems.push(`scopes: ${scope} names a resource this panel does not have`)\n else readable.push(match[1]!)\n }\n }\n\n const resources = Array.isArray(manifest.resources) ? manifest.resources : null\n\n if (!resources || resources.length === 0 || resources.length > 20) {\n problems.push('resources: a list of 1 to 20')\n\n return problems\n }\n\n const own = resources.flatMap((resource) => (isObject(resource) && typeof resource.name === 'string' ? [resource.name] : []))\n const reachable = (name: string) => own.includes(name) || readable.includes(name)\n\n for (const [index, resource] of resources.entries()) {\n const where = `resources[${index}]`\n\n if (!isObject(resource)) {\n problems.push(`${where}: not an object`)\n continue\n }\n\n only(\n where,\n resource,\n ['name', 'label', 'singular', 'key', 'title', 'description', 'columns', 'form', 'filters', 'perPage', 'mode', 'readOnly', 'bulkActions'],\n problems,\n )\n\n const name = typeof resource.name === 'string' && NAME.test(resource.name) ? resource.name : null\n\n if (!name) problems.push(`${where}.name: lowercase letters, digits and dashes`)\n else if (RESERVED.includes(name)) problems.push(`${where}.name: ${name} is the panel's own`)\n else if (host.includes(name)) problems.push(`${where}.name: this panel already has a resource named ${name}`)\n else if (own.indexOf(name) !== index) problems.push(`${where}.name: two resources are named ${name}`)\n\n for (const key of ['label', 'singular', 'description'] as const) {\n if (resource[key] !== undefined && !text(resource[key], key === 'description' ? 300 : 60)) problems.push(`${where}.${key}: too long, or empty`)\n }\n\n const form = resource.form\n const properties = isObject(form) && form.type === 'object' && isObject(form.properties) ? form.properties : null\n\n if (!properties || Object.keys(properties).length === 0 || Object.keys(properties).length > 50) {\n problems.push(`${where}.form: a JSON Schema object with 1 to 50 properties`)\n continue\n }\n\n only(`${where}.form`, form as Record<string, unknown>, ['type', 'properties', 'required'], problems)\n\n const fieldNames = Object.keys(properties)\n\n if (form && (form as { required?: unknown }).required !== undefined) {\n const required = (form as { required?: unknown }).required\n\n if (!Array.isArray(required) || required.some((field) => !fieldNames.includes(field as string))) {\n problems.push(`${where}.form.required: names that are properties`)\n }\n }\n\n for (const [field, property] of Object.entries(properties)) {\n const at = `${where}.form.properties.${field}`\n\n if (!KEY.test(field)) problems.push(`${at}: not a field name`)\n if (!isObject(property)) {\n problems.push(`${at}: not an object`)\n continue\n }\n\n only(at, property, ['type', 'format', 'enum', 'minLength', 'maxLength', 'minimum', 'maximum', 'pattern', 'title', 'description', 'relation'], problems)\n\n if (!['string', 'number', 'integer', 'boolean'].includes(property.type as string)) problems.push(`${at}.type: string, number, integer or boolean`)\n if (property.format !== undefined && !['email', 'date', 'date-time', 'textarea'].includes(property.format as string)) {\n problems.push(`${at}.format: email, date, date-time or textarea`)\n }\n if (property.enum !== undefined && (!Array.isArray(property.enum) || property.enum.length === 0 || property.enum.some((option) => !text(option, 60)))) {\n problems.push(`${at}.enum: a list of strings`)\n }\n for (const bound of ['minLength', 'maxLength', 'minimum', 'maximum'] as const) {\n if (property[bound] !== undefined && typeof property[bound] !== 'number') problems.push(`${at}.${bound}: a number`)\n }\n // Only the browser runs it - a pattern run on the server is a regular\n // expression a stranger wrote, and one can be made to take minutes.\n if (property.pattern !== undefined && !text(property.pattern, 200)) problems.push(`${at}.pattern: at most 200 characters`)\n if (property.relation !== undefined && !(typeof property.relation === 'string' && reachable(property.relation))) {\n problems.push(`${at}.relation: ${String(property.relation)} is neither its own resource nor one its scopes read`)\n }\n }\n\n const fieldOf = (key: unknown) => typeof key === 'string' && (fieldNames.includes(key) || key === resource.key)\n\n if (typeof resource.key !== 'string' || !KEY.test(resource.key)) problems.push(`${where}.key: a field name`)\n if (!fieldOf(resource.title)) problems.push(`${where}.title: one of the form's fields, or the key`)\n\n const columns = Array.isArray(resource.columns) ? resource.columns : null\n\n if (!columns || columns.length === 0 || columns.length > 30) problems.push(`${where}.columns: a list of 1 to 30`)\n else {\n for (const [at, column] of columns.entries()) {\n const where2 = `${where}.columns[${at}]`\n\n if (!isObject(column)) {\n problems.push(`${where2}: not an object`)\n continue\n }\n\n only(where2, column, ['key', 'label', 'format', 'relation', 'sortable', 'searchable'], problems)\n\n if (typeof column.key !== 'string' || !KEY.test(column.key)) problems.push(`${where2}.key: a field name`)\n if (column.label !== undefined && !text(column.label, 60)) problems.push(`${where2}.label: at most 60 characters`)\n if (column.format !== undefined && !FORMATS.includes(column.format as ManifestFormat)) problems.push(`${where2}.format: ${FORMATS.join(', ')}`)\n if (column.relation !== undefined && !(typeof column.relation === 'string' && reachable(column.relation))) {\n problems.push(`${where2}.relation: ${String(column.relation)} is neither its own resource nor one its scopes read`)\n }\n }\n }\n\n if (resource.filters !== undefined) {\n const filters = Array.isArray(resource.filters) ? resource.filters : []\n\n for (const filter of filters) {\n if (typeof filter !== 'string' || !(properties[filter] as ManifestProperty | undefined)?.enum) problems.push(`${where}.filters: ${String(filter)} is not an enum field`)\n }\n }\n\n if (resource.perPage !== undefined && !(Number.isInteger(resource.perPage) && (resource.perPage as number) >= 1 && (resource.perPage as number) <= 100)) {\n problems.push(`${where}.perPage: 1 to 100`)\n }\n if (resource.mode !== undefined && resource.mode !== 'server' && resource.mode !== 'client') problems.push(`${where}.mode: server or client`)\n if (resource.readOnly !== undefined && typeof resource.readOnly !== 'boolean') problems.push(`${where}.readOnly: true or false`)\n\n if (resource.bulkActions !== undefined) {\n const actions = Array.isArray(resource.bulkActions) ? resource.bulkActions : null\n\n if (!actions || actions.length > 10) problems.push(`${where}.bulkActions: a list of at most 10`)\n else if (resource.readOnly === true && actions.length > 0) problems.push(`${where}.bulkActions: a read-only resource has none`)\n else {\n for (const action of actions) {\n if (!isObject(action) || typeof action.name !== 'string' || !NAME.test(action.name) || action.name === 'delete' || !text(action.label, 40)) {\n problems.push(`${where}.bulkActions: each needs a name (not delete) and a label`)\n }\n }\n }\n }\n }\n\n if (manifest.events !== undefined) {\n const events = Array.isArray(manifest.events) ? manifest.events : null\n\n if (!events) problems.push('events: a list of resource names')\n else {\n for (const event of events) {\n if (typeof event !== 'string' || !reachable(event)) problems.push(`events: ${String(event)} is neither its own resource nor one its scopes read`)\n }\n }\n }\n\n return problems\n}\n\n/** The scopes, as the person installing reads them. */\nexport function scopeWords(manifest: PluginManifest, labelOf: (resource: string) => string): string[] {\n return manifest.scopes.map((scope) => {\n const resource = labelOf(scope.slice('read:'.length)).toLowerCase()\n\n return `See your ${resource}: be told when one changes, and which one its own records point to.`\n })\n}\n\n// ---------------------------------------------------------- reaching a plugin\n\n/** Https to a name: not localhost, and not an address, which is how a private network is reached by url. */\nexport function publicHttps(url: URL): boolean {\n if (url.protocol !== 'https:') return false\n\n const host = url.hostname.replace(/^\\[|\\]$/g, '')\n\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local') || host.endsWith('.internal')) return false\n if (/^[\\d.]+$/.test(host) || host.includes(':')) return false\n\n return true\n}\n\nconst MANIFEST_BYTES = 256 * 1024\nconst ANSWER_BYTES = 5 * 1024 * 1024\n\n/** A response's body, up to a limit: a plugin that answers with gigabytes is a plugin that is wrong. */\nasync function bounded(response: Response, limit: number): Promise<string> {\n const declared = Number(response.headers.get('content-length') ?? 0)\n\n if (declared > limit) throw new Error(`[admin] the answer is ${declared} bytes, over ${limit}`)\n\n const body = await response.text()\n\n if (body.length > limit) throw new Error(`[admin] the answer is over ${limit} bytes`)\n\n return body\n}\n\n/** A manifest, fetched and checked. Redirects are refused: a url allowed once must not lead somewhere that was not. */\nexport async function fetchManifest(manifestUrl: string, allow: (url: URL) => boolean, host: string[]): Promise<PluginManifest> {\n let url: URL\n\n try {\n url = new URL(manifestUrl)\n } catch {\n throw new Error('That is not a url.')\n }\n\n if (!allow(url)) throw new Error('This panel does not install plugins from that address.')\n\n const response = await fetch(url, { headers: { accept: 'application/json' }, redirect: 'error', signal: AbortSignal.timeout(5000) }).catch(() => null)\n\n if (!response?.ok) throw new Error(`The manifest could not be fetched${response ? ` (${response.status})` : ''}.`)\n\n let manifest: unknown\n\n try {\n manifest = JSON.parse(await bounded(response, MANIFEST_BYTES))\n } catch {\n throw new Error('The manifest is not JSON.')\n }\n\n const problems = manifestProblems(manifest, url.href, host)\n\n if (problems.length > 0) throw new Error(`The manifest has problems: ${problems.join('; ')}.`)\n\n const checked = manifest as PluginManifest\n\n return { ...checked, endpoint: new URL(checked.endpoint, url).href }\n}\n\n/** A stable digest of a manifest: what was reviewed is what is installed, or the install is refused. */\nexport async function digestOf(manifest: PluginManifest): Promise<string> {\n const bytes = new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(manifest))))\n\n return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\nexport function newInstallation(manifestUrl: string, manifest: PluginManifest): Installation {\n return { id: crypto.randomUUID(), manifestUrl, manifest, secret: newSecret(), secretSeen: false, installedAt: new Date().toISOString() }\n}\n\n/** What a plugin's endpoint is sent. `tenant` is the installation's, never one the browser named. */\nexport type PluginCall =\n | { type: 'list'; resource: string; query: ListQuery }\n | { type: 'find'; resource: string; id: string }\n | { type: 'create'; resource: string; values: Record<string, unknown> }\n | { type: 'update'; resource: string; id: string; values: Record<string, unknown> }\n | { type: 'delete'; resource: string; id: string }\n | { type: 'bulk'; resource: string; action: string; ids: string[] }\n | { type: 'change'; change: RecordChange }\n\nexport type PluginRequest = PluginCall & { tenant: Tenant | null }\n\n/**\n * One signed call. A 404 is null; a 422 with `{ errors: { field: [message] } }`\n * is the form's field errors, exactly as Laravel's are; anything else that is\n * not a 2xx is a failure with the plugin's name on it.\n */\nexport async function callPlugin(\n installation: Installation,\n call: PluginCall,\n context: SourceContext,\n allow: (url: URL) => boolean,\n timeout = 10_000,\n): Promise<unknown> {\n const endpoint = new URL(installation.manifest.endpoint)\n const name = installation.manifest.name\n\n // Checked again on every call: the rule can tighten after the install.\n if (!allow(endpoint)) throw new Error(`[admin] the plugin ${name}'s endpoint is no longer allowed`)\n\n const body = JSON.stringify({ ...call, tenant: context.tenant ? { id: context.tenant.id, name: context.tenant.name } : null })\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json', accept: 'application/json', ...(await signedHeaders(installation.id, installation.secret, body)) },\n body,\n redirect: 'error',\n signal: AbortSignal.timeout(timeout),\n })\n\n if (response.status === 404) return null\n\n const answer = await bounded(response, ANSWER_BYTES)\n let json: unknown = null\n\n try {\n json = answer === '' ? null : JSON.parse(answer)\n } catch {\n throw new Error(`[admin] the plugin ${name} answered ${response.status} with something that is not JSON`)\n }\n\n if (response.status === 422 && isObject(json) && isObject(json.errors)) {\n const errors = Object.fromEntries(\n Object.entries(json.errors).map(([field, messages]) => [field, ([] as unknown[]).concat(messages).map(String).slice(0, 5)]),\n )\n\n throw new ActionValidationError(errors)\n }\n\n if (!response.ok) throw new Error(`[admin] the plugin ${name} answered ${response.status}`)\n\n return json\n}\n\nconst row = (value: unknown, plugin: string): Record<string, unknown> => {\n if (!isObject(value)) throw new Error(`[admin] the plugin ${plugin} answered a row that is not an object`)\n\n return value\n}\n\n/** A resource's five calls, made to the plugin. */\nfunction remoteSource(installation: Installation, resource: string, allow: (url: URL) => boolean): DataSource<Record<string, unknown>, Record<string, unknown>> {\n const plugin = installation.manifest.name\n const call = (body: PluginCall, context: SourceContext) => callPlugin(installation, body, context, allow)\n\n return {\n async list(query, context) {\n const answer = await call({ type: 'list', resource, query }, context)\n\n const cursor = (value: unknown) => typeof value === 'string' || value === null || value === undefined\n const paged = isObject(answer) && 'next' in answer && cursor(answer.next) && cursor(answer.prev)\n\n if (!isObject(answer) || !Array.isArray(answer.rows) || (typeof answer.total !== 'number' && !paged)) {\n throw new Error(`[admin] the plugin ${plugin} answered a list without { rows, total } or { rows, next }`)\n }\n\n const rows = answer.rows.map((value) => row(value, plugin))\n\n return paged\n ? { rows, next: (answer.next as string | null) ?? null, prev: (answer.prev as string | null | undefined) ?? null }\n : { rows, total: answer.total as number }\n },\n async find(id, context) {\n const answer = await call({ type: 'find', resource, id }, context)\n\n return isObject(answer) && answer.row !== undefined && answer.row !== null ? row(answer.row, plugin) : null\n },\n async create(values, context) {\n return row((await call({ type: 'create', resource, values }, context)) as { row?: unknown } | null, plugin).row as Record<string, unknown>\n },\n async update(id, values, context) {\n return row((await call({ type: 'update', resource, id, values }, context)) as { row?: unknown } | null, plugin).row as Record<string, unknown>\n },\n async delete(id, context) {\n await call({ type: 'delete', resource, id }, context)\n },\n }\n}\n\n// ------------------------------------------------------------------ the form\n\n/**\n * A Standard Schema from the manifest's JSON Schema, so a plugin's form goes\n * through the same parse as any other. It checks what is cheap and safe to\n * check here - types, required, enums, lengths, bounds - and never `pattern`;\n * the plugin's endpoint checks everything again and answers 422.\n */\nexport function jsonSchemaForm(form: ManifestResource['form']): FormSchema {\n const required = new Set(form.required ?? [])\n\n const validate = (value: unknown): { value: Record<string, unknown> } | { issues: { message: string; path: string[] }[] } => {\n if (!isObject(value)) return { issues: [{ message: 'Expected an object', path: [] }] }\n\n const out: Record<string, unknown> = {}\n const issues: { message: string; path: string[] }[] = []\n\n for (const [name, property] of Object.entries(form.properties)) {\n const given = value[name]\n const fail = (message: string) => issues.push({ message, path: [name] })\n\n if (given === undefined || given === null || given === '') {\n if (required.has(name) && property.type !== 'boolean') fail('Required')\n continue\n }\n\n if (property.type === 'boolean') {\n if (typeof given !== 'boolean') fail('Expected true or false')\n else out[name] = given\n continue\n }\n\n if (property.type === 'number' || property.type === 'integer') {\n const number = typeof given === 'number' ? given : Number(given)\n\n if (!Number.isFinite(number)) fail('Expected a number')\n else if (property.type === 'integer' && !Number.isInteger(number)) fail('Expected a whole number')\n else if (property.minimum !== undefined && number < property.minimum) fail(`At least ${property.minimum}`)\n else if (property.maximum !== undefined && number > property.maximum) fail(`At most ${property.maximum}`)\n else out[name] = number\n continue\n }\n\n const string = String(given)\n\n if (property.enum && !property.enum.includes(string)) fail(`One of ${property.enum.join(', ')}`)\n else if (property.minLength !== undefined && string.length < property.minLength) fail(`At least ${property.minLength} characters`)\n else if (property.maxLength !== undefined && string.length > property.maxLength) fail(`At most ${property.maxLength} characters`)\n else out[name] = string\n }\n\n return issues.length > 0 ? { issues } : { value: out }\n }\n\n const json = {\n type: 'object',\n required: form.required ?? [],\n properties: Object.fromEntries(\n Object.entries(form.properties).map(([name, { relation: _relation, title: _title, format, ...property }]) => [\n name,\n { ...property, ...(format && format !== 'textarea' ? { format } : {}) },\n ]),\n ),\n }\n\n return {\n '~standard': {\n version: 1,\n vendor: '@rsc-kit/press/plugin',\n validate,\n jsonSchema: { input: () => json, output: () => json },\n },\n } as unknown as FormSchema\n}\n\n/** An installation's resources, as the panel's own. */\nexport function resourcesOf(installation: Installation, allow: (url: URL) => boolean): Resource[] {\n return installation.manifest.resources.map((described) =>\n defineResource<Record<string, unknown>>()({\n name: described.name,\n ...(described.label ? { label: described.label } : {}),\n ...(described.singular ? { singular: described.singular } : {}),\n key: described.key,\n title: described.title,\n source: remoteSource(installation, described.name, allow),\n table: (col) =>\n described.columns.map((column) => {\n const built = column.relation ? col.relation(column.key, column.relation) : col[column.format ?? 'text'](column.key)\n\n if (column.label) built.label(column.label)\n if (column.sortable) built.sortable()\n if (column.searchable) built.searchable()\n\n return built\n }),\n form: jsonSchemaForm(described.form),\n fields: Object.fromEntries(\n Object.entries(described.form.properties).flatMap(([name, property]) => {\n const override = {\n ...(property.title ? { label: property.title } : {}),\n ...(property.description ? { description: property.description } : {}),\n ...(property.format === 'textarea' ? { type: 'textarea' as const } : {}),\n ...(property.relation ? { relation: property.relation } : {}),\n }\n\n return Object.keys(override).length > 0 ? [[name, override]] : []\n }),\n ),\n ...(described.filters ? { filters: described.filters } : {}),\n ...(described.perPage ? { perPage: described.perPage } : {}),\n ...(described.mode ? { mode: described.mode } : {}),\n ...(described.readOnly ? { readOnly: true } : {}),\n ...(described.description ? { description: described.description } : {}),\n bulkActions: (described.bulkActions ?? []).map((action) => ({\n name: action.name,\n label: action.label,\n ...(action.done ? { done: action.done } : {}),\n ...(action.destructive ? { destructive: true } : {}),\n run: async (rows: Record<string, unknown>[], context: SourceContext) => {\n const answer = await callPlugin(\n installation,\n { type: 'bulk', resource: described.name, action: action.name, ids: rows.map((found) => String(found[described.key])) },\n context,\n allow,\n )\n\n return isObject(answer) && typeof answer.message === 'string' ? answer.message.slice(0, 200) : undefined\n },\n })),\n }),\n )\n}\n\n/** Whether a plugin is told about a change: one of its events, which its scopes or ownership allow. */\nexport function wantsChange(installation: Installation, change: RecordChange): boolean {\n return installation.manifest.events?.includes(change.resource) ?? false\n}\n"]}
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import type { ComponentType, ReactNode } from 'react';
14
14
  import type { StandardSchemaV1 } from '@rsc-kit/core/standardSchema';
15
+ import type { HostAction, HostFields } from './forms/host.js';
15
16
  import type { Storage } from './uploads.js';
16
17
  /** What the form validates with: any Standard Schema - zod, valibot, arktype, effect. */
17
18
  export type FormSchema = StandardSchemaV1<any, Record<string, unknown>>;
@@ -124,6 +125,12 @@ export interface ListQuery {
124
125
  sort?: string;
125
126
  dir: 'asc' | 'desc';
126
127
  page: number;
128
+ /**
129
+ * The cursor a source that pages by cursor answered as next or prev, from
130
+ * the url's ?cursor=; absent for the first page. Such a source reads this
131
+ * and not page.
132
+ */
133
+ cursor?: string;
127
134
  /**
128
135
  * Null asks for every row: a resource whose table works in the browser
129
136
  * (mode: 'client') is loaded whole, and searched, sorted and paged there.
@@ -156,16 +163,30 @@ export interface SourceContext {
156
163
  * In a panel with tenants, a source answers only for context.tenant. find()
157
164
  * returning null for another tenant's record is what makes its url a 404.
158
165
  */
166
+ /**
167
+ * One page of a list. Counted, { rows, total }, and the table numbers its
168
+ * pages. By cursor, { rows, next, prev } - Laravel's cursorPaginate, a keyset
169
+ * query, an API that hands back a cursor - and the table pages with Previous
170
+ * and Next, asking for the cursor it was given. null is no page that way.
171
+ */
172
+ export type ListPage<Row> = {
173
+ rows: Row[];
174
+ total: number;
175
+ } | {
176
+ rows: Row[];
177
+ next: string | null;
178
+ prev?: string | null;
179
+ total?: number;
180
+ };
159
181
  export interface DataSource<Row, Values> {
160
- list(query: ListQuery, context: SourceContext): Promise<{
161
- rows: Row[];
162
- total: number;
163
- }>;
182
+ list(query: ListQuery, context: SourceContext): Promise<ListPage<Row>>;
164
183
  find(id: string, context: SourceContext): Promise<Row | null>;
165
184
  create(values: Values, context: SourceContext): Promise<Row>;
166
185
  update(id: string, values: Values, context: SourceContext): Promise<Row>;
167
186
  delete(id: string, context: SourceContext): Promise<void>;
168
187
  }
188
+ /** A source for a resource whose saves are a backend's actions: it lists, finds and deletes. */
189
+ export type ReadSource<Row> = Pick<DataSource<Row, never>, 'list' | 'find' | 'delete'>;
169
190
  /**
170
191
  * Where a table's search, filters, sort and paging happen.
171
192
  *
@@ -202,7 +223,13 @@ export interface Resource<Row = any, Schema extends FormSchema = FormSchema> {
202
223
  columns: Column[];
203
224
  filters: Filter[];
204
225
  schema: Schema;
226
+ /** The create form's fields. */
205
227
  fields: Field[];
228
+ /** The edit form's: the update action's, for a resource saved by a backend. */
229
+ editFields: Field[];
230
+ editSchema: FormSchema;
231
+ /** The resources its fields name, known before the fields are: a backend's arrive with the server entry. */
232
+ relations: string[];
206
233
  perPage: number;
207
234
  mode: TableMode;
208
235
  /** Listed and searched, never created, edited or deleted from the panel. */
@@ -249,11 +276,33 @@ export interface ResourceDefinition<Row, Schema extends FormSchema> {
249
276
  /** Under the page's heading. */
250
277
  description?: string;
251
278
  }
279
+ /**
280
+ * A resource saved by the backend's own actions, with no schema in
281
+ * TypeScript: the generated stubs for a Laravel FormRequest or a Go struct.
282
+ * The create form is drawn from what `create` takes, the edit form from what
283
+ * `update` takes, and every check is the backend's - its 422 lands under the
284
+ * fields. `update` is called with the record's key after the values:
285
+ * `update(UpdateProductRequest $request, string $product)`.
286
+ */
287
+ export interface HostResourceDefinition<Row, Create extends HostAction, Update extends HostAction> extends Omit<ResourceDefinition<Row, FormSchema>, 'source' | 'form' | 'fields' | 'readOnly'> {
288
+ /** Lists, finds and deletes; the saves are the actions. */
289
+ source: ReadSource<Row>;
290
+ /** `import { productsStore, productsUpdate } from '../server-actions.generated'`. */
291
+ actions: {
292
+ create: Create;
293
+ update: Update;
294
+ };
295
+ /** Labels, controls and relations the backend's types cannot say. */
296
+ fields?: Partial<Record<HostFields<Create> | HostFields<Update>, FieldOverride>>;
297
+ }
252
298
  /**
253
299
  * Two calls so the row is named and the schema is inferred:
254
300
  * defineResource<Product>()({ ... }).
255
301
  */
256
- export declare function defineResource<Row>(): <Schema extends FormSchema>(definition: ResourceDefinition<Row, Schema>) => Resource<Row, Schema>;
302
+ export declare function defineResource<Row>(): {
303
+ <Schema extends FormSchema>(definition: ResourceDefinition<Row, Schema>): Resource<Row, Schema>;
304
+ <Create extends HostAction, Update extends HostAction>(definition: HostResourceDefinition<Row, Create, Update>): Resource<Row>;
305
+ };
257
306
  /** A field as the app says it, over what the schema said. */
258
307
  export type FieldOverride = Partial<Pick<Field, 'type' | 'label' | 'description' | 'required' | 'relation'>> & {
259
308
  /** A bare value is labelled by humanizing it: 'in_stock' reads "In stock". */