@loadbare/app 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/assemble.d.ts +1 -2
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +11 -53
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/cli.d.ts +4 -10
- package/dist/build/cli.d.ts.map +1 -1
- package/dist/build/cli.js +11 -33
- package/dist/build/cli.js.map +1 -1
- package/dist/build/declared.js +1 -1
- package/dist/build/declared.js.map +1 -1
- package/dist/core/lb-constants.d.ts +8 -18
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +32 -72
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +12 -11
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +7 -0
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +10 -37
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +139 -522
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +115 -126
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/hub/lb-lifecycle.d.ts +2 -2
- package/dist/hub/lb-lifecycle.d.ts.map +1 -1
- package/dist/hub/lb-lifecycle.js +9 -17
- package/dist/hub/lb-lifecycle.js.map +1 -1
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +3 -3
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +35 -19
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +141 -88
- package/dist/server/lb-server.js.map +1 -1
- package/docs/binding-cases.md +150 -0
- package/docs/comparison.md +18 -29
- package/docs/miswarts.md +2 -2
- package/docs/reference/css.md +2 -15
- package/docs/reference/custom-elements.md +6 -8
- package/docs/reference/data-binding.md +37 -180
- package/docs/reference/names.md +10 -26
- package/docs/reference/page-files.md +1 -12
- package/docs/reference/server.md +0 -1
- package/docs/testing.md +2 -5
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +52 -54
- package/skills/loadbare-app/references/css.md +2 -15
- package/skills/loadbare-app/references/custom-elements.md +6 -8
- package/skills/loadbare-app/references/data-binding.md +37 -180
- package/skills/loadbare-app/references/miswarts.md +2 -2
- package/skills/loadbare-app/references/names.md +10 -26
- package/skills/loadbare-app/references/page-files.md +1 -12
- package/skills/loadbare-app/references/server.md +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-server.js","sourceRoot":"","sources":["../../server/lb-server.ts"],"names":[],"mappings":"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","sourcesContent":["// The server-side contract and the engine that runs it.\n//\n// This is the counterpart to core/lb-constants.ts: that file is the\n// vocabulary the browser writes, this one is the vocabulary the application\n// writes. The engine below is the whole of Loadbare on the server. It ships\n// no HTTP server, no router, and no data layer.\n\nimport type {\n ColumnTypes,\n HubData,\n HubRequest,\n HubResponse,\n HubResult,\n Kind,\n GatheredRow,\n HubRefusal,\n Patch,\n RequestFields,\n ResponseItem,\n Row,\n} from \"../core/lb-types.js\";\nimport { isRowRequest, nextLocation, parseOrder } from \"../core/lb-types.js\";\nimport {\n CHOICES_QUERY,\n COLUMN_TYPES,\n KIND_ROW,\n KIND_ROWS,\n LB_RESERVED_PREFIX,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n URL_COLUMN_PATH,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\n\n/**\n * Whatever the application hands the engine for the duration of one request.\n *\n * Loadbare declares it empty and never reads it. An application fills it in\n * by declaration merging, once, anywhere in its own source:\n *\n * declare module \"@loadbare/app/server\" {\n * interface HubContext {\n * db: Db;\n * }\n * }\n *\n * That is why no type on this page takes a type parameter. The context is a\n * request-scoped handle — an authenticated database connection is the\n * expected case — so it is passed per call rather than held by the engine.\n * The page's query parms are the other expected member: a query that reads\n * one off the context takes no argument, and re-runs under `refresh` like\n * any other.\n */\nexport interface HubContext {}\n\n/**\n * A declared query: its kind, its key, and the run that answers it.\n *\n * Kind and key are properties of the name rather than of any one answer, so\n * they are declared here and never inferred from what comes back. The engine\n * sends both with every answer, so the markup repeats neither. Every query\n * has a key; an aggregate row answers with a constant one.\n *\n * Write one with `row()` or `rows()` below; nothing else builds one.\n */\nexport interface Query {\n readonly kind: Kind;\n readonly key: string;\n readonly run: (ctx: HubContext) => HubResult | Promise<HubResult>;\n /** A `rows` query's order, as `rows()` declared it. */\n readonly order?: string;\n /** A `rows` query's column types, as `rows()` declared them. */\n readonly types?: ColumnTypes;\n}\n\n/**\n * What a `rows` query declares about its order.\n *\n * `order` is the order the hub places its rows by: columns separated by\n * commas, each descending when it begins with `-`, as in\n * `category_order,-opened`. An order the user chose, held in `lb-choices`,\n * replaces it. With neither, rows show in the order the query answered them\n * in.\n *\n * `types` names a type for any of its columns, and the hub sorts, filters\n * and takes `lb-min` and `lb-max` of that column read as its type rather\n * than by the JSON type its values arrive as: `{ amount: \"number\" }` for a\n * Postgres `numeric` that arrives as a string. A column it does not name\n * compares by its JSON type. The order and the types go to the browser\n * once, with the answer that loads the page.\n */\nexport interface RowsOptions {\n order?: string;\n types?: ColumnTypes;\n}\n\n/**\n * A query that answers with one row, identified by its `key` column, or with\n * `null` when there is none.\n */\nexport function row(\n key: string,\n run: (ctx: HubContext) => Row | null | Promise<Row | null>,\n): Query {\n return { kind: KIND_ROW, key, run };\n}\n\n/**\n * A query that answers with all its rows. Rows land by their `key` column: a\n * row whose key is not in the answer is gone. The hub places them by the\n * order in `options`, or in the order they arrived when there is none.\n */\nexport function rows(\n key: string,\n run: (ctx: HubContext) => Row[] | Promise<Row[]>,\n options: RowsOptions = {},\n): Query {\n return {\n kind: KIND_ROWS,\n key,\n run,\n ...(options.order !== undefined && { order: options.order }),\n ...(options.types !== undefined && { types: options.types }),\n };\n}\n\n/**\n * Only what changed, returned from a handler for a `rows` query. Rows named\n * here are added or updated, keys in `drop` are removed, and everything\n * unnamed is left alone — its contents, and its place in whatever order the\n * page is keeping.\n *\n * `Array.isArray` is what tells a patch from all rows, so a patch needs no\n * marker of its own and no column name is reserved to carry one.\n */\nexport function patch(change: { rows?: Row[]; drop?: unknown[] }): Patch {\n const made = { ...change };\n patches.add(made);\n return made;\n}\n\n/**\n * Every object `patch()` made. A patch and a row are both plain objects, so\n * this is how the engine refuses a patch answering a `row` query.\n */\nconst patches = new WeakSet<object>();\n\n/**\n * A new row for `lb-url`, returned from a handler when only the handler can\n * know where the page belongs: the key of a row it inserted, or the absence\n * of one it deleted.\n *\n * A column other than `lb-path` is a query parm: set, or taken out when its\n * value is empty. An array of strings repeats the parm once per value. With `lb-path` naming another page, the page is entered\n * with only the parms named here; otherwise every other parm is kept.\n *\n * The page then loads at the new URL in the same round trip, as a cold load\n * of it would, so the refresh set is not run and whatever else the handler\n * returned is dropped: both were answers for the URL the page is leaving.\n * This is Post/Redirect/Get without the redirect.\n */\nexport function url(columns: Record<string, string | string[]>): HubData {\n return { [URL_QUERY]: { ...columns } };\n}\n\n/**\n * The user's choices for the page, returned from `onPageEnter` to restore\n * what the user last chose: an order, a filter. Each column is a column of\n * `lb-choices`, and the hub places the page's rows by them as they land.\n * Return it beside `url()` with the spread both are written for:\n *\n * return { ...url({ acct: \"23\" }), ...choices({ sort: \"-amount\" }) };\n */\nexport function choices(columns: Record<string, string>): HubData {\n return { [CHOICES_QUERY]: { ...columns } };\n}\n\n/**\n * A handler's refusal: thrown, and answered to the browser with the words\n * it carries. Every other error a handler throws reaches the browser as a\n * failure with nothing in it.\n */\nexport class Refusal extends Error {\n readonly refusal: HubRefusal;\n\n constructor(refusal: HubRefusal) {\n super(refusal.message ?? \"refused\");\n this.name = \"Refusal\";\n this.refusal = refusal;\n }\n}\n\n/**\n * Refuse a request, in words written for the user. Throw what it returns:\n *\n * throw refusal(\"That series already has paychecks entered.\");\n * throw refusal({ amount: \"Must be more than zero\" });\n * throw refusal(\"Two fields need attention.\", { amount: \"…\" });\n *\n * A message alone is for the request as a whole; columns name the controls\n * the request gathered, and each message is shown on its control. Thrown\n * rather than returned, so a refusal can come from anywhere the handler\n * calls, and whatever the application has open rolls back as for any\n * throw.\n */\nexport function refusal(\n message: string | Record<string, string>,\n columns?: Record<string, string>,\n): Refusal {\n return typeof message === \"string\"\n ? new Refusal({ message, ...(columns && { columns }) })\n : new Refusal({ columns: message });\n}\n\n/** The shape of a `<stub>.queries.ts` module. */\nexport type Queries = Record<string, Query>;\n\n/**\n * What a handler does, and which queries re-run once it has.\n *\n * `run` may also return answers of its own, which are laid over the\n * refreshed ones. That is how a patch reaches the browser: a query answers\n * for all its rows and cannot know why it re-ran, but the handler knows\n * exactly what it changed and can say only that.\n */\nexport interface Handler<R = RequestFields> {\n run: (\n ctx: HubContext,\n request: R,\n ) => void | HubData | Promise<void | HubData>;\n refresh: string[];\n}\n\n/**\n * `rows` holds the rows of the queries nested in the form the request came\n * from, by query name, each with its own values: a master's details,\n * written with it.\n */\nexport type RowInsertHandler = Handler<{\n key?: string;\n values: Record<string, string>;\n rows?: Record<string, GatheredRow[]>;\n}>;\n/** `values` holds only the columns being set, as an SQL UPDATE sets them. */\nexport type RowUpdateHandler = Handler<{\n key: string;\n values: Record<string, string>;\n rows?: Record<string, GatheredRow[]>;\n}>;\nexport type RowDeleteHandler = Handler<{ key: string }>;\n\n/**\n * What the requests Loadbare provides do to one query, keyed by its name in\n * `Requests.crud`. A query with no entry here permits none of them: the wire\n * cannot reach anything the page has not published.\n *\n * Every key is the request name with the prefix stripped and the rest\n * camel-cased: `lb-row-insert` runs `rowInsert`.\n */\nexport interface Crud {\n rowInsert?: RowInsertHandler;\n rowUpdate?: RowUpdateHandler;\n rowDelete?: RowDeleteHandler;\n}\n\n/**\n * The shape of a `<stub>.requests.ts` module.\n *\n * `onPageEnter` runs when the page loads, before its queries. It declares no\n * refresh set: the page's queries all run afterward, so whatever it changed\n * is already in the response. It may return `url()` instead, to move the\n * page to other query parms or another page before anything shows, as an\n * application restoring what the user last had on screen does. The page\n * then loads there in the same round trip, and `onPageEnter` running for\n * that load is not followed again. It may return `choices()`, with `url()`\n * or alone, to restore the user's choices for the page.\n *\n * `onChoices` runs each time the user makes a choice, with every choice\n * made on the page, so an application can keep them for `onPageEnter` to\n * restore. It answers with nothing, by convention.\n *\n * `handlers` holds the page's declared requests, by name. A name the page\n * did not declare is refused, so the wire cannot reach anything the page has\n * not published.\n *\n * `crud` holds what the requests Loadbare provides run, by query name.\n */\nexport interface Requests {\n onPageEnter?: (ctx: HubContext) => void | HubData | Promise<void | HubData>;\n onChoices?: (\n ctx: HubContext,\n choices: Record<string, string>,\n ) => void | HubData | Promise<void | HubData>;\n handlers?: Record<string, Handler>;\n crud?: Record<string, Crud>;\n}\n\n/** A page is three files sharing a stub; two of them are these. */\nexport interface Page {\n queries: Queries;\n requests: Requests;\n}\n\n/**\n * The page registry. The builder writes it from every `.requests.ts` and\n * `.queries.ts` it discovers — see docs/reference/builder.md.\n */\nexport type Pages = Record<string, Page>;\n\n/**\n * How a page load treats a `url()` its `onPageEnter` returned.\n *\n * With `follow` false it is ignored and the queries run, as they must for a\n * load that is itself at a URL something else moved to. With `parms`, the\n * query parms the page is being loaded at, one that leads to the page and\n * parms already in hand is ignored the same way, so the page loads once.\n */\nexport interface PageLoadOptions {\n follow?: boolean;\n parms?: URLSearchParams;\n}\n\n/**\n * The engine. Both calls answer with response items. A request whose\n * handler returned `url()`, and a page load whose `onPageEnter` did, answer\n * with the `lb-url` item alone: the caller builds a context at the new URL,\n * calls `dataForPage` with it and `follow` false, and sends the load beside the\n * item. `hubRoutes` does exactly that.\n */\nexport interface Hub {\n /** A page load: its `onPageEnter`, then all of its queries. */\n dataForPage(\n page: string,\n ctx: HubContext,\n options?: PageLoadOptions,\n ): Promise<HubResponse>;\n\n /** A request: run the handler its name picks, then the refresh set. */\n runRequest(\n page: string,\n request: HubRequest,\n ctx: HubContext,\n ): Promise<HubResponse>;\n}\n\n/** The camel-cased `Crud` key of a request Loadbare provides. */\nconst CRUD_KEYS: Record<string, keyof Crud> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * Build the engine over a set of pages. The pages are fixed at startup; the\n * context is not, and arrives with each call.\n */\nexport function createHub(pages: Pages): Hub {\n // Names beginning with the reserved prefix are Loadbare's — its own query,\n // the requests it provides — so an application cannot declare one.\n // Refused at startup, because a name is a fact about the page and not\n // about any request.\n for (const [page, entry] of Object.entries(pages)) {\n const declared = [\n ...Object.keys(entry.queries),\n ...Object.keys(entry.requests.handlers ?? {}),\n ...Object.keys(entry.requests.crud ?? {}),\n ];\n for (const name of declared) {\n if (name.startsWith(LB_RESERVED_PREFIX)) {\n throw new Error(\n `loadbare: page '${page}' declares '${name}', but names ` +\n `beginning with '${LB_RESERVED_PREFIX}' are reserved`,\n );\n }\n }\n\n // A crud handler always knows which rows it changed: an update or a\n // delete names its row by key, and an insert has the new row's key once\n // it has written it. So each can answer with a patch, and the hub places\n // what the patch names by the query's order. Refreshing the whole set\n // instead sends every row to say one thing. There is no case where that\n // is the answer, so it is refused rather than warned about.\n // An order is a fact about the query, so a malformed one is refused at\n // startup, as a reserved name is.\n // So is a column type the hub does not know.\n for (const [name, query] of Object.entries(entry.queries)) {\n for (const [column, type] of Object.entries(query.types ?? {})) {\n if (!(COLUMN_TYPES as readonly string[]).includes(type)) {\n throw new Error(\n `loadbare: page '${page}' query '${name}' declares column ` +\n `'${column}' as '${type}'; a column type is one of ` +\n `${COLUMN_TYPES.join(\", \")}`,\n );\n }\n }\n if (query.order === undefined) continue;\n try {\n parseOrder(query.order);\n } catch (err) {\n throw new Error(\n `loadbare: page '${page}' query '${name}': ${(err as Error).message}`,\n );\n }\n }\n\n for (const [name, crud] of Object.entries(entry.requests.crud ?? {})) {\n if (entry.queries[name]?.kind !== KIND_ROWS) continue;\n for (const request of [\"rowInsert\", \"rowUpdate\", \"rowDelete\"] as const) {\n if (crud[request]?.refresh.includes(name)) {\n throw new Error(\n `loadbare: page '${page}' refreshes '${name}' after its own ` +\n `${request}; return the changed rows in a patch instead`,\n );\n }\n }\n }\n }\n\n /**\n * Answers into response items, adding the kind and key each query\n * declared. An answer that disagrees with its declaration is a mistake in\n * the application rather than a case to handle, and is left out: a page is\n * better off missing one query than showing the wrong shape for it.\n *\n * A page load also adds each `rows` query's order and column types, which\n * the hub keeps for the answers after it.\n */\n function items(page: string, data: HubData, load = false): HubResponse {\n const out: ResponseItem[] = [];\n for (const [name, result] of Object.entries(data)) {\n const query = pages[page]?.queries[name];\n if (!query) {\n console.warn(`loadbare: page '${page}' declares no query '${name}'`);\n continue;\n }\n const item: ResponseItem = {\n query: name,\n kind: query.kind,\n key: query.key,\n };\n if (query.kind === KIND_ROW) {\n if (Array.isArray(result) || patches.has(result as object)) {\n console.warn(\n `loadbare: query '${name}' is declared row but answered with ` +\n `${Array.isArray(result) ? \"rows\" : \"a patch\"}`,\n );\n continue;\n }\n // No row reaches the browser as null, which JSON keeps and\n // undefined it would drop.\n item.row = (result ?? null) as Row | null;\n } else {\n if (result === null || result === undefined) {\n console.warn(\n `loadbare: query '${name}' is declared rows but answered with nothing`,\n );\n continue;\n }\n if (Array.isArray(result)) item.rows = result;\n else item.patch = result as Patch;\n if (load && query.order !== undefined) item.order = query.order;\n if (load && query.types !== undefined) item.types = query.types;\n }\n out.push(item);\n }\n return out;\n }\n\n /** Run queries by name, each answering with all its rows or its row. */\n async function run(\n page: string,\n names: string[],\n ctx: HubContext,\n ): Promise<HubData> {\n const data: HubData = {};\n for (const name of names) {\n const query = pages[page]?.queries[name];\n if (!query) {\n console.warn(`loadbare: page '${page}' declares no query '${name}'`);\n continue;\n }\n const result = await query.run(ctx);\n if (Array.isArray(result) !== (query.kind === KIND_ROWS)) {\n console.warn(\n `loadbare: query '${name}' is declared ${query.kind} but answered ` +\n `with ${Array.isArray(result) ? \"rows\" : result == null ? \"nothing\" : \"one row\"}`,\n );\n continue;\n }\n data[name] = result;\n }\n return data;\n }\n\n /**\n * Run a handler, then its refresh set against the same context, laying\n * what the handler itself returned over the refreshed queries — the\n * narrower answer wins because it knows what actually changed.\n *\n * A handler that returned `url()` answers with the `lb-url` item alone.\n * Loading the page there needs a context built at the new URL, and\n * building one is the caller's.\n */\n async function settle(\n page: string,\n handler: Handler<never> | undefined,\n request: RequestFields,\n ctx: HubContext,\n notFound: string,\n ): Promise<HubResponse> {\n if (!handler) {\n console.warn(notFound);\n return [];\n }\n const { [URL_QUERY]: moved, ...stated } =\n (await handler.run(ctx, request as never)) ?? {};\n const item = urlItem(page, moved);\n if (item) return [item];\n return items(page, {\n ...(await run(page, handler.refresh, ctx)),\n ...stated,\n });\n }\n\n /**\n * An update or a delete for several rows: the page's `crud` entry runs once\n * per key, as it would for each row alone, so an application writes\n * nothing more to allow it. What each run returns is laid together, a\n * patch onto a patch, and the refresh set runs once.\n *\n * A run that returns `url()` is reported and its row ignored: a URL names\n * one view, and several rows cannot each choose it.\n */\n async function eachKey(\n page: string,\n name: string,\n fields: RequestFields,\n ctx: HubContext,\n ): Promise<HubResponse> {\n const { keys, ...one } = fields;\n const query = fields.query ?? \"\";\n const handler = pages[page]?.requests.crud?.[query]?.[CRUD_KEYS[name]!] as\n | Handler<never>\n | undefined;\n if (!handler) {\n console.warn(\n `loadbare: page '${page}' declares no ${CRUD_KEYS[name]} for '${query}'`,\n );\n return [];\n }\n if (!Array.isArray(keys) || keys.some((key) => typeof key !== \"string\")) {\n console.warn(`loadbare: '${name}' carries keys that are not strings`);\n return [];\n }\n let stated: HubData = {};\n for (const key of keys) {\n const { [URL_QUERY]: moved, ...answer } =\n (await handler.run(ctx, { ...one, key } as never)) ?? {};\n if (moved !== undefined) {\n console.warn(\n `loadbare: ${CRUD_KEYS[name]} for '${query}' returned url() for ` +\n `one of several rows, ignoring it`,\n );\n }\n stated = layered(stated, answer);\n }\n return items(page, {\n ...(await run(page, handler.refresh, ctx)),\n ...stated,\n });\n }\n\n return {\n async dataForPage(page, ctx, { follow = true, parms } = {}) {\n const entry = pages[page];\n if (!entry) {\n console.warn(`loadbare: no page '${page}'`);\n return [];\n }\n const {\n [URL_QUERY]: moved,\n [CHOICES_QUERY]: chose,\n ...stated\n } = (await entry.requests.onPageEnter?.(ctx)) ?? {};\n if (Object.keys(stated).length > 0) {\n console.warn(\n `loadbare: page '${page}' onPageEnter returned answers; it returns ` +\n `url(), choices() or nothing, ignoring them`,\n );\n }\n const item = follow ? urlItem(page, moved) : undefined;\n if (item && !(parms && leadsHere(page, parms, item.row!))) return [item];\n // The choices go first, so the rows that read them land by them.\n return [\n ...choicesItem(page, chose),\n ...items(page, await run(page, Object.keys(entry.queries), ctx), true),\n ];\n },\n\n runRequest(page, request, ctx) {\n const { name, ...sent } = request;\n // Only a column of lb-url holds several values, and the hub answers\n // those itself: a column the server receives holds one.\n if (sent.values !== undefined && !stringValues(sent.values)) {\n console.warn(\n `loadbare: '${name}' carries values that are not one string per ` +\n `column, ignoring it`,\n );\n return Promise.resolve([]);\n }\n const fields = sent as RequestFields;\n if (fields.rows !== undefined && !wellFormedRows(fields.rows)) {\n console.warn(\n `loadbare: '${name}' carries rows that are not keys and string ` +\n `values by query, ignoring it`,\n );\n return Promise.resolve([]);\n }\n // The user's choices, told to the page. One with no `onChoices` is\n // not keeping them, and is not told.\n if (fields.query === CHOICES_QUERY) {\n const onChoices = pages[page]?.requests.onChoices;\n if (name !== REQUEST_ROW_UPDATE || !onChoices) {\n return Promise.resolve([]);\n }\n return settle(\n page,\n {\n run: (c: HubContext, r: RequestFields) =>\n onChoices(c, r.values ?? {}),\n refresh: [],\n } as Handler<never>,\n fields,\n ctx,\n \"\",\n );\n }\n if (\n fields.keys !== undefined &&\n (name === REQUEST_ROW_UPDATE || name === REQUEST_ROW_DELETE)\n ) {\n return eachKey(page, name, fields, ctx);\n }\n if (isRowRequest(name)) {\n const query = fields.query ?? \"\";\n return settle(\n page,\n pages[page]?.requests.crud?.[query]?.[CRUD_KEYS[name]!] as\n Handler<never> | undefined,\n fields,\n ctx,\n `loadbare: page '${page}' declares no ${CRUD_KEYS[name]} for '${query}'`,\n );\n }\n return settle(\n page,\n pages[page]?.requests.handlers?.[name] as Handler<never> | undefined,\n fields,\n ctx,\n `loadbare: page '${page}' declares no request '${name}'`,\n );\n },\n };\n}\n\n/** Values as a server request carries them: one string per column. */\nfunction stringValues(values: unknown): values is Record<string, string> {\n return (\n typeof values === \"object\" &&\n values !== null &&\n !Array.isArray(values) &&\n Object.values(values).every((v) => typeof v === \"string\")\n );\n}\n\n/** Gathered rows as the hub sends them: string keys and string values. */\nfunction wellFormedRows(rows: unknown): boolean {\n if (typeof rows !== \"object\" || rows === null || Array.isArray(rows)) {\n return false;\n }\n return Object.values(rows).every(\n (set) =>\n Array.isArray(set) &&\n set.every(\n (row) =>\n typeof row === \"object\" &&\n row !== null &&\n typeof row.key === \"string\" &&\n typeof row.values === \"object\" &&\n row.values !== null &&\n Object.values(row.values).every((v) => typeof v === \"string\"),\n ),\n );\n}\n\n/**\n * Two answers as one: a patch laid onto a patch for the same query keeps\n * what both name, and any other answer replaces the one before it.\n */\nfunction layered(before: HubData, after: HubData): HubData {\n const out: HubData = { ...before };\n for (const [name, answer] of Object.entries(after)) {\n const was = out[name];\n if (was && patches.has(was) && patches.has(answer as object)) {\n const a = was as Patch;\n const b = answer as Patch;\n out[name] = patch({\n rows: [...(a.rows ?? []), ...(b.rows ?? [])],\n drop: [...(a.drop ?? []), ...(b.drop ?? [])],\n });\n } else {\n out[name] = answer;\n }\n }\n return out;\n}\n\n/**\n * The `lb-url` item for a row a handler or `onPageEnter` returned, or\n * nothing when it returned none or one that cannot be used. Whatever\n * returned it has already run, so a row that cannot be used is reported and\n * the call goes on as if none had been returned.\n */\nfunction urlItem(page: string, moved: unknown): ResponseItem | undefined {\n if (moved === undefined) return undefined;\n const refused = unusableUrl(moved, true);\n if (refused === undefined) {\n return {\n query: URL_QUERY,\n kind: KIND_ROW,\n key: URL_COLUMN_PATH,\n row: moved as Row,\n };\n }\n console.warn(\n `loadbare: page '${page}' returned ${URL_QUERY} ${refused}, ignoring it`,\n );\n return undefined;\n}\n\n/**\n * The `lb-choices` item for the choices `onPageEnter` returned, or nothing\n * when it returned none or ones that cannot be used, which are reported.\n */\nfunction choicesItem(page: string, chose: unknown): ResponseItem[] {\n if (chose === undefined) return [];\n const refused = unusableUrl(chose, false);\n if (refused !== undefined) {\n console.warn(\n `loadbare: page '${page}' returned ${CHOICES_QUERY} ${refused}, ` +\n `ignoring it`,\n );\n return [];\n }\n return [\n {\n query: CHOICES_QUERY,\n kind: KIND_ROW,\n key: URL_COLUMN_PATH,\n row: chose as Row,\n },\n ];\n}\n\n/** Whether a row for `lb-url` leads to the page and parms already in hand. */\nfunction leadsHere(page: string, parms: URLSearchParams, row: Row): boolean {\n const next = nextLocation(page, parms, row);\n return next.page === page && next.parms.toString() === parms.toString();\n}\n\n/**\n * Why a returned row for `lb-url` or `lb-choices` cannot be used, or nothing\n * when it can.\n *\n * A value is a string, as a control's is and a URL's is, or for `lb-url`,\n * whose parms a URL may repeat, an array of strings. At least one column is\n * named: a row naming none changes nothing.\n */\nfunction unusableUrl(columns: unknown, several: boolean): string | undefined {\n if (\n typeof columns !== \"object\" ||\n columns === null ||\n Array.isArray(columns)\n ) {\n return \"that is not a row\";\n }\n const values = Object.values(columns);\n if (values.length === 0) return \"naming no column\";\n const usable = (v: unknown) =>\n typeof v === \"string\" ||\n (several && Array.isArray(v) && v.every((e) => typeof e === \"string\"));\n if (!values.every(usable)) {\n return several\n ? `holding a value that is not a string or an array of strings`\n : `holding a value that is not a string; write each value as \"...\"`;\n }\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lb-server.js","sourceRoot":"","sources":["../../server/lb-server.ts"],"names":[],"mappings":"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","sourcesContent":["// The server-side contract and the engine that runs it.\n//\n// This is the counterpart to core/lb-constants.ts: that file is the\n// vocabulary the browser writes, this one is the vocabulary the application\n// writes. The engine below is the whole of Loadbare on the server. It ships\n// no HTTP server, no router, and no data layer.\n\nimport type {\n ColumnTypes,\n HubData,\n HubRequest,\n HubResponse,\n HubResult,\n Kind,\n GatheredRow,\n HubRefusal,\n Patch,\n RequestFields,\n ResponseItem,\n Row,\n} from \"../core/lb-types.js\";\nimport { isRowRequest, nextLocation, oneRow, parseOrder } from \"../core/lb-types.js\";\nimport {\n COLUMN_TYPES,\n FILTERS_COLUMN_QUERY,\n FILTERS_QUERY,\n KIND_ROW,\n KIND_ROWS,\n LB_RESERVED_PREFIX,\n REQUEST_READ,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n SORT_COLUMN_ORDER,\n SORT_COLUMN_QUERY,\n SORT_QUERY,\n URL_COLUMN_PATH,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\n\n/**\n * Whatever the application hands the engine for the duration of one request.\n *\n * Loadbare declares it empty and never reads it. An application fills it in\n * by declaration merging, once, anywhere in its own source:\n *\n * declare module \"@loadbare/app/server\" {\n * interface HubContext {\n * db: Db;\n * }\n * }\n *\n * That is why no type on this page takes a type parameter. The context is a\n * request-scoped handle — an authenticated database connection is the\n * expected case — so it is passed per call rather than held by the engine.\n * The page's query parms are the other expected member: a query that reads\n * one off the context takes no argument, and re-runs under `refresh` like\n * any other.\n */\nexport interface HubContext {}\n\n/** What answers a `row` query. */\ntype RowRun = (ctx: HubContext) => HubResult | Promise<HubResult>;\n/** What answers a `rows` query, given its resolved order. */\ntype RowsRun = (ctx: HubContext, order: string) => HubResult | Promise<HubResult>;\n\n/**\n * A declared query: its kind, its key, and the run that answers it.\n *\n * Kind and key are properties of the name rather than of any one answer, so\n * they are declared here and never inferred from what comes back. The engine\n * sends both with every answer, so the markup repeats neither. Every query\n * has a key; an aggregate row answers with a constant one.\n *\n * Write one with `row()` or `rows()` below; nothing else builds one.\n */\nexport interface Query {\n readonly kind: Kind;\n readonly key: string;\n readonly run: RowRun | RowsRun;\n /** A `rows` query's order, as `rows()` declared it. */\n readonly order?: string;\n /** A `rows` query's column types, as `rows()` declared them. */\n readonly types?: ColumnTypes;\n}\n\n/**\n * What a `rows` query declares about its order.\n *\n * `order` is the order the hub places its rows by: columns separated by\n * commas, each descending when it begins with `-`, as in\n * `category_order,-opened`. An order the user chose, held in `lb-sort`,\n * replaces it. With neither, rows show in the order the query answered them\n * in.\n *\n * `types` names a type for any of its columns, and the hub sorts and\n * filters that column read as its type rather\n * than by the JSON type its values arrive as: `{ amount: \"number\" }` for a\n * Postgres `numeric` that arrives as a string. A column it does not name\n * compares by its JSON type. The order and the types go to the browser\n * once, with the answer that loads the page.\n */\nexport interface RowsOptions {\n order?: string;\n types?: ColumnTypes;\n}\n\n/**\n * A query that answers with one row, identified by its `key` column, or with\n * `null` when there is none.\n */\nexport function row(\n key: string,\n run: (ctx: HubContext) => Row | null | Promise<Row | null>,\n): Query {\n return { kind: KIND_ROW, key, run };\n}\n\n/**\n * A query that answers with all its rows. Rows land by their `key` column: a\n * row whose key is not in the answer is gone. The hub places them by the\n * order in `options`, or in the order they arrived when there is none.\n *\n * `run` receives the resolved order — the user's, from `lb-sort`, or the\n * one `options` declares — and orders its SQL by it, so the rows it answers\n * arrive in the order the page shows them in.\n */\nexport function rows(\n key: string,\n run: (ctx: HubContext, order: string) => Row[] | Promise<Row[]>,\n options: RowsOptions = {},\n): Query {\n return {\n kind: KIND_ROWS,\n key,\n run,\n ...(options.order !== undefined && { order: options.order }),\n ...(options.types !== undefined && { types: options.types }),\n };\n}\n\n/**\n * Only what changed, returned from a handler for a `rows` query. Rows named\n * here are added or updated, keys in `drop` are removed, and everything\n * unnamed is left alone — its contents, and its place in whatever order the\n * page is keeping.\n *\n * `Array.isArray` is what tells a patch from all rows, so a patch needs no\n * marker of its own and no column name is reserved to carry one.\n */\nexport function patch(change: { rows?: Row[]; drop?: unknown[] }): Patch {\n const made = { ...change };\n patches.add(made);\n return made;\n}\n\n/**\n * Every object `patch()` made. A patch and a row are both plain objects, so\n * this is how the engine refuses a patch answering a `row` query.\n */\nconst patches = new WeakSet<object>();\n\n/**\n * A new row for `lb-url`, returned from a handler when only the handler can\n * know where the page belongs: the key of a row it inserted, or the absence\n * of one it deleted.\n *\n * A column other than `lb-path` is a query parm: set, or taken out when its\n * value is empty. An array of strings repeats the parm once per value. With `lb-path` naming another page, the page is entered\n * with only the parms named here; otherwise every other parm is kept.\n *\n * The page then loads at the new URL in the same round trip, as a cold load\n * of it would, so the refresh set is not run and whatever else the handler\n * returned is dropped: both were answers for the URL the page is leaving.\n * This is Post/Redirect/Get without the redirect.\n */\nexport function url(columns: Record<string, string | string[]>): HubData {\n return { [URL_QUERY]: { ...columns } };\n}\n\n/**\n * The resolved order for each `rows` query, returned from `onPageEnter` to\n * restore what the user last chose. Each key is a query name and its value\n * the order the query runs its rows in, as `rows()` declares one.\n */\nexport function sort(orders: Record<string, string>): HubData {\n return {\n [SORT_QUERY]: Object.entries(orders).map(([query, order]) => ({\n [SORT_COLUMN_QUERY]: query,\n [SORT_COLUMN_ORDER]: order,\n })),\n };\n}\n\n/**\n * The user's filters for the page, returned from `onPageEnter` to restore\n * them. Each key is a query name and its value the filter values for that\n * query's rows, by column.\n */\nexport function filters(\n rows: Record<string, Record<string, string>>,\n): HubData {\n return {\n [FILTERS_QUERY]: Object.entries(rows).map(([query, values]) => ({\n [FILTERS_COLUMN_QUERY]: query,\n ...values,\n })),\n };\n}\n\n/**\n * A handler's refusal: thrown, and answered to the browser with the words\n * it carries. Every other error a handler throws reaches the browser as a\n * failure with nothing in it.\n */\nexport class Refusal extends Error {\n readonly refusal: HubRefusal;\n\n constructor(refusal: HubRefusal) {\n super(refusal.message ?? \"refused\");\n this.name = \"Refusal\";\n this.refusal = refusal;\n }\n}\n\n/**\n * Refuse a request, in words written for the user. Throw what it returns:\n *\n * throw refusal(\"That series already has paychecks entered.\");\n * throw refusal({ amount: \"Must be more than zero\" });\n * throw refusal(\"Two fields need attention.\", { amount: \"…\" });\n *\n * A message alone is for the request as a whole; columns name the controls\n * the request gathered, and each message is shown on its control. Thrown\n * rather than returned, so a refusal can come from anywhere the handler\n * calls, and whatever the application has open rolls back as for any\n * throw.\n */\nexport function refusal(\n message: string | Record<string, string>,\n columns?: Record<string, string>,\n): Refusal {\n return typeof message === \"string\"\n ? new Refusal({ message, ...(columns && { columns }) })\n : new Refusal({ columns: message });\n}\n\n/** The shape of a `<stub>.queries.ts` module. */\nexport type Queries = Record<string, Query>;\n\n/**\n * What a handler does, and which queries re-run once it has.\n *\n * `run` may also return answers of its own, which are laid over the\n * refreshed ones. That is how a patch reaches the browser: a query answers\n * for all its rows and cannot know why it re-ran, but the handler knows\n * exactly what it changed and can say only that.\n */\nexport interface Handler<R = RequestFields> {\n run: (\n ctx: HubContext,\n request: R,\n ) => void | HubData | Promise<void | HubData>;\n refresh: string[];\n}\n\n/**\n * `rows` holds the rows of the queries nested in the form the request came\n * from, by query name, each with its own values: a master's details,\n * written with it.\n */\nexport type RowInsertHandler = Handler<{\n key?: string;\n values: Record<string, string>;\n rows?: Record<string, GatheredRow[]>;\n}>;\n/** `values` holds only the columns being set, as an SQL UPDATE sets them. */\nexport type RowUpdateHandler = Handler<{\n key: string;\n values: Record<string, string>;\n rows?: Record<string, GatheredRow[]>;\n}>;\nexport type RowDeleteHandler = Handler<{ key: string }>;\n\n/**\n * What the requests Loadbare provides do to one query, keyed by its name in\n * `Requests.crud`. A query with no entry here permits none of them: the wire\n * cannot reach anything the page has not published.\n *\n * Every key is the request name with the prefix stripped and the rest\n * camel-cased: `lb-row-insert` runs `rowInsert`.\n */\nexport interface Crud {\n rowInsert?: RowInsertHandler;\n rowUpdate?: RowUpdateHandler;\n rowDelete?: RowDeleteHandler;\n}\n\n/**\n * The shape of a `<stub>.requests.ts` module.\n *\n * `onPageEnter` runs when the page loads, before its queries. It declares no\n * refresh set: the page's queries all run afterward, so whatever it changed\n * is already in the response. It may return `url()` instead, to move the\n * page to other query parms or another page before anything shows, as an\n * application restoring what the user last had on screen does. The page\n * then loads there in the same round trip, and `onPageEnter` running for\n * that load is not followed again. It may return `sort()`, `filters()`,\n * with `url()` or alone, to restore the user's choices for the page.\n *\n * `onChoices` runs each time the user changes the sort or a filter, with\n * the query and its new sort or filters, so an application can keep them\n * for `onPageEnter` to restore. It answers with nothing, by convention.\n *\n * `handlers` holds the page's declared requests, by name. A name the page\n * did not declare is refused, so the wire cannot reach anything the page has\n * not published.\n *\n * `crud` holds what the requests Loadbare provides run, by query name.\n */\nexport interface Requests {\n onPageEnter?: (ctx: HubContext) => void | HubData | Promise<void | HubData>;\n onChoices?: (\n ctx: HubContext,\n change: {\n query: string;\n sort?: string;\n filters?: Record<string, string>;\n },\n ) => void | HubData | Promise<void | HubData>;\n handlers?: Record<string, Handler>;\n crud?: Record<string, Crud>;\n}\n\n/** A page is three files sharing a stub; two of them are these. */\nexport interface Page {\n queries: Queries;\n requests: Requests;\n}\n\n/**\n * The page registry. The builder writes it from every `.requests.ts` and\n * `.queries.ts` it discovers.\n */\nexport type Pages = Record<string, Page>;\n\n/**\n * How a page load treats a `url()` its `onPageEnter` returned.\n *\n * With `follow` false it is ignored and the queries run, as they must for a\n * load that is itself at a URL something else moved to. With `parms`, the\n * query parms the page is being loaded at, one that leads to the page and\n * parms already in hand is ignored the same way, so the page loads once.\n */\nexport interface PageLoadOptions {\n follow?: boolean;\n parms?: URLSearchParams;\n}\n\n/**\n * The engine. Both calls answer with response items. A request whose\n * handler returned `url()`, and a page load whose `onPageEnter` did, answer\n * with the `lb-url` item alone: the caller builds a context at the new URL,\n * calls `dataForPage` with it and `follow` false, and sends the load beside the\n * item. `hubRoutes` does exactly that.\n */\nexport interface Hub {\n /** A page load: its `onPageEnter`, then all of its queries. */\n dataForPage(\n page: string,\n ctx: HubContext,\n options?: PageLoadOptions,\n ): Promise<HubResponse>;\n\n /** A request: run the handler its name picks, then the refresh set. */\n runRequest(\n page: string,\n request: HubRequest,\n ctx: HubContext,\n ): Promise<HubResponse>;\n}\n\n/** The camel-cased `Crud` key of a request Loadbare provides. */\nconst CRUD_KEYS: Record<string, keyof Crud> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * Build the engine over a set of pages. The pages are fixed at startup; the\n * context is not, and arrives with each call.\n */\nexport function createHub(pages: Pages): Hub {\n // Names beginning with the reserved prefix are Loadbare's — its own query,\n // the requests it provides — so an application cannot declare one.\n // Refused at startup, because a name is a fact about the page and not\n // about any request.\n for (const [page, entry] of Object.entries(pages)) {\n const declared = [\n ...Object.keys(entry.queries),\n ...Object.keys(entry.requests.handlers ?? {}),\n ...Object.keys(entry.requests.crud ?? {}),\n ];\n for (const name of declared) {\n if (name.startsWith(LB_RESERVED_PREFIX)) {\n throw new Error(\n `loadbare: page '${page}' declares '${name}', but names ` +\n `beginning with '${LB_RESERVED_PREFIX}' are reserved`,\n );\n }\n }\n\n // A crud handler always knows which rows it changed: an update or a\n // delete names its row by key, and an insert has the new row's key once\n // it has written it. So each can answer with a patch, and the hub places\n // what the patch names by the query's order. Refreshing the whole set\n // instead sends every row to say one thing. There is no case where that\n // is the answer, so it is refused rather than warned about.\n // An order is a fact about the query, so a malformed one is refused at\n // startup, as a reserved name is.\n // So is a column type the hub does not know.\n for (const [name, query] of Object.entries(entry.queries)) {\n for (const [column, type] of Object.entries(query.types ?? {})) {\n if (!(COLUMN_TYPES as readonly string[]).includes(type)) {\n throw new Error(\n `loadbare: page '${page}' query '${name}' declares column ` +\n `'${column}' as '${type}'; a column type is one of ` +\n `${COLUMN_TYPES.join(\", \")}`,\n );\n }\n }\n if (query.order === undefined) continue;\n try {\n parseOrder(query.order);\n } catch (err) {\n throw new Error(\n `loadbare: page '${page}' query '${name}': ${(err as Error).message}`,\n );\n }\n }\n\n for (const [name, crud] of Object.entries(entry.requests.crud ?? {})) {\n if (entry.queries[name]?.kind !== KIND_ROWS) continue;\n for (const request of [\"rowInsert\", \"rowUpdate\", \"rowDelete\"] as const) {\n if (crud[request]?.refresh.includes(name)) {\n throw new Error(\n `loadbare: page '${page}' refreshes '${name}' after its own ` +\n `${request}; return the changed rows in a patch instead`,\n );\n }\n }\n }\n }\n\n /**\n * Answers into response items, adding the kind and key each query\n * declared. An answer that disagrees with its declaration is a mistake in\n * the application rather than a case to handle, and is left out: a page is\n * better off missing one query than showing the wrong shape for it.\n *\n * A page load also adds each `rows` query's order and column types, which\n * the hub keeps for the answers after it.\n */\n function items(page: string, data: HubData, load = false): HubResponse {\n const out: ResponseItem[] = [];\n for (const [name, result] of Object.entries(data)) {\n const query = pages[page]?.queries[name];\n if (!query) {\n console.warn(`loadbare: page '${page}' declares no query '${name}'`);\n continue;\n }\n const item: ResponseItem = {\n query: name,\n kind: query.kind,\n key: query.key,\n };\n if (query.kind === KIND_ROW) {\n if (Array.isArray(result) || patches.has(result as object)) {\n console.warn(\n `loadbare: query '${name}' is declared row but answered with ` +\n `${Array.isArray(result) ? \"rows\" : \"a patch\"}`,\n );\n continue;\n }\n // Every answer is rows: a `row` query's is its row, or none.\n item.rows = result == null ? [] : [result as Row];\n } else {\n if (result === null || result === undefined) {\n console.warn(\n `loadbare: query '${name}' is declared rows but answered with nothing`,\n );\n continue;\n }\n if (Array.isArray(result)) item.rows = result;\n else item.patch = result as Patch;\n if (load && query.types !== undefined) item.types = query.types;\n }\n out.push(item);\n }\n return out;\n }\n\n /**\n * Run queries by name, each answering with all its rows or its row. A\n * `rows` query receives the resolved order for its name from `sorts`, or\n * its declared default, and the order each ran with is returned beside the\n * data for the caller to write to `lb-sort`.\n */\n async function run(\n page: string,\n names: string[],\n ctx: HubContext,\n sorts?: Map<string, string>,\n ): Promise<{ data: HubData; orders: Map<string, string> }> {\n const data: HubData = {};\n const orders = new Map<string, string>();\n for (const name of names) {\n const query = pages[page]?.queries[name];\n if (!query) {\n console.warn(`loadbare: page '${page}' declares no query '${name}'`);\n continue;\n }\n const order = sorts?.get(name) ?? query.order ?? \"\";\n const result =\n query.kind === KIND_ROWS\n ? await (query.run as RowsRun)(ctx, order)\n : await (query.run as RowRun)(ctx);\n if (Array.isArray(result) !== (query.kind === KIND_ROWS)) {\n console.warn(\n `loadbare: query '${name}' is declared ${query.kind} but answered ` +\n `with ${Array.isArray(result) ? \"rows\" : result == null ? \"nothing\" : \"one row\"}`,\n );\n continue;\n }\n data[name] = result;\n if (query.kind === KIND_ROWS && order !== \"\") orders.set(name, order);\n }\n return { data, orders };\n }\n\n /**\n * Run a handler, then its refresh set against the same context, laying\n * what the handler itself returned over the refreshed queries — the\n * narrower answer wins because it knows what actually changed.\n *\n * A handler that returned `url()` answers with the `lb-url` item alone.\n * Loading the page there needs a context built at the new URL, and\n * building one is the caller's.\n */\n async function settle(\n page: string,\n handler: Handler<never> | undefined,\n request: RequestFields,\n ctx: HubContext,\n notFound: string,\n ): Promise<HubResponse> {\n if (!handler) {\n console.warn(notFound);\n return [];\n }\n const { [URL_QUERY]: moved, ...stated } =\n (await handler.run(ctx, request as never)) ?? {};\n const item = urlItem(page, moved);\n if (item) return [item];\n return items(page, {\n ...(await run(page, handler.refresh, ctx)).data,\n ...stated,\n });\n }\n\n return {\n async dataForPage(page, ctx, { follow = true, parms } = {}) {\n const entry = pages[page];\n if (!entry) {\n console.warn(`loadbare: no page '${page}'`);\n return [];\n }\n const {\n [URL_QUERY]: moved,\n [SORT_QUERY]: sorts,\n [FILTERS_QUERY]: filters,\n ...stated\n } = (await entry.requests.onPageEnter?.(ctx)) ?? {};\n if (Object.keys(stated).length > 0) {\n console.warn(\n `loadbare: page '${page}' onPageEnter returned answers; it returns ` +\n `url(), sort(), filters() or nothing, ignoring them`,\n );\n }\n const item = follow ? urlItem(page, moved) : undefined;\n if (item && !(parms && leadsHere(page, parms, oneRow(item)!))) return [item];\n const { data, orders } = await run(\n page,\n Object.keys(entry.queries),\n ctx,\n sortsToMap(sorts),\n );\n // The sort and filters go first, so the rows that read them land by\n // them.\n return [\n ...sortItem(orders),\n ...filtersItem(page, filters),\n ...items(page, data, true),\n ];\n },\n\n async runRequest(page, request, ctx) {\n const { name, ...sent } = request;\n // The hub's read: the queries named, at the parms the request was sent\n // at, answered as any query is.\n if (name === REQUEST_READ) {\n const declared = pages[page]?.queries ?? {};\n const names = (Array.isArray(sent.queries) ? sent.queries : []).filter(\n (q): q is string => typeof q === \"string\" && q in declared,\n );\n return run(page, names, ctx).then(({ data }) => items(page, data));\n }\n // Only a column of lb-url holds several values, and the hub answers\n // those itself: a column the server receives holds one.\n if (sent.values !== undefined && !stringValues(sent.values)) {\n console.warn(\n `loadbare: '${name}' carries values that are not one string per ` +\n `column, ignoring it`,\n );\n return Promise.resolve([]);\n }\n const fields = sent as RequestFields;\n if (fields.rows !== undefined && !wellFormedRows(fields.rows)) {\n console.warn(\n `loadbare: '${name}' carries rows that are not keys and string ` +\n `values by query, ignoring it`,\n );\n return Promise.resolve([]);\n }\n // A sort or filter the user changed, told to the page. One with no\n // `onChoices` is not keeping them, and is not told.\n if (fields.query === SORT_QUERY || fields.query === FILTERS_QUERY) {\n const onChoices = pages[page]?.requests.onChoices;\n if (name !== REQUEST_ROW_UPDATE || !onChoices) {\n return Promise.resolve([]);\n }\n const query = fields.key ?? \"\";\n const sort =\n fields.query === SORT_QUERY\n ? fields.values?.[SORT_COLUMN_ORDER]\n : undefined;\n const filters =\n fields.query === FILTERS_QUERY ? fields.values : undefined;\n await onChoices(ctx, {\n query,\n ...(sort !== undefined && { sort }),\n ...(filters !== undefined && { filters }),\n });\n // The browser re-sorts or re-filters the rows it holds; the server\n // only persists the change for the next page.\n return [];\n }\n if (isRowRequest(name)) {\n const query = fields.query ?? \"\";\n return settle(\n page,\n pages[page]?.requests.crud?.[query]?.[CRUD_KEYS[name]!] as\n Handler<never> | undefined,\n fields,\n ctx,\n `loadbare: page '${page}' declares no ${CRUD_KEYS[name]} for '${query}'`,\n );\n }\n return settle(\n page,\n pages[page]?.requests.handlers?.[name] as Handler<never> | undefined,\n fields,\n ctx,\n `loadbare: page '${page}' declares no request '${name}'`,\n );\n },\n };\n}\n\n/** Values as a server request carries them: one string per column. */\nfunction stringValues(values: unknown): values is Record<string, string> {\n return (\n typeof values === \"object\" &&\n values !== null &&\n !Array.isArray(values) &&\n Object.values(values).every((v) => typeof v === \"string\")\n );\n}\n\n/** Gathered rows as the hub sends them: string keys and string values. */\nfunction wellFormedRows(rows: unknown): boolean {\n if (typeof rows !== \"object\" || rows === null || Array.isArray(rows)) {\n return false;\n }\n return Object.values(rows).every(\n (set) =>\n Array.isArray(set) &&\n set.every(\n (row) =>\n typeof row === \"object\" &&\n row !== null &&\n typeof row.key === \"string\" &&\n typeof row.values === \"object\" &&\n row.values !== null &&\n Object.values(row.values).every((v) => typeof v === \"string\"),\n ),\n );\n}\n\n/**\n * Two answers as one: a patch laid onto a patch for the same query keeps\n * what both name, and any other answer replaces the one before it.\n */\nfunction layered(before: HubData, after: HubData): HubData {\n const out: HubData = { ...before };\n for (const [name, answer] of Object.entries(after)) {\n const was = out[name];\n if (was && patches.has(was) && patches.has(answer as object)) {\n const a = was as Patch;\n const b = answer as Patch;\n out[name] = patch({\n rows: [...(a.rows ?? []), ...(b.rows ?? [])],\n drop: [...(a.drop ?? []), ...(b.drop ?? [])],\n });\n } else {\n out[name] = answer;\n }\n }\n return out;\n}\n\n/**\n * The `lb-url` item for a row a handler or `onPageEnter` returned, or\n * nothing when it returned none or one that cannot be used. Whatever\n * returned it has already run, so a row that cannot be used is reported and\n * the call goes on as if none had been returned.\n */\nfunction urlItem(page: string, moved: unknown): ResponseItem | undefined {\n if (moved === undefined) return undefined;\n const refused = unusableUrl(moved, true);\n if (refused === undefined) {\n return {\n query: URL_QUERY,\n kind: KIND_ROW,\n key: URL_COLUMN_PATH,\n rows: [moved as Row],\n };\n }\n console.warn(\n `loadbare: page '${page}' returned ${URL_QUERY} ${refused}, ignoring it`,\n );\n return undefined;\n}\n\n/** The sorts `onPageEnter` returned, as a map of query name to order. */\nfunction sortsToMap(sorts: unknown): Map<string, string> {\n const map = new Map<string, string>();\n if (!Array.isArray(sorts)) return map;\n for (const row of sorts) {\n if (typeof row !== \"object\" || row === null) continue;\n const r = row as Record<string, unknown>;\n if (\n typeof r[SORT_COLUMN_QUERY] === \"string\" &&\n typeof r[SORT_COLUMN_ORDER] === \"string\"\n ) {\n map.set(r[SORT_COLUMN_QUERY], r[SORT_COLUMN_ORDER]);\n }\n }\n return map;\n}\n\n/**\n * The `lb-sort` item for the orders each `rows` query ran with, or nothing\n * when none ran with an order.\n */\nfunction sortItem(orders: Map<string, string>): ResponseItem[] {\n if (orders.size === 0) return [];\n return [\n {\n query: SORT_QUERY,\n kind: KIND_ROWS,\n key: SORT_COLUMN_QUERY,\n rows: [...orders.entries()].map(([query, order]) => ({\n [SORT_COLUMN_QUERY]: query,\n [SORT_COLUMN_ORDER]: order,\n })),\n },\n ];\n}\n\n/**\n * The `lb-filters` item for the filters `onPageEnter` returned, or nothing\n * when it returned none. Each row names its query and its filter values, by\n * column.\n */\nfunction filtersItem(page: string, filters: unknown): ResponseItem[] {\n if (filters === undefined) return [];\n if (!Array.isArray(filters)) {\n console.warn(\n `loadbare: page '${page}' returned ${FILTERS_QUERY} that is not a ` +\n `list, ignoring it`,\n );\n return [];\n }\n const rows: Row[] = [];\n for (const row of filters) {\n if (typeof row !== \"object\" || row === null) continue;\n const r = row as Record<string, unknown>;\n if (typeof r[FILTERS_COLUMN_QUERY] !== \"string\") continue;\n const values = Object.fromEntries(\n Object.entries(r).filter(\n ([key, value]) => key !== FILTERS_COLUMN_QUERY && typeof value === \"string\",\n ),\n ) as Record<string, string>;\n rows.push({ [FILTERS_COLUMN_QUERY]: r[FILTERS_COLUMN_QUERY], ...values });\n }\n if (rows.length === 0) return [];\n return [\n {\n query: FILTERS_QUERY,\n kind: KIND_ROWS,\n key: FILTERS_COLUMN_QUERY,\n rows,\n },\n ];\n}\n\n/** Whether a row for `lb-url` leads to the page and parms already in hand. */\nfunction leadsHere(page: string, parms: URLSearchParams, row: Row): boolean {\n const next = nextLocation(page, parms, row);\n return next.page === page && next.parms.toString() === parms.toString();\n}\n\n/**\n * Why a returned row for `lb-url` cannot be used, or nothing when it can.\n *\n * A value is a string, as a control's is and a URL's is, or for `lb-url`,\n * whose parms a URL may repeat, an array of strings. At least one column is\n * named: a row naming none changes nothing.\n */\nfunction unusableUrl(columns: unknown, several: boolean): string | undefined {\n if (\n typeof columns !== \"object\" ||\n columns === null ||\n Array.isArray(columns)\n ) {\n return \"that is not a row\";\n }\n const values = Object.values(columns);\n if (values.length === 0) return \"naming no column\";\n const usable = (v: unknown) =>\n typeof v === \"string\" ||\n (several && Array.isArray(v) && v.every((e) => typeof e === \"string\"));\n if (!values.every(usable)) {\n return several\n ? `holding a value that is not a string or an array of strings`\n : `holding a value that is not a string; write each value as \"...\"`;\n }\n return undefined;\n}\n"]}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Binding cases
|
|
2
|
+
|
|
3
|
+
Concept Zero: All data is in rows, rows belong to named queries.
|
|
4
|
+
- Everything is a row. A row is a javascript object where
|
|
5
|
+
the values are always scalar (Loadbare does not understand object
|
|
6
|
+
in object)
|
|
7
|
+
- A named query has zero, one or more rows in its results.
|
|
8
|
+
- A query has an owner, either the application or the hub.
|
|
9
|
+
|
|
10
|
+
Browser Navigation goes through the hub-owned `lb-url` query, which
|
|
11
|
+
is read/write in the browser and on the server.
|
|
12
|
+
|
|
13
|
+
Queries are bound to HTML by the shape of the HTML. An element names
|
|
14
|
+
a query with `lb-query="query-name"`. If that element does not contain
|
|
15
|
+
an HTML `<template>`, then it is assumed its children represent only one row.
|
|
16
|
+
If it contains the template, it can display any number of rows, where
|
|
17
|
+
the template is the representation of a row within the query results.
|
|
18
|
+
|
|
19
|
+
The hub "lands" values onto elements by their declared `lb-column`
|
|
20
|
+
attribute.
|
|
21
|
+
|
|
22
|
+
## CRUD Cases
|
|
23
|
+
|
|
24
|
+
Application owned queries are processed on the server and sent to the
|
|
25
|
+
browser, where the hub "lands" the values by the `lb-column` attribute
|
|
26
|
+
on elements.
|
|
27
|
+
|
|
28
|
+
The `lb-action` attribute allows update, insert and delete of single
|
|
29
|
+
rows.
|
|
30
|
+
|
|
31
|
+
Because list elements just contain individual rows, no special vocabulary
|
|
32
|
+
is required to enable CRUD on the single row of a list - each element
|
|
33
|
+
in the list acts like a standalone row.
|
|
34
|
+
|
|
35
|
+
The vocabulary is expanded to allow update, insert and delete of
|
|
36
|
+
multiple rows on list elements.
|
|
37
|
+
|
|
38
|
+
## UI Cases
|
|
39
|
+
|
|
40
|
+
Within the UI we speak of "interaction state", which represents whether
|
|
41
|
+
a row being edited by the user is new or existing.
|
|
42
|
+
|
|
43
|
+
The cases build in three layers, and each layer adds one idea. The notes
|
|
44
|
+
under each layer say where today's markup departs from it. Navigation, the
|
|
45
|
+
hub's other rows, and a query owned in the browser sit outside the layers.
|
|
46
|
+
Today a query parm the URL repeats is one column holding an array, where
|
|
47
|
+
every other value is scalar.
|
|
48
|
+
|
|
49
|
+
A verb is aimed at a target: the bound row or the list.
|
|
50
|
+
Insert, update and delete are verbs, and so is any request the application
|
|
51
|
+
names, such as marking an item complete; the owner decides what a named
|
|
52
|
+
verb does. The owner answers with every row the write changed, in any
|
|
53
|
+
query it owns, and may move the URL. A write can therefore add, change or
|
|
54
|
+
drop rows of other queries, recalculate a whole list, or move a row from
|
|
55
|
+
one list to another.
|
|
56
|
+
|
|
57
|
+
An element that is a row with no binding takes its target from the verb:
|
|
58
|
+
an insert makes a new row.
|
|
59
|
+
|
|
60
|
+
### Layer 1: one row
|
|
61
|
+
|
|
62
|
+
An element that is a row, most often a form, is bound to a new row or to a
|
|
63
|
+
row on record. A new row inserts, a row on record updates, and either can
|
|
64
|
+
be deleted; deleting a new row discards it.
|
|
65
|
+
|
|
66
|
+
| Case | What the user does |
|
|
67
|
+
|--------------------|---------------------------------------------------------------------|
|
|
68
|
+
| Show | sees the row |
|
|
69
|
+
| Create one | fills an empty row and saves it |
|
|
70
|
+
| Edit one | changes a row on record and saves it |
|
|
71
|
+
| Delete one | deletes the row |
|
|
72
|
+
| Abandon | leaves the row unsaved, reverted or discarded |
|
|
73
|
+
| Create in sequence | saves a new row, and the next starts, carrying values |
|
|
74
|
+
| Choose | sets a column to a row of another query, or creates one and sets it |
|
|
75
|
+
|
|
76
|
+
Interaction state for one row: whether it is new or on record, and whether
|
|
77
|
+
the user has changed it.
|
|
78
|
+
|
|
79
|
+
A new row may start with values: carried forward from the row saved before
|
|
80
|
+
it, taken from what the page shows, or copied from another row. The
|
|
81
|
+
mechanism is not decided.
|
|
82
|
+
|
|
83
|
+
A picker is a list whose rows are the choices for one column of a row. It
|
|
84
|
+
is a control of layer 1 rather than a list the user works on. Choosing sets
|
|
85
|
+
the column to the chosen row's key. Creating a row in the picker's query is
|
|
86
|
+
Create one, and the new row's key is then chosen.
|
|
87
|
+
|
|
88
|
+
Today:
|
|
89
|
+
- Create one. A form for a new record is bound to the saved row by moving
|
|
90
|
+
the URL to its key, rather than by binding the form.
|
|
91
|
+
- Abandon. Nothing reverts a row on record that the user has changed in
|
|
92
|
+
place. A new row in a list is discarded by a delete that the hub answers
|
|
93
|
+
with no round trip, a request acting as a command.
|
|
94
|
+
- Create in sequence. The ghost row is an insert form laid out as a table
|
|
95
|
+
row, outside the list. Nothing carries values forward. A server can give
|
|
96
|
+
a new record starting values by answering its query with them, with an
|
|
97
|
+
empty key.
|
|
98
|
+
- Choose. Creating a row and choosing it is the widget `lb-insert-dialog`'s
|
|
99
|
+
work, in its own script.
|
|
100
|
+
|
|
101
|
+
### Layer 2: a list
|
|
102
|
+
|
|
103
|
+
Every row of a list is a layer 1 row. The user works on it in place, or in
|
|
104
|
+
a form bound to it; which one is a choice of HTML, and the binding is the
|
|
105
|
+
same. A list adds only what many rows make possible.
|
|
106
|
+
|
|
107
|
+
| Case | What the list adds |
|
|
108
|
+
|---------------------------|----------------------------------------------|
|
|
109
|
+
| Show | order and filter |
|
|
110
|
+
| Any layer 1 case | on one of its rows |
|
|
111
|
+
| Several changes, one save | rows added, changed, removed, saved together |
|
|
112
|
+
|
|
113
|
+
Interaction state for a list adds which rows are
|
|
114
|
+
marked for deletion at the next save.
|
|
115
|
+
|
|
116
|
+
Today:
|
|
117
|
+
- Several changes, one save. Only new rows can wait for a save, and only in
|
|
118
|
+
a list inside a form's own query, which is layer 3. Nothing saves changes
|
|
119
|
+
to rows on record together, or marks rows for deletion at the next save.
|
|
120
|
+
|
|
121
|
+
### Layer 3: a row with a list inside it
|
|
122
|
+
|
|
123
|
+
A layer 1 row whose element holds a layer 2 list, saved together: a master
|
|
124
|
+
and its details. The list inside follows the outer row's binding, so a
|
|
125
|
+
form bound to another row shows that row's details.
|
|
126
|
+
|
|
127
|
+
| Case | What the user does |
|
|
128
|
+
|----------------|---------------------------------------------|
|
|
129
|
+
| Saved together | changes a row and the list inside it, saves |
|
|
130
|
+
|
|
131
|
+
A master and its details as two queries saved separately is a layer 1 row
|
|
132
|
+
beside a layer 2 list, and not layer 3.
|
|
133
|
+
|
|
134
|
+
Today:
|
|
135
|
+
- The list inside follows the outer row's binding by a round trip: Edit and
|
|
136
|
+
New read the list's query again, with the bound key as a parm.
|
|
137
|
+
- A new master is keyed `""` by either of two means: its query answering an
|
|
138
|
+
empty key, or New aimed at the form.
|
|
139
|
+
|
|
140
|
+
## Decisions
|
|
141
|
+
|
|
142
|
+
- One shape. The wire and the hub carry every answer as rows, zero or
|
|
143
|
+
more. The server's `row()` declares a query that answers with at most
|
|
144
|
+
one row; the declaration is checked, and it gives the query's elements
|
|
145
|
+
their default binding.
|
|
146
|
+
- The hub owns interaction state, and keeps it where the platform has a
|
|
147
|
+
place for it: a checkbox's `checked`, a control's value, a dialog's
|
|
148
|
+
`open`, an element's stamps. A query the hub owns holds only what the
|
|
149
|
+
platform has no place for: the URL row, the user's choices, and the hub's
|
|
150
|
+
status. A form's binding is its `lb-key-value` and its filled controls.
|
package/docs/comparison.md
CHANGED
|
@@ -202,8 +202,7 @@ observable, and no client cache that answers a request.
|
|
|
202
202
|
the path, the page label and the query parms.
|
|
203
203
|
- An element hidden by `lb-show` exists inside a `<template>` standing where
|
|
204
204
|
it stood.
|
|
205
|
-
- A form pointed at a row carries that row's key as `lb-key-value
|
|
206
|
-
row ticked by its `lb-select` checkbox carries `lb-row-selected`.
|
|
205
|
+
- A form pointed at a row carries that row's key as `lb-key-value`.
|
|
207
206
|
- The hub keeps each query's last answer until the page changes, only to
|
|
208
207
|
fill an element that names the query and arrives after it, such as a
|
|
209
208
|
picker in a new live row, to fill a form pointed at one of its rows, and
|
|
@@ -265,15 +264,12 @@ At run time the hub performs a closed set of operations:
|
|
|
265
264
|
| All rows | Place every row by the query's order, or the user's; remove rows whose keys did not arrive |
|
|
266
265
|
| An answer to a request, under `lb-manual` | Leave the rows already showing where they stand, stamp `lb-row-unplaced` on each out of place |
|
|
267
266
|
| A patch | Upsert the rows named, remove the keys named, leave other rows in place |
|
|
268
|
-
| A group template | Build a group where the order breaks, keep it while its run goes on, remove it with its last row |
|
|
269
|
-
| An aggregate (`lb-count`, `lb-sum` and the rest) | Set the element from the rows showing in its group, or in the whole list |
|
|
270
267
|
| A row changes, moves or leaves | Stamp `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested` or `lb-row-leaving`; remove a leaving row once its animations finish |
|
|
271
268
|
| After rows land | Stamp `lb-query-row-count`, `lb-query-unplaced-count` and `lb-query-order` |
|
|
272
269
|
| The user makes a choice | Land `lb-choices`, place and filter the rows already held again, tell the server |
|
|
273
270
|
| A row the user's filter does not pass | Set `hidden` on it |
|
|
274
271
|
| An `lb-show` column | Move the element into, or out of, a `<template lb-show>` |
|
|
275
272
|
| A `commandfor` button with `--lb-edit` or `--lb-new` | Fill the form from the held row, or empty it; stamp `lb-key-value` and `lb-row-editing`; open its `<dialog>` |
|
|
276
|
-
| An `lb-select` checkbox is ticked | Stamp `lb-row-selected` on its row |
|
|
277
273
|
| A request starts or ends | Set or remove `lb-request-pending` and `aria-busy`; set `lb-request-error` on failure, `lb-request-refused` and control validity on a refusal |
|
|
278
274
|
| An insert from a form succeeds | Reset the form |
|
|
279
275
|
| A request from a pointed form succeeds | Let the form go: empty it, drop its key, close its `<dialog>` |
|
|
@@ -324,7 +320,7 @@ There are two kinds of placeholder, and they run at different times.
|
|
|
324
320
|
a whole attribute value or a whole text node. The tag supplies it with an
|
|
325
321
|
`exp-name` attribute. An unsupplied placeholder with no default drops the
|
|
326
322
|
attribute. The value becomes permanent in the shipped HTML.
|
|
327
|
-
- Run time. Data lands through `lb-column`,
|
|
323
|
+
- Run time. Data lands through `lb-column`,
|
|
328
324
|
`lb-query` with a row or group `<template>`, and `lb-show`. There is no
|
|
329
325
|
run-time interpolation into attribute values or text.
|
|
330
326
|
|
|
@@ -496,8 +492,7 @@ query declared.
|
|
|
496
492
|
|
|
497
493
|
A column holds one value. A set of values is a query of its own. A row
|
|
498
494
|
never contains rows. Master-detail is a row and rows answered under two
|
|
499
|
-
names. Many masters with their details is one `rows` query of joined rows
|
|
500
|
-
shown in groups by a group template.
|
|
495
|
+
names. Many masters with their details is one `rows` query of joined rows.
|
|
501
496
|
|
|
502
497
|
The application designs no endpoints, routes or serialization format. The
|
|
503
498
|
names in the markup are the keys in the page's `.queries.ts` and
|
|
@@ -572,10 +567,6 @@ request the page declares.
|
|
|
572
567
|
- A list carrying `lb-manual` leaves its rows where they stand, by order,
|
|
573
568
|
filter or both, for an answer to any request. A fourth command,
|
|
574
569
|
`--lb-place`, aimed at the list, places them with no round trip.
|
|
575
|
-
- `<input type="checkbox" lb-select>` in a row template selects its row. An
|
|
576
|
-
update or delete for the query that names no one row carries the selected
|
|
577
|
-
keys as `keys`, and the server runs `rowUpdate` or `rowDelete` once per
|
|
578
|
-
key.
|
|
579
570
|
|
|
580
571
|
On the server, each entry has `run` and `refresh`. `run` does the work and
|
|
581
572
|
may return results, such as a `patch()`, or a new row for the URL with
|
|
@@ -685,7 +676,7 @@ for the mechanisms by project.
|
|
|
685
676
|
`<dialog lb-url-unknown>` if the chrome has one.
|
|
686
677
|
- The hub serves its own query, `lb-url`, of kind `row` and keyed by
|
|
687
678
|
`lb-path`. Its columns are `lb-path`, `lb-page-label` (the page file's
|
|
688
|
-
`<title>`, or its stub),
|
|
679
|
+
`<title>`, or its stub), and one column per query parm,
|
|
689
680
|
bound like any server row.
|
|
690
681
|
- A control inside `lb-query="lb-url"` carrying `lb-request="lb-row-update"`
|
|
691
682
|
writes a query parm. The hub answers that request itself: it replaces the
|
|
@@ -725,10 +716,10 @@ answers 200 for an unknown path, as Loadbare/app does.
|
|
|
725
716
|
### Loadbare/app
|
|
726
717
|
|
|
727
718
|
```
|
|
728
|
-
loadbare-app-build [--src src] [--out dist] [--
|
|
719
|
+
loadbare-app-build [--src src] [--out dist] [--minify]
|
|
729
720
|
```
|
|
730
721
|
|
|
731
|
-
-
|
|
722
|
+
- Three flags. No configuration file.
|
|
732
723
|
- Files are classified by name: `chrome.html`, `*.page.html`,
|
|
733
724
|
`*.queries.ts`, `*.requests.ts`, `imports.ts`, `*.css`, `<tag>.html`,
|
|
734
725
|
`<tag>.browser.ts`.
|
|
@@ -740,8 +731,10 @@ loadbare-app-build [--src src] [--out dist] [--watch] [--minify]
|
|
|
740
731
|
widget script whose tag is never used is never imported.
|
|
741
732
|
- Outputs: `app.html`, `client.js`, `app.css` when there is a stylesheet,
|
|
742
733
|
`pages.ts` when there is page data, and `client-entry.ts`.
|
|
743
|
-
-
|
|
744
|
-
|
|
734
|
+
- The builder runs once. `@loadbare/app/dev` is the dev server: it
|
|
735
|
+
rebuilds on changes to `.html`, `.ts` and `.css` files and restarts the
|
|
736
|
+
application's server when a server-side file changed. There is no hot
|
|
737
|
+
module replacement and no browser reload.
|
|
745
738
|
- `app.html` is formatted with Prettier and never minified.
|
|
746
739
|
|
|
747
740
|
Checked at build time, as errors:
|
|
@@ -770,12 +763,8 @@ Checked at build time, as errors:
|
|
|
770
763
|
`<lb-hub>`
|
|
771
764
|
- `lb-show` on a `<template>`, on a row template's root, with no `lb-query`
|
|
772
765
|
around it, or written `!` or `!!column`
|
|
773
|
-
- `lb-group` on anything but a `<template>` holding a `<template>`, and an
|
|
774
|
-
aggregate on a `<template>` or naming no column
|
|
775
766
|
- `lb-order` or `lb-filter` on an element not carrying `lb-query`, or naming
|
|
776
767
|
no column
|
|
777
|
-
- `lb-select` on anything but a checkbox in a row template, or beside
|
|
778
|
-
`lb-column` or `lb-request`
|
|
779
768
|
- a `command` beginning `--lb-` that is not one of the three, and one of the
|
|
780
769
|
three with no `commandfor`
|
|
781
770
|
- an `lb-query` naming a query the page does not declare, an `lb-request`
|
|
@@ -824,12 +813,12 @@ Nothing is scoped, renamed, added or removed.
|
|
|
824
813
|
widgets.
|
|
825
814
|
- The hub stamps attributes a stylesheet can select on: `lb-column-value`,
|
|
826
815
|
`lb-key-value`, `lb-row-live`, `lb-query-row-count`, the group stamps,
|
|
827
|
-
`lb-row-editing`, `lb-
|
|
816
|
+
`lb-row-editing`, `lb-request-pending`,
|
|
828
817
|
`lb-request-error` and `lb-request-refused`. An empty `rows` query is
|
|
829
818
|
styled with `[lb-query-row-count="0"]`.
|
|
830
819
|
- Every change the hub makes to a list is stamped: `lb-row-created`,
|
|
831
|
-
`lb-row-changed`, `lb-row-moved`, `lb-row-requested
|
|
832
|
-
`lb-
|
|
820
|
+
`lb-row-changed`, `lb-row-moved`, `lb-row-requested` and
|
|
821
|
+
`lb-row-leaving`, and a row an answer left out of place carries
|
|
833
822
|
`lb-row-unplaced`. A leaving row is removed once its animations finish,
|
|
834
823
|
so an application animates rows with CSS alone.
|
|
835
824
|
|
|
@@ -990,8 +979,8 @@ cross-reference.
|
|
|
990
979
|
|
|
991
980
|
| Owned | Count | Names |
|
|
992
981
|
| ------------------------------------ | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
993
|
-
| `lb-*` attributes a developer writes |
|
|
994
|
-
| `lb-*` stamps |
|
|
982
|
+
| `lb-*` attributes a developer writes | 11 | `lb-query`, `lb-column`, `lb-show`, `lb-order`, `lb-filter`, `lb-manual`, `lb-request`, `lb-url-link`, `lb-url-parm`, `lb-url-push`, `lb-url-unknown` |
|
|
983
|
+
| `lb-*` stamps | 23 | `lb-column-value`, `lb-key-value`, `lb-row-live`, `lb-query-row-count`, `lb-query-unplaced-count`, `lb-query-order`, `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested`, `lb-row-unplaced`, `lb-row-leaving`, `lb-row-editing`, `lb-request-pending`, `lb-request-error`, `lb-request-refused`, `lb-page`, `lb-page-title`, and on `<lb-hub>` `lb-load-state`, `lb-server-state`, `lb-error`, `lb-pending`, `lb-as-of` |
|
|
995
984
|
| `lb-*` build-time attributes | 2 | `lb-exp-slot`, `lb-exp-template` |
|
|
996
985
|
| Requests Loadbare provides | 3 | `lb-row-insert`, `lb-row-update`, `lb-row-delete` |
|
|
997
986
|
| Commands for `commandfor` | 4 | `--lb-edit`, `--lb-new`, `--lb-release`, `--lb-place` |
|
|
@@ -999,8 +988,8 @@ cross-reference.
|
|
|
999
988
|
| Attribute namespace for expansion | 1 | `exp-*` |
|
|
1000
989
|
| Reserved tags | 1 | `<lb-hub>` |
|
|
1001
990
|
| Reserved events | 2 | `lb-request`, `lb-request-done` |
|
|
1002
|
-
| Queries the hub serves | 3 | `lb-url`, with columns `lb-path`, `lb-page-label
|
|
1003
|
-
| Builder flags |
|
|
991
|
+
| Queries the hub serves | 3 | `lb-url`, with columns `lb-path`, `lb-page-label`; `lb-choices`, keyed by `lb-path`; `lb-hub-status`, with columns `lb-load-state`, `lb-server-state`, `lb-error`, `lb-pending`, `lb-as-of` |
|
|
992
|
+
| Builder flags | 3 | `--src`, `--out`, `--minify` |
|
|
1004
993
|
| Reserved file names and patterns | 8 | `chrome.html`, `imports.ts`, `*.page.html`, `*.queries.ts`, `*.requests.ts`, `<tag>.html`, `<tag>.browser.ts`, `*.css` |
|
|
1005
994
|
| Reserved `package.json` keys | 1 | `loadbare.widgets` |
|
|
1006
995
|
| Server functions | 8 | `row`, `rows`, `patch`, `url`, `choices`, `refusal`, `createHub`, `hubRoutes` |
|
|
@@ -1412,7 +1401,7 @@ Each equivalent is approximate.
|
|
|
1412
1401
|
| `lb-exp-slot`, `lb-exp-template` | Default slot and named slots; `ng-content` with `select` |
|
|
1413
1402
|
| `lbRowsLanded` | An effect after a list renders |
|
|
1414
1403
|
| `lbAttached`, `lbDetached` | Mount and unmount; `onMounted` and `onUnmounted` (Vue); `onMount` and `onDestroy` (Svelte) |
|
|
1415
|
-
| `order
|
|
1404
|
+
| `order` | `ORDER BY` with report-writer control breaks (`BREAK ON`, group bands) |
|
|
1416
1405
|
| `lb-choices`, `lb-order`, `lb-filter` | A data grid's sort and filter model |
|
|
1417
1406
|
| `ctx` / `HubContext` | `event.locals` (SvelteKit); loader `context` (React Router 7); request-scoped dependency injection |
|
|
1418
1407
|
| `imports.ts` | Installing and registering a component library |
|
package/docs/miswarts.md
CHANGED
|
@@ -68,7 +68,7 @@ gathered.
|
|
|
68
68
|
## Rows hidden by a filter
|
|
69
69
|
|
|
70
70
|
**What.** A row that does not pass the user's filter gets the `hidden`
|
|
71
|
-
attribute
|
|
71
|
+
attribute.
|
|
72
72
|
|
|
73
73
|
**Rule broken.** The hub writes only its own stamps, which always
|
|
74
74
|
begin with `lb-`.
|
|
@@ -78,7 +78,7 @@ over rows already on the page needs no round trip, and `hidden` is HTML's
|
|
|
78
78
|
own word for an element that is present but not shown. A stamp of our
|
|
79
79
|
own would make every stylesheet restate what the browser already does.
|
|
80
80
|
|
|
81
|
-
**Why it terminates.** It applies only to live rows
|
|
81
|
+
**Why it terminates.** It applies only to live rows under a
|
|
82
82
|
filter.
|
|
83
83
|
|
|
84
84
|
## Leaving rows are inert
|
package/docs/reference/css.md
CHANGED
|
@@ -69,7 +69,6 @@ in the stamp, so the selector reads the number and not its text. See
|
|
|
69
69
|
| Select | For |
|
|
70
70
|
|----------------------|-------------------------------------------------|
|
|
71
71
|
| `[lb-row-live]` | Every live row of a list |
|
|
72
|
-
| `[lb-row-selected]` | A row its `lb-select` checkbox ticks |
|
|
73
72
|
| `[lb-row-editing]` | The row a pointed form is editing |
|
|
74
73
|
| `[lb-key-value="…"]` | One row, by its key |
|
|
75
74
|
| `[hidden]` | A row a filter hides; the browser hides it |
|
|
@@ -86,24 +85,12 @@ landed inside a list, such as a total in a table's foot, carries a key and
|
|
|
86
85
|
is not one of the list's rows.
|
|
87
86
|
|
|
88
87
|
```css
|
|
89
|
-
[lb-row-selected] { background: var(--selected); }
|
|
90
88
|
[lb-row-editing] { outline: 2px solid var(--accent); }
|
|
91
89
|
```
|
|
92
90
|
|
|
93
|
-
See [Rows](./data-binding.md#rows)
|
|
91
|
+
See [Rows](./data-binding.md#rows)
|
|
94
92
|
and [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form).
|
|
95
93
|
|
|
96
|
-
### A group
|
|
97
|
-
|
|
98
|
-
Each top-level element of a group carries `lb-group-live`, the column it
|
|
99
|
-
breaks on as `lb-group-column`, and its value there as `lb-group-value`:
|
|
100
|
-
|
|
101
|
-
```css
|
|
102
|
-
[lb-group-column="category"][lb-group-value="Income"] { color: var(--income); }
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
See [Groups](./data-binding.md#groups).
|
|
106
|
-
|
|
107
94
|
### What just happened to a row
|
|
108
95
|
|
|
109
96
|
The hub stamps what its last landing did, so a stylesheet animates with
|
|
@@ -115,7 +102,7 @@ nothing else:
|
|
|
115
102
|
| `[lb-row-changed]` | A row whose values changed |
|
|
116
103
|
| `[lb-row-moved]` | A row whose values changed and now stands elsewhere; it carries `lb-row-changed` too |
|
|
117
104
|
| `[lb-row-requested]` | Any of the three, done by this page's request, as `created`, `changed` or `moved` |
|
|
118
|
-
| `[lb-row-leaving]
|
|
105
|
+
| `[lb-row-leaving]` | A row on its way out; the hub removes it when its animations end |
|
|
119
106
|
|
|
120
107
|
```css
|
|
121
108
|
[lb-row-created] { animation: arrive 200ms; }
|