@protoyard/blueprint 0.0.0-stage → 1.1.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.
Files changed (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1149 -2
  3. package/dist/aggregate.d.ts +83 -0
  4. package/dist/aggregate.js +268 -0
  5. package/dist/app-model.d.ts +76 -0
  6. package/dist/app-model.js +287 -0
  7. package/dist/archetypes/index.d.ts +1 -0
  8. package/dist/archetypes/index.js +1 -0
  9. package/dist/archetypes/schema.d.ts +126 -0
  10. package/dist/archetypes/schema.js +77 -0
  11. package/dist/audit.d.ts +52 -0
  12. package/dist/audit.js +149 -0
  13. package/dist/authoring/deck-lint.d.ts +32 -0
  14. package/dist/authoring/deck-lint.js +567 -0
  15. package/dist/authoring/index.d.ts +1 -0
  16. package/dist/authoring/index.js +1 -0
  17. package/dist/authoring/select-deck-layouts.d.ts +50 -0
  18. package/dist/authoring/select-deck-layouts.js +213 -0
  19. package/dist/authoring-apis.d.ts +13 -0
  20. package/dist/authoring-apis.js +13 -0
  21. package/dist/authoring-validation.d.ts +65 -0
  22. package/dist/authoring-validation.js +267 -0
  23. package/dist/avatar.d.ts +25 -0
  24. package/dist/avatar.js +30 -0
  25. package/dist/brand.d.ts +70 -0
  26. package/dist/brand.js +294 -0
  27. package/dist/brief/archetypes.d.ts +28 -0
  28. package/dist/brief/archetypes.js +798 -0
  29. package/dist/brief/content-synthesize.d.ts +31 -0
  30. package/dist/brief/content-synthesize.js +210 -0
  31. package/dist/brief/deck-outlines.d.ts +96 -0
  32. package/dist/brief/deck-outlines.js +561 -0
  33. package/dist/brief/draft-bodies.d.ts +29 -0
  34. package/dist/brief/draft-bodies.js +224 -0
  35. package/dist/brief/fields.d.ts +19 -0
  36. package/dist/brief/fields.js +229 -0
  37. package/dist/brief/index.d.ts +8 -0
  38. package/dist/brief/index.js +8 -0
  39. package/dist/brief/kind-detection.d.ts +69 -0
  40. package/dist/brief/kind-detection.js +353 -0
  41. package/dist/brief/limits.d.ts +117 -0
  42. package/dist/brief/limits.js +217 -0
  43. package/dist/brief/page-doc-outlines.d.ts +80 -0
  44. package/dist/brief/page-doc-outlines.js +485 -0
  45. package/dist/brief/synthesize.d.ts +59 -0
  46. package/dist/brief/synthesize.js +511 -0
  47. package/dist/brief/text.d.ts +13 -0
  48. package/dist/brief/text.js +59 -0
  49. package/dist/canvas-screen.d.ts +31 -0
  50. package/dist/canvas-screen.js +33 -0
  51. package/dist/canvas-seed.d.ts +58 -0
  52. package/dist/canvas-seed.js +82 -0
  53. package/dist/computed/evaluate.d.ts +11 -0
  54. package/dist/computed/evaluate.js +101 -0
  55. package/dist/computed/index.d.ts +4 -0
  56. package/dist/computed/index.js +3 -0
  57. package/dist/computed/public.d.ts +14 -0
  58. package/dist/computed/public.js +19 -0
  59. package/dist/computed/schema.d.ts +10 -0
  60. package/dist/computed/schema.js +140 -0
  61. package/dist/computed/types.d.ts +70 -0
  62. package/dist/computed/types.js +1 -0
  63. package/dist/computed/validate.d.ts +6 -0
  64. package/dist/computed/validate.js +146 -0
  65. package/dist/csv-guard.d.ts +15 -0
  66. package/dist/csv-guard.js +29 -0
  67. package/dist/csv.d.ts +85 -0
  68. package/dist/csv.js +288 -0
  69. package/dist/files.d.ts +35 -0
  70. package/dist/files.js +246 -0
  71. package/dist/form-schema.d.ts +124 -0
  72. package/dist/form-schema.js +300 -0
  73. package/dist/generator-version.d.ts +11 -0
  74. package/dist/generator-version.js +51 -0
  75. package/dist/generator.d.ts +34 -0
  76. package/dist/generator.js +445 -0
  77. package/dist/identity.d.ts +34 -0
  78. package/dist/identity.js +91 -0
  79. package/dist/import/csv-shape.d.ts +40 -0
  80. package/dist/import/csv-shape.js +265 -0
  81. package/dist/import/json-shape.d.ts +55 -0
  82. package/dist/import/json-shape.js +491 -0
  83. package/dist/import/limits.d.ts +13 -0
  84. package/dist/import/limits.js +14 -0
  85. package/dist/import/sqlite-schema.d.ts +36 -0
  86. package/dist/import/sqlite-schema.js +729 -0
  87. package/dist/index.d.ts +60 -0
  88. package/dist/index.js +58 -0
  89. package/dist/kinds/anchors.d.ts +18 -0
  90. package/dist/kinds/anchors.js +46 -0
  91. package/dist/kinds/bodies.d.ts +6 -0
  92. package/dist/kinds/bodies.js +32 -0
  93. package/dist/kinds/brand-runtime.d.ts +19 -0
  94. package/dist/kinds/brand-runtime.js +35 -0
  95. package/dist/kinds/deck.d.ts +8 -0
  96. package/dist/kinds/deck.js +34 -0
  97. package/dist/kinds/doc.d.ts +19 -0
  98. package/dist/kinds/doc.js +41 -0
  99. package/dist/kinds/embed/index.d.ts +39 -0
  100. package/dist/kinds/embed/index.js +91 -0
  101. package/dist/kinds/envelope.d.ts +159 -0
  102. package/dist/kinds/envelope.js +78 -0
  103. package/dist/kinds/index.d.ts +11 -0
  104. package/dist/kinds/index.js +11 -0
  105. package/dist/kinds/kind.d.ts +36 -0
  106. package/dist/kinds/kind.js +55 -0
  107. package/dist/kinds/limits.d.ts +64 -0
  108. package/dist/kinds/limits.js +190 -0
  109. package/dist/kinds/page-doc-runtime.d.ts +37 -0
  110. package/dist/kinds/page-doc-runtime.js +55 -0
  111. package/dist/kinds/page.d.ts +16 -0
  112. package/dist/kinds/page.js +67 -0
  113. package/dist/kinds/parse.d.ts +18 -0
  114. package/dist/kinds/parse.js +27 -0
  115. package/dist/kinds/runtime.d.ts +128 -0
  116. package/dist/kinds/runtime.js +206 -0
  117. package/dist/kinds/services.d.ts +49 -0
  118. package/dist/kinds/services.js +249 -0
  119. package/dist/kinds/testing.d.ts +9 -0
  120. package/dist/kinds/testing.js +16 -0
  121. package/dist/migrations.d.ts +97 -0
  122. package/dist/migrations.js +351 -0
  123. package/dist/motion.d.ts +50 -0
  124. package/dist/motion.js +52 -0
  125. package/dist/notifications.d.ts +19 -0
  126. package/dist/notifications.js +177 -0
  127. package/dist/patch.d.ts +122 -0
  128. package/dist/patch.js +322 -0
  129. package/dist/policy.d.ts +23 -0
  130. package/dist/policy.js +105 -0
  131. package/dist/runtime-definition.d.ts +39 -0
  132. package/dist/runtime-definition.js +74 -0
  133. package/dist/scenarios.d.ts +9 -0
  134. package/dist/scenarios.js +49 -0
  135. package/dist/schema/blueprint.schema.json +10191 -0
  136. package/dist/schema-json.d.ts +6 -0
  137. package/dist/schema-json.js +36 -0
  138. package/dist/schema.d.ts +6348 -0
  139. package/dist/schema.js +1269 -0
  140. package/dist/search.d.ts +16 -0
  141. package/dist/search.js +96 -0
  142. package/dist/services.d.ts +89 -0
  143. package/dist/services.js +898 -0
  144. package/dist/share.d.ts +78 -0
  145. package/dist/share.js +234 -0
  146. package/dist/sheet-screen.d.ts +49 -0
  147. package/dist/sheet-screen.js +91 -0
  148. package/dist/storage.d.ts +43 -0
  149. package/dist/storage.js +179 -0
  150. package/dist/stream.d.ts +26 -0
  151. package/dist/stream.js +173 -0
  152. package/dist/untrusted.d.ts +63 -0
  153. package/dist/untrusted.js +502 -0
  154. package/dist/validation-tasks.d.ts +178 -0
  155. package/dist/validation-tasks.js +199 -0
  156. package/dist/validation.d.ts +35 -0
  157. package/dist/validation.js +312 -0
  158. package/dist/variants.d.ts +112 -0
  159. package/dist/variants.js +283 -0
  160. package/dist/workflows/index.d.ts +3 -0
  161. package/dist/workflows/index.js +3 -0
  162. package/dist/workflows/schema.d.ts +50 -0
  163. package/dist/workflows/schema.js +34 -0
  164. package/dist/workflows/transition.d.ts +15 -0
  165. package/dist/workflows/transition.js +25 -0
  166. package/dist/workflows/validate.d.ts +17 -0
  167. package/dist/workflows/validate.js +140 -0
  168. package/package.json +120 -3
@@ -0,0 +1,16 @@
1
+ import type { PrototypeRuntime } from '@protoyard/core';
2
+ import type { Blueprint } from './schema.ts';
3
+ export interface EntitySearchMatch {
4
+ entityName: string;
5
+ entityLabel: string;
6
+ recordId: string;
7
+ title: string;
8
+ matchedFields: string[];
9
+ titleMatch: boolean;
10
+ }
11
+ export interface SearchEntitiesOptions {
12
+ /** Maximum number of results returned for each readable entity. Defaults to 10. */
13
+ limitPerEntity?: number;
14
+ }
15
+ /** Searches every readable entity using its title field and declared searchable fields. */
16
+ export declare function searchEntities(runtime: PrototypeRuntime, blueprint: Blueprint, query: string, options?: SearchEntitiesOptions): Promise<EntitySearchMatch[]>;
package/dist/search.js ADDED
@@ -0,0 +1,96 @@
1
+ import { createEntityServices } from "./services.js";
2
+ import { inputProperty } from "./validation.js";
3
+ function normalize(value) {
4
+ return value.normalize('NFKD').replace(/\p{M}/gu, '').toLowerCase();
5
+ }
6
+ function searchableText(value) {
7
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
8
+ return String(value);
9
+ }
10
+ if (Array.isArray(value)) {
11
+ return value
12
+ .filter((item) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean')
13
+ .map(String)
14
+ .join(' ');
15
+ }
16
+ return '';
17
+ }
18
+ function entityLabel(entity) {
19
+ return (entity.label ??
20
+ entity.name.replace(/-/g, ' ').replace(/\b\w/g, (letter) => letter.toUpperCase()));
21
+ }
22
+ function fieldMap(entity) {
23
+ return new Map(entity.fields.map((field) => [field.name, field]));
24
+ }
25
+ function compareMatches(left, right) {
26
+ return (Number(right.titleMatch) - Number(left.titleMatch) ||
27
+ Number(right.exactTitleMatch) - Number(left.exactTitleMatch) ||
28
+ Number(right.titlePrefixMatch) - Number(left.titlePrefixMatch) ||
29
+ (left.entityName < right.entityName ? -1 : left.entityName > right.entityName ? 1 : 0) ||
30
+ (left.normalizedTitle < right.normalizedTitle
31
+ ? -1
32
+ : left.normalizedTitle > right.normalizedTitle
33
+ ? 1
34
+ : 0) ||
35
+ (left.recordId < right.recordId ? -1 : left.recordId > right.recordId ? 1 : 0));
36
+ }
37
+ /** Searches every readable entity using its title field and declared searchable fields. */
38
+ export async function searchEntities(runtime, blueprint, query, options = {}) {
39
+ const normalizedQuery = normalize(query.trim());
40
+ if (!normalizedQuery)
41
+ return [];
42
+ const requestedLimit = options.limitPerEntity ?? 10;
43
+ const limitPerEntity = Number.isFinite(requestedLimit)
44
+ ? Math.max(0, Math.floor(requestedLimit))
45
+ : 10;
46
+ if (limitPerEntity === 0)
47
+ return [];
48
+ const services = createEntityServices(runtime, blueprint);
49
+ const entityMatches = await Promise.all(blueprint.entities.map(async (entity) => {
50
+ const capability = `${entity.name}:read`;
51
+ if (!runtime.session.can(capability))
52
+ return [];
53
+ const service = services[entity.name];
54
+ if (!service)
55
+ return [];
56
+ const rows = await service.list();
57
+ const hasRecordReadRule = Boolean(runtime.policy.definition.rules?.[capability]);
58
+ const fields = fieldMap(entity);
59
+ const searchableNames = [...new Set([entity.titleField, ...entity.searchable])];
60
+ const titleField = fields.get(entity.titleField);
61
+ const matches = [];
62
+ for (const row of rows) {
63
+ if (hasRecordReadRule && !runtime.session.can(capability, row))
64
+ continue;
65
+ const title = searchableText(titleField ? row[inputProperty(titleField)] : '').trim();
66
+ const normalizedTitle = normalize(title);
67
+ const matchedFields = [];
68
+ for (const fieldName of searchableNames) {
69
+ const field = fields.get(fieldName);
70
+ if (!field)
71
+ continue;
72
+ const value = normalize(searchableText(row[inputProperty(field)]));
73
+ if (value.includes(normalizedQuery))
74
+ matchedFields.push(fieldName);
75
+ }
76
+ if (matchedFields.length === 0)
77
+ continue;
78
+ matches.push({
79
+ entityName: entity.name,
80
+ entityLabel: entityLabel(entity),
81
+ recordId: row.id,
82
+ title: title || row.id,
83
+ matchedFields,
84
+ titleMatch: matchedFields.includes(entity.titleField),
85
+ normalizedTitle,
86
+ exactTitleMatch: normalizedTitle === normalizedQuery,
87
+ titlePrefixMatch: normalizedTitle.startsWith(normalizedQuery),
88
+ });
89
+ }
90
+ return matches.sort(compareMatches).slice(0, limitPerEntity);
91
+ }));
92
+ return entityMatches
93
+ .flat()
94
+ .sort(compareMatches)
95
+ .map(({ normalizedTitle: _normalizedTitle, exactTitleMatch: _exactTitleMatch, titlePrefixMatch: _titlePrefixMatch, ...match }) => match);
96
+ }
@@ -0,0 +1,89 @@
1
+ import { type PrototypeRuntime, type Row } from '@protoyard/core';
2
+ import { type CsvParseOptions, type CsvSerializeOptions, type CsvValidationResult } from './csv.ts';
3
+ import { type Blueprint, type BlueprintEntity } from './schema.ts';
4
+ import { type EntityInput } from './validation.ts';
5
+ /** Thrown when a record does not exist (or was removed meanwhile). */
6
+ export declare class EntityNotFoundError extends Error {
7
+ readonly code: "not-found";
8
+ constructor(entity: string, id: string);
9
+ }
10
+ /** Thrown when `onDelete: 'restrict'` blocks a removal; nothing was deleted. */
11
+ export declare class EntityInUseError extends Error {
12
+ readonly code: "in-use";
13
+ constructor(entity: string, references: Record<string, number>);
14
+ }
15
+ export interface EntityService {
16
+ readonly entity: BlueprintEntity;
17
+ readonly table: string;
18
+ /** Rows sorted by `defaultSort` (else `createdAt`); soft-deleted rows are hidden. */
19
+ list(): Promise<Row[]>;
20
+ /** Soft-deleted rows sorted by the entity default sort; empty when soft delete is disabled. */
21
+ trash(): Promise<Row[]>;
22
+ get(id: string): Promise<Row | undefined>;
23
+ create(input: EntityInput): Promise<Row>;
24
+ update(id: string, patch: EntityInput): Promise<Row>;
25
+ /** Returns an undo token for soft-delete entities; hard-delete entities return undefined. */
26
+ remove(id: string): Promise<string | undefined>;
27
+ /** Restores the complete row set affected by a soft remove before its simulated deadline. */
28
+ restore(undoToken: string): Promise<number>;
29
+ /** Restores a trashed row and the soft-delete cascade recorded for it. */
30
+ restoreTrashed(id: string): Promise<number>;
31
+ /** Permanently removes a trashed row and its cascade dependents. */
32
+ purge(id: string): Promise<number>;
33
+ /**
34
+ * Gives the record the next face for one avatar field (default: the first)
35
+ * and returns the saved row. Same contract as `update` (`<entity>:update` or
36
+ * `update:own`, operation `<entity>.shuffleAvatar`). Deterministic: the next
37
+ * face depends only on the record id, the field and the current face (see
38
+ * `nextAvatar`), so repeated shuffles visit every face before any repeats.
39
+ */
40
+ shuffleAvatar(id: string, field?: string): Promise<Row>;
41
+ bulkUpdate(ids: readonly string[], patch: EntityInput): Promise<Row[]>;
42
+ /** Removes every id (with `onDelete` effects) and returns how many rows were deleted. */
43
+ bulkRemove(ids: readonly string[]): Promise<number>;
44
+ /**
45
+ * Visible rows as CSV text (headers = field labels). Gated by `<entity>:export`.
46
+ * Optional filter narrows the listed set; relation cells are the stored ids.
47
+ */
48
+ exportCsv(filter?: ExportCsvFilter, options?: CsvSerializeOptions): Promise<string>;
49
+ /**
50
+ * CSV import: `mode: 'preview'` (default) validates only; `mode: 'commit'` writes
51
+ * in one transaction (chunks of {@link BULK_CHUNK}). Gated by `<entity>:import`.
52
+ * Relation cells accept a target id or its title. Without `skipInvalid`, any
53
+ * invalid row aborts the commit and writes nothing.
54
+ */
55
+ importCsv(text: string, options?: ImportCsvOptions): Promise<ImportCsvResult>;
56
+ }
57
+ /** Optional narrow of `list()` before export. */
58
+ export interface ExportCsvFilter {
59
+ ids?: readonly string[];
60
+ where?: (row: Row) => boolean;
61
+ }
62
+ export interface ImportCsvOptions extends CsvParseOptions {
63
+ /** `preview` validates only; `commit` writes (default `preview`). */
64
+ mode?: 'preview' | 'commit';
65
+ /**
66
+ * On commit: write only valid rows when true; when false (default), any
67
+ * invalid row fails the whole import and nothing is written.
68
+ */
69
+ skipInvalid?: boolean;
70
+ serialize?: CsvSerializeOptions;
71
+ }
72
+ export interface ImportCsvResult extends CsvValidationResult {
73
+ mode: 'preview' | 'commit';
74
+ /** Rows written on a successful commit (empty on preview). */
75
+ created: Row[];
76
+ /** Audit entry ids written for this batch (when `features.audit` is on). */
77
+ auditIds: string[];
78
+ }
79
+ /** Thrown when commit is requested and invalid rows remain without `skipInvalid`. */
80
+ export declare class ImportCsvError extends Error {
81
+ readonly code: "import-invalid";
82
+ readonly report: ImportCsvResult;
83
+ constructor(report: ImportCsvResult);
84
+ }
85
+ export type EntityServices = Record<string, EntityService>;
86
+ /** Writes larger than this go through several `bulkPut` calls inside the same transaction. */
87
+ export declare const BULK_CHUNK = 1000;
88
+ export declare const UNDO_WINDOW_MS = 30000;
89
+ export declare function createEntityServices(runtime: PrototypeRuntime, blueprint: Blueprint): EntityServices;