@rsc-kit/press 0.30.51 → 0.30.53
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/README.md +1 -1
- package/dist/admin/panel.js +4 -1
- package/dist/admin/panel.js.map +1 -1
- package/dist/conditions.d.ts +32 -0
- package/dist/conditions.js +25 -0
- package/dist/conditions.js.map +1 -0
- package/dist/forms/actions.d.ts +6 -5
- package/dist/forms/actions.js +13 -8
- package/dist/forms/actions.js.map +1 -1
- package/dist/forms/blueprint.d.ts +99 -0
- package/dist/forms/blueprint.js +141 -0
- package/dist/forms/blueprint.js.map +1 -0
- package/dist/forms/brand.d.ts +26 -0
- package/dist/forms/brand.js +9 -0
- package/dist/forms/brand.js.map +1 -0
- package/dist/forms/host.d.ts +1 -1
- package/dist/forms/host.js.map +1 -1
- package/dist/forms/index.d.ts +3 -3
- package/dist/forms/index.js +1 -1
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/registry.d.ts +6 -1
- package/dist/forms/registry.js +1 -1
- package/dist/forms/registry.js.map +1 -1
- package/dist/forms/theme.d.ts +10 -7
- package/dist/forms/theme.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/resource.d.ts +23 -1
- package/dist/resource.js +80 -16
- package/dist/resource.js.map +1 -1
- package/dist/shadcn/blueprint-form.d.ts +8 -0
- package/dist/shadcn/{public-form.js → blueprint-form.js} +7 -6
- package/dist/shadcn/blueprint-form.js.map +1 -0
- package/dist/shadcn/fields.d.ts +11 -1
- package/dist/shadcn/fields.js +72 -10
- package/dist/shadcn/fields.js.map +1 -1
- package/dist/shadcn/index.d.ts +1 -1
- package/dist/shadcn/index.js +3 -3
- package/dist/shadcn/index.js.map +1 -1
- package/dist/shadcn/resource-form.js +3 -2
- package/dist/shadcn/resource-form.js.map +1 -1
- package/package.json +2 -2
- package/dist/forms/form.d.ts +0 -61
- package/dist/forms/form.js +0 -66
- package/dist/forms/form.js.map +0 -1
- package/dist/shadcn/public-form.d.ts +0 -8
- package/dist/shadcn/public-form.js.map +0 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fieldsOf } from '../resource.js';
|
|
3
|
+
import { submitForm } from './actions.js';
|
|
4
|
+
import { BLUEPRINT } from './brand.js';
|
|
5
|
+
import { hostSchema, requireDrawable } from './host.js';
|
|
6
|
+
import { HONEYPOT } from './honeypot.js';
|
|
7
|
+
import { registerForm } from './registry.js';
|
|
8
|
+
export function defineBlueprint(definition) {
|
|
9
|
+
const { name } = definition;
|
|
10
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
11
|
+
throw new Error(`[forms] a blueprint's name is lowercase letters, digits and dashes: ${JSON.stringify(name)}`);
|
|
12
|
+
}
|
|
13
|
+
if (definition.done !== undefined && definition.redirect !== undefined) {
|
|
14
|
+
throw new Error(`[forms] the blueprint ${name} has both done and redirect; a sent form shows one or goes to the other`);
|
|
15
|
+
}
|
|
16
|
+
const { actions } = definition;
|
|
17
|
+
if ((actions === undefined) === (definition.form === undefined)) {
|
|
18
|
+
throw new Error(`[forms] the blueprint ${name} needs a form schema, or the backend's actions, and not both`);
|
|
19
|
+
}
|
|
20
|
+
if (actions !== undefined) {
|
|
21
|
+
const given = [actions.create, actions.update].filter((action) => action !== undefined);
|
|
22
|
+
if (given.length === 0 || given.some((action) => typeof action !== 'function')) {
|
|
23
|
+
throw new Error(`[forms] the blueprint ${name}'s actions are { create, update }, either or both, each the action the build generated: ` +
|
|
24
|
+
"import { productsStore, productsUpdate } from '../server-actions.generated'");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const overrides = (definition.fields ?? {});
|
|
28
|
+
const host = actions;
|
|
29
|
+
const schemaDefinition = definition;
|
|
30
|
+
// A backend's create may be missing - a blueprint that only edits - so the
|
|
31
|
+
// edit form's description stands in for the create's schema, never used.
|
|
32
|
+
const schema = host ? hostSchema((host.create ?? host.update)) : definition.form;
|
|
33
|
+
const editSchema = host ? hostSchema((host.update ?? host.create)) : schema;
|
|
34
|
+
const create = host
|
|
35
|
+
? host.create
|
|
36
|
+
? (values) => host.create(values)
|
|
37
|
+
: null
|
|
38
|
+
: schemaDefinition.handle
|
|
39
|
+
? async (values) => schemaDefinition.handle(values)
|
|
40
|
+
: null;
|
|
41
|
+
const update = host
|
|
42
|
+
? host.update
|
|
43
|
+
? (values, key) => host.update(values, key)
|
|
44
|
+
: null
|
|
45
|
+
: schemaDefinition.update
|
|
46
|
+
? async (values, key) => schemaDefinition.update(values, key)
|
|
47
|
+
: null;
|
|
48
|
+
const drawn = {};
|
|
49
|
+
const trapped = (fields) => {
|
|
50
|
+
if (definition.honeypot !== false && fields.some((field) => field.name === HONEYPOT)) {
|
|
51
|
+
throw new Error(`[forms] the blueprint ${name} has a field named ${HONEYPOT}, which is the name its bot trap uses; rename it, or honeypot: false`);
|
|
52
|
+
}
|
|
53
|
+
return fields;
|
|
54
|
+
};
|
|
55
|
+
function createFields() {
|
|
56
|
+
if (drawn.create)
|
|
57
|
+
return drawn.create;
|
|
58
|
+
if (host) {
|
|
59
|
+
if (!host.create)
|
|
60
|
+
throw new Error(`[forms] the blueprint ${name} has no create action, so it cannot draw a form that creates; pass an id to edit`);
|
|
61
|
+
requireDrawable({ name: `[forms] the blueprint ${name}'s create`, key: false }, host.create, overrides);
|
|
62
|
+
}
|
|
63
|
+
return (drawn.create = trapped(fieldsOf(schema, overrides)));
|
|
64
|
+
}
|
|
65
|
+
function editFields() {
|
|
66
|
+
if (drawn.edit)
|
|
67
|
+
return drawn.edit;
|
|
68
|
+
if (!host)
|
|
69
|
+
return (drawn.edit = createFields());
|
|
70
|
+
if (!host.update)
|
|
71
|
+
throw new Error(`[forms] the blueprint ${name} has no update action, so it cannot draw a form that edits`);
|
|
72
|
+
requireDrawable({ name: `[forms] the blueprint ${name}'s update`, key: true }, host.update, overrides);
|
|
73
|
+
return (drawn.edit = trapped(fieldsOf(editSchema, overrides)));
|
|
74
|
+
}
|
|
75
|
+
// A backend's types arrive with the server entry, which runs after this
|
|
76
|
+
// module: those fields wait for the first render. A schema's fail here.
|
|
77
|
+
if (!host)
|
|
78
|
+
createFields();
|
|
79
|
+
const relations = Object.values(overrides).flatMap((override) => (override?.relation ? [override.relation] : []));
|
|
80
|
+
if (!host)
|
|
81
|
+
for (const field of createFields())
|
|
82
|
+
if (field.relation && !relations.includes(field.relation))
|
|
83
|
+
relations.push(field.relation);
|
|
84
|
+
registerForm({
|
|
85
|
+
name,
|
|
86
|
+
schema,
|
|
87
|
+
editSchema,
|
|
88
|
+
get fields() {
|
|
89
|
+
return createFields();
|
|
90
|
+
},
|
|
91
|
+
get editFields() {
|
|
92
|
+
return editFields();
|
|
93
|
+
},
|
|
94
|
+
create,
|
|
95
|
+
update,
|
|
96
|
+
redirect: definition.redirect ?? null,
|
|
97
|
+
honeypot: definition.honeypot !== false,
|
|
98
|
+
});
|
|
99
|
+
const { theme } = definition;
|
|
100
|
+
function Form({ values, id }) {
|
|
101
|
+
if (!theme) {
|
|
102
|
+
throw new Error(`[forms] the blueprint ${name} has no theme to draw <${name}.Form /> with: theme: shadcn, from '@rsc-kit/press/shadcn'`);
|
|
103
|
+
}
|
|
104
|
+
const editing = id !== undefined && id !== null;
|
|
105
|
+
if (editing && !update)
|
|
106
|
+
throw new Error(`[forms] <${name}.Form id={...} /> edits, and the blueprint ${name} has no update to save it with`);
|
|
107
|
+
if (!editing && !create)
|
|
108
|
+
throw new Error(`[forms] <${name}.Form /> creates, and the blueprint ${name} has no create to save it with; pass the record's id to edit`);
|
|
109
|
+
const fields = editing ? editFields() : createFields();
|
|
110
|
+
const Draw = theme.BlueprintForm;
|
|
111
|
+
const done = definition.redirect === undefined ? (definition.done ?? (editing ? 'Saved.' : 'Thank you. It was sent.')) : undefined;
|
|
112
|
+
return (_jsx(Draw, { action: submitForm.bind(null, name, editing ? String(id) : null), fields: fields, values: valuesFor(fields, values), submit: definition.submit ?? (editing ? 'Save' : 'Send'), ...(done === undefined ? {} : { done }), ...(definition.honeypot === false ? {} : { honeypot: HONEYPOT }) }));
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
[BLUEPRINT]: true,
|
|
116
|
+
name,
|
|
117
|
+
schema,
|
|
118
|
+
editSchema,
|
|
119
|
+
get fields() {
|
|
120
|
+
return createFields();
|
|
121
|
+
},
|
|
122
|
+
get editFields() {
|
|
123
|
+
return editFields();
|
|
124
|
+
},
|
|
125
|
+
relations,
|
|
126
|
+
create,
|
|
127
|
+
update,
|
|
128
|
+
Form,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Only the form's own fields go to the browser: a record handed over whole
|
|
133
|
+
* would carry every column it has - a password's hash, a token - into the page.
|
|
134
|
+
*/
|
|
135
|
+
export function valuesFor(fields, values) {
|
|
136
|
+
if (!values)
|
|
137
|
+
return {};
|
|
138
|
+
const record = values;
|
|
139
|
+
return Object.fromEntries(fields.flatMap((field) => (field.name in record ? [[field.name, record[field.name]]] : [])));
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=blueprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../../src/forms/blueprint.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react'\nimport { fieldsOf } from '../resource.js'\nimport type { Field, FieldOverride, FormSchema, ValuesOf } from '../resource.js'\nimport { submitForm } from './actions.js'\nimport { BLUEPRINT } from './brand.js'\nimport type { BlueprintParts } from './brand.js'\nimport { hostSchema, requireDrawable } from './host.js'\nimport type { HostAction, HostFields } from './host.js'\nimport { HONEYPOT } from './honeypot.js'\nimport { registerForm } from './registry.js'\nimport type { FormsTheme } from './theme.js'\n\ninterface BlueprintOptions {\n /** Unique in the app: the form is found by it when it is sent. Lowercase letters, digits and dashes. */\n name: string\n /** What draws `<blueprint.Form />`: import { shadcn } from '@rsc-kit/press/shadcn'. A blueprint only a panel draws needs none. */\n theme?: FormsTheme\n /** The submit button's words. Send to create and Save to update, unless said. */\n submit?: string\n /** What the visitor reads in place of the form once it is sent. */\n done?: string\n /** Where the visitor goes once it is sent, instead of `done`. */\n redirect?: string\n /**\n * A field people never see and bots fill in, for a form anyone may send.\n * On unless said; a form only signed-in people reach can turn it off.\n */\n honeypot?: boolean\n}\n\nexport interface SchemaBlueprintDefinition<Schema extends FormSchema> extends BlueprintOptions {\n /** Any Standard Schema of an object: z.object(), v.object(), type({...}). It checks every submission, on the server. */\n form: Schema\n /** Labels, descriptions and controls the schema cannot say - or every field, for a schema that does not describe itself as JSON Schema. */\n fields?: Partial<Record<keyof ValuesOf<Schema> & string, FieldOverride>>\n /**\n * What a new submission that passed the schema does: store it, send it,\n * call a backend. Throw an ActionValidationError - or let a backend's 422\n * through - and its messages land under the fields.\n */\n handle?: (values: ValuesOf<Schema>) => unknown\n /** What a submission of `<blueprint.Form id={...} />` does, with that key. */\n update?: (values: ValuesOf<Schema>, key: string) => unknown\n}\n\nexport interface HostBlueprintDefinition<Create extends HostAction, Update extends HostAction> extends BlueprintOptions {\n /**\n * The backend actions it saves with, as the build generated them:\n * `import { productsStore, productsUpdate } from '../server-actions.generated'`.\n * The create form is drawn from what `create` takes and the edit form from\n * what `update` takes, and they do all the checking: a Laravel FormRequest,\n * a Go struct. `update` is called with the key after the values.\n */\n actions: { create?: Create; update?: Update }\n /** Labels, descriptions and controls the backend's types cannot say. */\n fields?: Partial<Record<HostFields<Create> | HostFields<Update>, FieldOverride>>\n}\n\nexport interface BlueprintFormProps {\n /** The record's current values, for an edit. Only the blueprint's fields reach the browser. */\n values?: object\n /** The record's key: the form then updates it rather than creating one. */\n id?: string | number\n}\n\n/**\n * A form's fields and what saving it does, defined once and drawn anywhere:\n * as `<product.Form />` in a page, and as a panel's create and edit pages\n * with `defineResource({ form: product })`.\n */\nexport interface Blueprint extends BlueprintParts {\n /**\n * The form, in any page. Without `id` it creates; with it, it updates that\n * record, starting from `values`:\n *\n * <profile.Form id={user.id} values={user} />\n *\n * The key comes back from the browser like any field, so the update must\n * check it may touch that record: a FormRequest's authorize(), a Go\n * handler's own check.\n */\n Form: (props: BlueprintFormProps) => ReactNode\n}\n\n/**\n * A form defined once - its fields, its checks, what saving it does - and\n * drawn wherever it is needed: a page, a panel, or both. Either from a\n * Standard Schema with `handle` and `update`, or from the backend's own\n * actions, which draw the fields and do the checking:\n *\n * export const product = defineBlueprint({\n * name: 'product',\n * theme: shadcn,\n * actions: { create: productsStore, update: productsUpdate },\n * })\n *\n * <product.Form /> // creates\n * <product.Form id={p.id} values={p} /> // updates\n * defineResource<Product>()({ ..., form: product })\n */\nexport function defineBlueprint<Schema extends FormSchema>(definition: SchemaBlueprintDefinition<Schema>): Blueprint\nexport function defineBlueprint<Create extends HostAction, Update extends HostAction>(definition: HostBlueprintDefinition<Create, Update>): Blueprint\nexport function defineBlueprint(\n definition: (SchemaBlueprintDefinition<FormSchema> | HostBlueprintDefinition<HostAction, HostAction>) & {\n form?: FormSchema\n actions?: { create?: unknown; update?: unknown }\n },\n): Blueprint {\n const { name } = definition\n\n if (!/^[a-z0-9-]+$/.test(name)) {\n throw new Error(`[forms] a blueprint's name is lowercase letters, digits and dashes: ${JSON.stringify(name)}`)\n }\n\n if (definition.done !== undefined && definition.redirect !== undefined) {\n throw new Error(`[forms] the blueprint ${name} has both done and redirect; a sent form shows one or goes to the other`)\n }\n\n const { actions } = definition\n\n if ((actions === undefined) === (definition.form === undefined)) {\n throw new Error(`[forms] the blueprint ${name} needs a form schema, or the backend's actions, and not both`)\n }\n\n if (actions !== undefined) {\n const given = [actions.create, actions.update].filter((action) => action !== undefined)\n\n if (given.length === 0 || given.some((action) => typeof action !== 'function')) {\n throw new Error(\n `[forms] the blueprint ${name}'s actions are { create, update }, either or both, each the action the build generated: ` +\n \"import { productsStore, productsUpdate } from '../server-actions.generated'\",\n )\n }\n }\n\n const overrides = (definition.fields ?? {}) as Record<string, FieldOverride | undefined>\n const host = actions as { create?: HostAction; update?: HostAction } | undefined\n const schemaDefinition = definition as SchemaBlueprintDefinition<FormSchema>\n\n // A backend's create may be missing - a blueprint that only edits - so the\n // edit form's description stands in for the create's schema, never used.\n const schema = host ? hostSchema((host.create ?? host.update)!) : definition.form!\n const editSchema = host ? hostSchema((host.update ?? host.create)!) : schema\n\n const create: BlueprintParts['create'] = host\n ? host.create\n ? (values) => (host.create as (values: unknown) => Promise<unknown>)(values)\n : null\n : schemaDefinition.handle\n ? async (values) => schemaDefinition.handle!(values as never)\n : null\n const update: BlueprintParts['update'] = host\n ? host.update\n ? (values, key) => (host.update as (values: unknown, key: string) => Promise<unknown>)(values, key)\n : null\n : schemaDefinition.update\n ? async (values, key) => schemaDefinition.update!(values as never, key)\n : null\n\n const drawn: { create?: Field[]; edit?: Field[] } = {}\n\n const trapped = (fields: Field[]): Field[] => {\n if (definition.honeypot !== false && fields.some((field) => field.name === HONEYPOT)) {\n throw new Error(`[forms] the blueprint ${name} has a field named ${HONEYPOT}, which is the name its bot trap uses; rename it, or honeypot: false`)\n }\n\n return fields\n }\n\n function createFields(): Field[] {\n if (drawn.create) return drawn.create\n\n if (host) {\n if (!host.create) throw new Error(`[forms] the blueprint ${name} has no create action, so it cannot draw a form that creates; pass an id to edit`)\n\n requireDrawable({ name: `[forms] the blueprint ${name}'s create`, key: false }, host.create, overrides)\n }\n\n return (drawn.create = trapped(fieldsOf(schema, overrides)))\n }\n\n function editFields(): Field[] {\n if (drawn.edit) return drawn.edit\n if (!host) return (drawn.edit = createFields())\n\n if (!host.update) throw new Error(`[forms] the blueprint ${name} has no update action, so it cannot draw a form that edits`)\n\n requireDrawable({ name: `[forms] the blueprint ${name}'s update`, key: true }, host.update, overrides)\n\n return (drawn.edit = trapped(fieldsOf(editSchema, overrides)))\n }\n\n // A backend's types arrive with the server entry, which runs after this\n // module: those fields wait for the first render. A schema's fail here.\n if (!host) createFields()\n\n const relations = Object.values(overrides).flatMap((override) => (override?.relation ? [override.relation] : []))\n\n if (!host) for (const field of createFields()) if (field.relation && !relations.includes(field.relation)) relations.push(field.relation)\n\n registerForm({\n name,\n schema,\n editSchema,\n get fields() {\n return createFields()\n },\n get editFields() {\n return editFields()\n },\n create,\n update,\n redirect: definition.redirect ?? null,\n honeypot: definition.honeypot !== false,\n })\n\n const { theme } = definition\n\n function Form({ values, id }: BlueprintFormProps) {\n if (!theme) {\n throw new Error(`[forms] the blueprint ${name} has no theme to draw <${name}.Form /> with: theme: shadcn, from '@rsc-kit/press/shadcn'`)\n }\n\n const editing = id !== undefined && id !== null\n\n if (editing && !update) throw new Error(`[forms] <${name}.Form id={...} /> edits, and the blueprint ${name} has no update to save it with`)\n if (!editing && !create) throw new Error(`[forms] <${name}.Form /> creates, and the blueprint ${name} has no create to save it with; pass the record's id to edit`)\n\n const fields = editing ? editFields() : createFields()\n const Draw = theme.BlueprintForm\n const done = definition.redirect === undefined ? (definition.done ?? (editing ? 'Saved.' : 'Thank you. It was sent.')) : undefined\n\n return (\n <Draw\n action={submitForm.bind(null, name, editing ? String(id) : null)}\n fields={fields}\n values={valuesFor(fields, values)}\n submit={definition.submit ?? (editing ? 'Save' : 'Send')}\n {...(done === undefined ? {} : { done })}\n {...(definition.honeypot === false ? {} : { honeypot: HONEYPOT })}\n />\n )\n }\n\n return {\n [BLUEPRINT]: true,\n name,\n schema,\n editSchema,\n get fields() {\n return createFields()\n },\n get editFields() {\n return editFields()\n },\n relations,\n create,\n update,\n Form,\n }\n}\n\n/**\n * Only the form's own fields go to the browser: a record handed over whole\n * would carry every column it has - a password's hash, a token - into the page.\n */\nexport function valuesFor(fields: Field[], values: object | undefined): Record<string, unknown> {\n if (!values) return {}\n\n const record = values as Record<string, unknown>\n\n return Object.fromEntries(fields.flatMap((field) => (field.name in record ? [[field.name, record[field.name]]] : [])))\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Field, FormSchema } from '../resource.js';
|
|
2
|
+
/**
|
|
3
|
+
* What marks a blueprint, so a resource handed one in `form` can tell it from
|
|
4
|
+
* a Standard Schema without importing the module that draws it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const BLUEPRINT: unique symbol;
|
|
7
|
+
/** What a resource reads from a blueprint: the fields and the saves, nothing it draws. */
|
|
8
|
+
export interface BlueprintParts {
|
|
9
|
+
readonly [BLUEPRINT]: true;
|
|
10
|
+
name: string;
|
|
11
|
+
/** What a create is checked against. */
|
|
12
|
+
schema: FormSchema;
|
|
13
|
+
/** What an update is checked against: a backend's update may take other fields than its create. */
|
|
14
|
+
editSchema: FormSchema;
|
|
15
|
+
/** The create form's fields. Throws for a blueprint that cannot create. */
|
|
16
|
+
readonly fields: Field[];
|
|
17
|
+
/** The edit form's fields. Throws for a blueprint that cannot update. */
|
|
18
|
+
readonly editFields: Field[];
|
|
19
|
+
/** The resources its relation fields name. */
|
|
20
|
+
relations: string[];
|
|
21
|
+
/** Saves a new record, answering what was saved. Null when the blueprint only updates, or leaves saving to a resource's source. */
|
|
22
|
+
create: ((values: Record<string, unknown>) => Promise<unknown>) | null;
|
|
23
|
+
/** Saves the record with this key. Null when the blueprint only creates, or leaves saving to a resource's source. */
|
|
24
|
+
update: ((values: Record<string, unknown>, key: string) => Promise<unknown>) | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function isBlueprint(value: unknown): value is BlueprintParts;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What marks a blueprint, so a resource handed one in `form` can tell it from
|
|
3
|
+
* a Standard Schema without importing the module that draws it.
|
|
4
|
+
*/
|
|
5
|
+
export const BLUEPRINT = Symbol.for('@rsc-kit/press.blueprint');
|
|
6
|
+
export function isBlueprint(value) {
|
|
7
|
+
return typeof value === 'object' && value !== null && value[BLUEPRINT] === true;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=brand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.js","sourceRoot":"","sources":["../../src/forms/brand.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;AAsB/D,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAAiC,CAAC,SAAS,CAAC,KAAK,IAAI,CAAA;AAC9G,CAAC","sourcesContent":["import type { Field, FormSchema } from '../resource.js'\n\n/**\n * What marks a blueprint, so a resource handed one in `form` can tell it from\n * a Standard Schema without importing the module that draws it.\n */\nexport const BLUEPRINT = Symbol.for('@rsc-kit/press.blueprint')\n\n/** What a resource reads from a blueprint: the fields and the saves, nothing it draws. */\nexport interface BlueprintParts {\n readonly [BLUEPRINT]: true\n name: string\n /** What a create is checked against. */\n schema: FormSchema\n /** What an update is checked against: a backend's update may take other fields than its create. */\n editSchema: FormSchema\n /** The create form's fields. Throws for a blueprint that cannot create. */\n readonly fields: Field[]\n /** The edit form's fields. Throws for a blueprint that cannot update. */\n readonly editFields: Field[]\n /** The resources its relation fields name. */\n relations: string[]\n /** Saves a new record, answering what was saved. Null when the blueprint only updates, or leaves saving to a resource's source. */\n create: ((values: Record<string, unknown>) => Promise<unknown>) | null\n /** Saves the record with this key. Null when the blueprint only creates, or leaves saving to a resource's source. */\n update: ((values: Record<string, unknown>, key: string) => Promise<unknown>) | null\n}\n\nexport function isBlueprint(value: unknown): value is BlueprintParts {\n return typeof value === 'object' && value !== null && (value as Record<symbol, unknown>)[BLUEPRINT] === true\n}\n"]}
|
package/dist/forms/host.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { FormFields } from '@rsc-kit/core/form';
|
|
2
2
|
import type { FieldOverride, FormSchema } from '../resource.js';
|
|
3
3
|
/**
|
|
4
|
-
* A form with no schema of its own:
|
|
4
|
+
* A form with no schema of its own: defineBlueprint({ actions: { create: contactSend } }), with
|
|
5
5
|
* the action the build generated for the backend's Contact.send. The backend
|
|
6
6
|
* said what the function takes when it wrote rsc-host.json, and the build put
|
|
7
7
|
* that in the server entry, so the fields come from there and every check is
|
package/dist/forms/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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:
|
|
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: defineBlueprint({ actions: { create: 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/forms/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export type {
|
|
1
|
+
export { defineBlueprint } from './blueprint.js';
|
|
2
|
+
export type { Blueprint, BlueprintFormProps, HostBlueprintDefinition, SchemaBlueprintDefinition } from './blueprint.js';
|
|
3
|
+
export type { FormProps, FormsTheme } from './theme.js';
|
|
4
4
|
export type { HostAction, HostFields } from './host.js';
|
package/dist/forms/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { defineBlueprint } from './blueprint.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/forms/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA","sourcesContent":["export { defineBlueprint } from './blueprint.js'\nexport type { Blueprint, BlueprintFormProps, HostBlueprintDefinition, SchemaBlueprintDefinition } from './blueprint.js'\nexport type { FormProps, FormsTheme } from './theme.js'\nexport type { HostAction, HostFields } from './host.js'\n"]}
|
package/dist/forms/registry.d.ts
CHANGED
|
@@ -2,9 +2,14 @@ import type { Field, FormSchema } from '../resource.js';
|
|
|
2
2
|
export interface FormHandle {
|
|
3
3
|
name: string;
|
|
4
4
|
schema: FormSchema;
|
|
5
|
+
editSchema: FormSchema;
|
|
5
6
|
fields: Field[];
|
|
6
|
-
|
|
7
|
+
editFields: Field[];
|
|
8
|
+
create: ((values: Record<string, unknown>) => Promise<unknown>) | null;
|
|
9
|
+
update: ((values: Record<string, unknown>, key: string) => Promise<unknown>) | null;
|
|
7
10
|
redirect: string | null;
|
|
11
|
+
/** Whether a filled bot trap drops the submission. */
|
|
12
|
+
honeypot: boolean;
|
|
8
13
|
}
|
|
9
14
|
export declare function registerForm(form: FormHandle): void;
|
|
10
15
|
export declare function formNamed(name: string): FormHandle;
|
package/dist/forms/registry.js
CHANGED
|
@@ -11,7 +11,7 @@ export function registerForm(form) {
|
|
|
11
11
|
export function formNamed(name) {
|
|
12
12
|
const form = forms.get(name);
|
|
13
13
|
if (!form)
|
|
14
|
-
throw new Error(`[forms] no form named ${name};
|
|
14
|
+
throw new Error(`[forms] no form named ${name}; defineBlueprint({ name: ${JSON.stringify(name)} }) has not run`);
|
|
15
15
|
return form;
|
|
16
16
|
}
|
|
17
17
|
//# sourceMappingURL=registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/forms/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/forms/registry.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;AAC9C,MAAM,KAAK,GAA4B,CAAE,UAAsC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,EAAE,CAA4B,CAAA;AAE9H,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAE5B,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,6BAA6B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;IAE3H,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import type { Field, FormSchema } from '../resource.js'\n\nexport interface FormHandle {\n name: string\n schema: FormSchema\n editSchema: FormSchema\n fields: Field[]\n editFields: Field[]\n create: ((values: Record<string, unknown>) => Promise<unknown>) | null\n update: ((values: Record<string, unknown>, key: string) => Promise<unknown>) | null\n redirect: string | null\n /** Whether a filled bot trap drops the submission. */\n honeypot: boolean\n}\n\n/**\n * Forms by name. An action receives a name, not a form - only what can be\n * serialised crosses to the browser and back - so the form is looked up here.\n * On globalThis because a dev server can evaluate this module more than once.\n */\nconst KEY = Symbol.for('@rsc-kit/press.forms')\nconst forms: Map<string, FormHandle> = ((globalThis as Record<symbol, unknown>)[KEY] ??= new Map()) as Map<string, FormHandle>\n\nexport function registerForm(form: FormHandle): void {\n forms.set(form.name, form)\n}\n\nexport function formNamed(name: string): FormHandle {\n const form = forms.get(name)\n\n if (!form) throw new Error(`[forms] no form named ${name}; defineBlueprint({ name: ${JSON.stringify(name)} }) has not run`)\n\n return form\n}\n"]}
|
package/dist/forms/theme.d.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
2
|
import type { Field } from '../resource.js';
|
|
3
3
|
/**
|
|
4
|
-
* What a theme draws for a
|
|
5
|
-
* a bound server action or a string - never a component - so the form
|
|
6
|
-
* a client component, and it is: it shows the browser's checks and the
|
|
4
|
+
* What a theme draws for a blueprint's form. As with a panel, every prop is
|
|
5
|
+
* data, a bound server action or a string - never a component - so the form
|
|
6
|
+
* may be a client component, and it is: it shows the browser's checks and the
|
|
7
7
|
* server's refusals under their fields.
|
|
8
8
|
*/
|
|
9
9
|
export interface FormsTheme {
|
|
10
|
-
|
|
10
|
+
BlueprintForm: ComponentType<FormProps>;
|
|
11
11
|
}
|
|
12
|
-
export interface
|
|
12
|
+
export interface FormProps {
|
|
13
13
|
/** Checks and sends the form. A refusal is a 422 whose messages belong under their fields. */
|
|
14
14
|
action: (form: FormData) => Promise<unknown>;
|
|
15
15
|
fields: Field[];
|
|
16
|
-
/**
|
|
16
|
+
/** What each field starts as: the record being edited, or nothing. */
|
|
17
|
+
values: Record<string, unknown>;
|
|
18
|
+
/** The submit button's words: Send message, Save. */
|
|
17
19
|
submit: string;
|
|
18
20
|
/**
|
|
19
21
|
* What the visitor reads once it is sent, in place of the form. Absent when
|
|
@@ -24,6 +26,7 @@ export interface PublicFormProps {
|
|
|
24
26
|
* The name of a field people never see and bots fill in. The theme renders
|
|
25
27
|
* it hidden from sight, from screen readers and from the tab order, with
|
|
26
28
|
* autocomplete off; a submission with it filled is thanked and dropped.
|
|
29
|
+
* Absent for a blueprint that turned it off.
|
|
27
30
|
*/
|
|
28
|
-
honeypot
|
|
31
|
+
honeypot?: string;
|
|
29
32
|
}
|
package/dist/forms/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/forms/theme.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentType } from 'react'\nimport type { Field } from '../resource.js'\n\n/**\n * What a theme draws for a
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/forms/theme.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentType } from 'react'\nimport type { Field } from '../resource.js'\n\n/**\n * What a theme draws for a blueprint's form. As with a panel, every prop is\n * data, a bound server action or a string - never a component - so the form\n * may be a client component, and it is: it shows the browser's checks and the\n * server's refusals under their fields.\n */\nexport interface FormsTheme {\n BlueprintForm: ComponentType<FormProps>\n}\n\nexport interface FormProps {\n /** Checks and sends the form. A refusal is a 422 whose messages belong under their fields. */\n action: (form: FormData) => Promise<unknown>\n fields: Field[]\n /** What each field starts as: the record being edited, or nothing. */\n values: Record<string, unknown>\n /** The submit button's words: Send message, Save. */\n submit: string\n /**\n * What the visitor reads once it is sent, in place of the form. Absent when\n * the form redirects instead.\n */\n done?: string\n /**\n * The name of a field people never see and bots fill in. The theme renders\n * it hidden from sight, from screen readers and from the tab order, with\n * autocomplete off; a submission with it filled is thanked and dropped.\n * Absent for a blueprint that turned it off.\n */\n honeypot?: string\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { defineResource, fieldsOf, humanize, parse, valuesFrom } from './resource.js';
|
|
2
|
+
export { shown, slugify } from './conditions.js';
|
|
3
|
+
export type { Derivation, Visibility } from './conditions.js';
|
|
2
4
|
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
5
|
export { IMAGE_TYPES } from './uploads.js';
|
|
4
6
|
export type { Storage, StorageContext } from './uploads.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { defineResource, fieldsOf, humanize, parse, valuesFrom } from './resource.js';
|
|
2
|
+
export { shown, slugify } from './conditions.js';
|
|
2
3
|
export { IMAGE_TYPES } from './uploads.js';
|
|
3
4
|
export { Blocks, defineBlock } from './blocks.js';
|
|
4
5
|
export { memorySource } from './memory.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;
|
|
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;AACrF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AA2BhD,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 { shown, slugify } from './conditions.js'\nexport type { Derivation, Visibility } from './conditions.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"]}
|
package/dist/resource.d.ts
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
import type { ComponentType, ReactNode } from 'react';
|
|
14
14
|
import type { StandardSchemaV1 } from '@rsc-kit/core/standardSchema';
|
|
15
15
|
import type { HostAction, HostFields } from './forms/host.js';
|
|
16
|
+
import type { Derivation, Visibility } from './conditions.js';
|
|
17
|
+
import type { BlueprintParts } from './forms/brand.js';
|
|
16
18
|
import type { Storage } from './uploads.js';
|
|
17
19
|
/** What the form validates with: any Standard Schema - zod, valibot, arktype, effect. */
|
|
18
20
|
export type FormSchema = StandardSchemaV1<any, Record<string, unknown>>;
|
|
@@ -114,6 +116,14 @@ export interface Field {
|
|
|
114
116
|
maxSize?: number;
|
|
115
117
|
/** The schema allows null: a file field that is emptied saves null, so the source clears it. */
|
|
116
118
|
nullable?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Shown only while another field says so: { when: 'type', is: 'business' }.
|
|
121
|
+
* Hidden, it is not sent, not checked by the browser, and dropped by the
|
|
122
|
+
* server if a post sends it anyway. Required means required when shown.
|
|
123
|
+
*/
|
|
124
|
+
visible?: Visibility;
|
|
125
|
+
/** Filled from another field while that is typed, until edited by hand: { field: 'title' } for a slug. */
|
|
126
|
+
from?: Derivation;
|
|
117
127
|
}
|
|
118
128
|
export interface Filter {
|
|
119
129
|
key: string;
|
|
@@ -295,6 +305,17 @@ export interface HostResourceDefinition<Row, Create extends HostAction, Update e
|
|
|
295
305
|
/** Labels, controls and relations the backend's types cannot say. */
|
|
296
306
|
fields?: Partial<Record<HostFields<Create> | HostFields<Update>, FieldOverride>>;
|
|
297
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* A resource whose form is a blueprint: the same fields, checks and saves a
|
|
310
|
+
* page draws with `<product.Form />`. A blueprint that saves - a backend's
|
|
311
|
+
* actions, or a schema's handle and update - needs only a source that lists,
|
|
312
|
+
* finds and deletes; one that leaves saving to the resource needs a whole one.
|
|
313
|
+
*/
|
|
314
|
+
export interface BlueprintResourceDefinition<Row> extends Omit<ResourceDefinition<Row, FormSchema>, 'source' | 'form' | 'fields'> {
|
|
315
|
+
source: ReadSource<Row> | DataSource<Row, Record<string, unknown>>;
|
|
316
|
+
/** import { product } from './blueprints/product', defined with defineBlueprint. */
|
|
317
|
+
form: BlueprintParts;
|
|
318
|
+
}
|
|
298
319
|
/**
|
|
299
320
|
* Two calls so the row is named and the schema is inferred:
|
|
300
321
|
* defineResource<Product>()({ ... }).
|
|
@@ -302,9 +323,10 @@ export interface HostResourceDefinition<Row, Create extends HostAction, Update e
|
|
|
302
323
|
export declare function defineResource<Row>(): {
|
|
303
324
|
<Schema extends FormSchema>(definition: ResourceDefinition<Row, Schema>): Resource<Row, Schema>;
|
|
304
325
|
<Create extends HostAction, Update extends HostAction>(definition: HostResourceDefinition<Row, Create, Update>): Resource<Row>;
|
|
326
|
+
(definition: BlueprintResourceDefinition<Row>): Resource<Row>;
|
|
305
327
|
};
|
|
306
328
|
/** A field as the app says it, over what the schema said. */
|
|
307
|
-
export type FieldOverride = Partial<Pick<Field, 'type' | 'label' | 'description' | 'required' | 'relation'>> & {
|
|
329
|
+
export type FieldOverride = Partial<Pick<Field, 'type' | 'label' | 'description' | 'required' | 'relation' | 'visible' | 'from'>> & {
|
|
308
330
|
/** A bare value is labelled by humanizing it: 'in_stock' reads "In stock". */
|
|
309
331
|
options?: (string | Option)[];
|
|
310
332
|
/** The kinds a blocks field offers: { type: 'blocks', blocks: [hero, features] }. */
|