wizards 0.2.16 → 0.2.18

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 (90) hide show
  1. package/apps/runtime/dist/agents/project-assistant.js +20 -2
  2. package/apps/runtime/dist/authoring/guide.js +6 -3
  3. package/apps/runtime/dist/credits/estimate.js +14 -8
  4. package/apps/runtime/dist/db/migrations/tenant/0008_run_event_note.sql +1 -0
  5. package/apps/runtime/dist/db/migrations/tenant/0009_knowledge.sql +104 -0
  6. package/apps/runtime/dist/db/migrations/tenant/meta/0008_snapshot.json +2019 -0
  7. package/apps/runtime/dist/db/migrations/tenant/meta/0009_snapshot.json +2837 -0
  8. package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +14 -0
  9. package/apps/runtime/dist/db/schema.js +162 -8
  10. package/apps/runtime/dist/engine/events.js +14 -2
  11. package/apps/runtime/dist/engine/prepare.js +5 -2
  12. package/apps/runtime/dist/engine/requirements.js +108 -0
  13. package/apps/runtime/dist/engine/runner.js +2 -2
  14. package/apps/runtime/dist/engine/steps.js +21 -21
  15. package/apps/runtime/dist/engine/template.js +13 -10
  16. package/apps/runtime/dist/env.js +2 -0
  17. package/apps/runtime/dist/files/storage.js +15 -0
  18. package/apps/runtime/dist/models.js +38 -0
  19. package/apps/runtime/dist/plugins/loader.js +2 -0
  20. package/apps/runtime/dist/routes/studio.js +149 -8
  21. package/apps/runtime/dist/services/cloud.js +59 -1
  22. package/apps/runtime/dist/services/knowledge-model.js +558 -0
  23. package/apps/runtime/dist/services/knowledge.js +721 -0
  24. package/apps/runtime/dist/services/marketplace.js +2 -2
  25. package/apps/runtime/dist/services/project-files.js +114 -6
  26. package/apps/runtime/dist/services/project-index.js +632 -94
  27. package/apps/runtime/dist/services/relevance.js +90 -0
  28. package/apps/runtime/dist/services/space-categories.js +882 -0
  29. package/apps/runtime/dist/services/space-data.js +466 -38
  30. package/apps/runtime/dist/services/space-origin.js +164 -0
  31. package/apps/runtime/dist/services/spaces.js +155 -1
  32. package/apps/runtime/dist/tools/browser.js +12 -6
  33. package/apps/runtime/dist/tools/index.js +12 -6
  34. package/apps/runtime/dist/tools/mail.js +5 -2
  35. package/apps/runtime/dist/tools/pages.js +28 -23
  36. package/apps/runtime/dist/tools/project.js +132 -39
  37. package/apps/runtime/dist/tools/store.js +4 -4
  38. package/apps/runtime/dist/widgets/step.js +10 -4
  39. package/apps/web/dist/assets/{Account-BE_W101C.js → Account-Cp1cgqjV.js} +1 -1
  40. package/apps/web/dist/assets/{ChangelogDialog-DjKQJAos.js → ChangelogDialog-Chke1qUe.js} +1 -1
  41. package/apps/web/dist/assets/ChatPanel-DWrwewr6.js +1 -0
  42. package/apps/web/dist/assets/CreditsDialog-Ckema8Ss.js +1 -0
  43. package/apps/web/dist/assets/EditorPage-XX6pfHk5.js +2 -0
  44. package/apps/web/dist/assets/{FlowPreview-DK4RZqTW.js → FlowPreview-DS545n78.js} +1 -1
  45. package/apps/web/dist/assets/{Harness-DcI94f1U.js → Harness-H3yXP-2L.js} +2 -2
  46. package/apps/web/dist/assets/{HomePage-BBNFgryu.js → HomePage-Bds84ShK.js} +1 -1
  47. package/apps/web/dist/assets/{ImportWizard-BoYqovdl.js → ImportWizard-0_NkA8aS.js} +1 -1
  48. package/apps/web/dist/assets/LocalRuntime-DnABzTOe.js +1 -0
  49. package/apps/web/dist/assets/{NewWizardPage-tKV2S4eK.js → NewWizardPage-BqP75V9K.js} +2 -2
  50. package/apps/web/dist/assets/ProjectPage-DbOvwOdZ.js +5 -0
  51. package/apps/web/dist/assets/PublicRunner-CtZ7vxl_.js +1 -0
  52. package/apps/web/dist/assets/RunnerView-xV5bWclu.js +70 -0
  53. package/apps/web/dist/assets/SettingsPage-DKt55zFh.js +1 -0
  54. package/apps/web/dist/assets/{SetupPage-qxHvlf87.js → SetupPage-c6mMD9zN.js} +1 -1
  55. package/apps/web/dist/assets/SharePage-DTKIbMj-.js +1 -0
  56. package/apps/web/dist/assets/{api-t9_w1XNe.js → api-CVG-xDEn.js} +2 -2
  57. package/apps/web/dist/assets/changelog-CXXT0zsp.js +1 -0
  58. package/apps/web/dist/assets/{esm-D0YVMTdo.js → esm-DLM3jdbm.js} +1 -1
  59. package/apps/web/dist/assets/index-1gmaxDSk.js +10 -0
  60. package/apps/web/dist/assets/index-Bwyehu0k.css +1 -0
  61. package/apps/web/dist/assets/{meta-BJHYbMh7.js → meta-m947x6uG.js} +1 -1
  62. package/apps/web/dist/assets/{settings-menu-BqqUAvIY.js → settings-menu-DUY6__C3.js} +1 -1
  63. package/apps/web/dist/assets/ui-BPY2pIh0.js +1 -0
  64. package/apps/web/dist/index.html +4 -4
  65. package/apps/web/dist/mcp/flow.html +54 -54
  66. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +164 -0
  67. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +10 -0
  68. package/node_modules/@engenty-wizards/shared/dist/definition.js +25 -0
  69. package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +199 -0
  70. package/node_modules/@engenty-wizards/shared/dist/knowledge.js +68 -0
  71. package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +1 -16
  72. package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
  73. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +149 -0
  74. package/node_modules/@engenty-wizards/shared/dist/run.js +77 -1
  75. package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +20 -0
  76. package/node_modules/@engenty-wizards/shared/dist/space-data.js +2 -1
  77. package/package.json +1 -1
  78. package/apps/web/dist/assets/ChatPanel-BWrcDs4s.js +0 -1
  79. package/apps/web/dist/assets/CreditsDialog-DUjR5xkl.js +0 -1
  80. package/apps/web/dist/assets/EditorPage-CrOqikP2.js +0 -2
  81. package/apps/web/dist/assets/LocalRuntime-BD8zGN8w.js +0 -1
  82. package/apps/web/dist/assets/ProjectPage-voR0hvzy.js +0 -4
  83. package/apps/web/dist/assets/PublicRunner-CYZE_hSi.js +0 -1
  84. package/apps/web/dist/assets/RunnerView-D_C7hn81.js +0 -70
  85. package/apps/web/dist/assets/SettingsPage-DguoPF_3.js +0 -1
  86. package/apps/web/dist/assets/SharePage-Cf3i7KB-.js +0 -1
  87. package/apps/web/dist/assets/changelog-kz0DmHY4.js +0 -1
  88. package/apps/web/dist/assets/index-BPpKz65x.css +0 -1
  89. package/apps/web/dist/assets/index-D5w3fW1p.js +0 -10
  90. package/apps/web/dist/assets/ui-BHXO_e0e.js +0 -1
@@ -188,6 +188,168 @@ export interface PluginIndex {
188
188
  /** Takes one key out, or without a key every entry of the plugin in the space. */
189
189
  remove(space: string, key?: string): Promise<void>;
190
190
  }
191
+ /** A Kategorie of a space: a typed property set on pages, tables, rows and files of Wissen. */
192
+ export interface PluginCategory {
193
+ name: string;
194
+ type: "choice" | "date" | "number" | "text" | "boolean";
195
+ /** A number's unit: `h`, `€`, `kg`. */
196
+ unit?: string;
197
+ /** A choice: several values per item. */
198
+ multiple?: boolean;
199
+ /** A choice whose values are ranked, in the order of `values`. */
200
+ ordered?: boolean;
201
+ /** Where its values come from, in a few words: "Kopfzeile", "Filter der Website". */
202
+ hint?: string;
203
+ /** A choice's values, known beforehand: a site's filter. Others are added as items bring them. */
204
+ values?: string[];
205
+ /** A column of a table the plugin wrote (by the table's key) whose cells are its values. */
206
+ column?: {
207
+ table: string;
208
+ column: string;
209
+ };
210
+ }
211
+ /**
212
+ * Kategorien of an item, by their names: a choice's value (or a list of them), a day as
213
+ * `YYYY-MM-DD`, a number, a short text, true or false. Null takes one off.
214
+ */
215
+ export type PluginCategoryValues = Record<string, string | number | boolean | (string | number)[] | null>;
216
+ /** The original an item was read from, kept as the evidence beside it. */
217
+ export interface PluginOriginal {
218
+ name: string;
219
+ mime: string;
220
+ data: Uint8Array;
221
+ }
222
+ /** `written`: the plugin's version stands. `kept`: a person changed it, the plugin no longer writes it. */
223
+ export type PluginItemState = "written" | "kept";
224
+ export interface PluginPageInput {
225
+ space: string;
226
+ /** The plugin's own name for the page: the same key again rewrites it, never doubles it. */
227
+ key: string;
228
+ /** What the person knows its source as: "Tagesberichte". */
229
+ label?: string;
230
+ title: string;
231
+ markdown: string;
232
+ /** The key of a page the plugin wrote before, to stand under it as a sub-page. */
233
+ parent?: string;
234
+ position?: number;
235
+ original?: PluginOriginal;
236
+ categories?: PluginCategoryValues;
237
+ /** A model fills the space's Kategorien the page did not get from `categories`. */
238
+ fill?: boolean;
239
+ /** Why a person should look at it ("hardly readable"); shown in Wissen. */
240
+ review?: string | null;
241
+ }
242
+ export interface PluginTableInput {
243
+ space: string;
244
+ key: string;
245
+ label?: string;
246
+ title: string;
247
+ /** Typed columns, as a table of the space has them (`text`, `number`, `date`, `select` …). */
248
+ columns: unknown[];
249
+ /** Cells by column id. They replace the rows the key had. */
250
+ rows: Record<string, unknown>[];
251
+ /** `faq`: a question and its answer per row. */
252
+ format?: "faq";
253
+ original?: PluginOriginal;
254
+ categories?: PluginCategoryValues;
255
+ review?: string | null;
256
+ }
257
+ export interface PluginFileInput {
258
+ space: string;
259
+ key: string;
260
+ label?: string;
261
+ name: string;
262
+ mime: string;
263
+ data: Uint8Array;
264
+ /** What it is, in a sentence: a file that is no page is found by its name and this. */
265
+ description?: string;
266
+ /** What a step reads of it, when there is text. */
267
+ text?: string;
268
+ categories?: PluginCategoryValues;
269
+ }
270
+ export interface PluginDocumentInput {
271
+ space: string;
272
+ key: string;
273
+ label?: string;
274
+ name: string;
275
+ mime: string;
276
+ data: Uint8Array;
277
+ /** Set on what it becomes: its first page, its first table, or the file. */
278
+ categories?: PluginCategoryValues;
279
+ /** A model fills the space's Kategorien it did not get from `categories`. */
280
+ fill?: boolean;
281
+ }
282
+ /** What a document became: pages and tables (the first is the one it is found as), or a file. */
283
+ export interface PluginDocumentResult {
284
+ /** The file, kept as the original. */
285
+ id: string;
286
+ path: string;
287
+ state: PluginItemState;
288
+ items: {
289
+ kind: "page" | "table";
290
+ id: string;
291
+ path: string;
292
+ title: string;
293
+ }[];
294
+ /** Why a person should look at it: hardly readable, or not readable at all. */
295
+ review: string | null;
296
+ }
297
+ /** What a plugin wrote into a space. */
298
+ export interface PluginSpaceItem {
299
+ key: string;
300
+ kind: "page" | "table" | "file";
301
+ id: string;
302
+ title: string;
303
+ /** How a step names it: `pages/…`, `tables/…`, `files/…`. */
304
+ path: string;
305
+ state: PluginItemState;
306
+ review: string | null;
307
+ updatedAt: string;
308
+ }
309
+ /**
310
+ * Wissen of a space, written by a plugin: pages, tables and files under keys of its own, with
311
+ * their Kategorien. Every write is indexed: `project_search` finds it a moment later.
312
+ */
313
+ export interface PluginSpaceData {
314
+ /**
315
+ * Adds Kategorien to the space, or values to ones it has. `proposed`: shown to the person to
316
+ * confirm, never filled before; a later call without it takes them.
317
+ */
318
+ putCategories(input: {
319
+ space: string;
320
+ categories: PluginCategory[];
321
+ proposed?: boolean;
322
+ }): Promise<void>;
323
+ putPage(input: PluginPageInput): Promise<{
324
+ id: string;
325
+ path: string;
326
+ state: PluginItemState;
327
+ }>;
328
+ putTable(input: PluginTableInput): Promise<{
329
+ id: string;
330
+ path: string;
331
+ state: PluginItemState;
332
+ }>;
333
+ putFile(input: PluginFileInput): Promise<{
334
+ id: string;
335
+ path: string;
336
+ }>;
337
+ /**
338
+ * A document read as an upload is: a page (sub-pages when it is long and has headings of its
339
+ * own), a table per sheet of a workbook, or a file when nothing reads it. The document stays as
340
+ * the original. The same key again reads it again, unless a person changed what it became.
341
+ */
342
+ putDocument(input: PluginDocumentInput): Promise<PluginDocumentResult>;
343
+ /** Everything the plugin wrote into the space. */
344
+ list(space: string): Promise<PluginSpaceItem[]>;
345
+ /** Takes out one key, or every item of the plugin in the space; what a person changed stays. */
346
+ remove(space: string, key?: string): Promise<number>;
347
+ /**
348
+ * Writes the Übersicht of values: of one, or of every value of a choice (or of every choice)
349
+ * that has three items or more. Returns how many were written. Costs a model call each.
350
+ */
351
+ summarize(space: string, category?: string, value?: string): Promise<number>;
352
+ }
191
353
  /** What a tool of the space assistant knows about the turn that calls it. */
192
354
  export interface PluginAssistantContext {
193
355
  tenantId: string;
@@ -310,6 +472,8 @@ export interface PluginServerApi {
310
472
  every(name: string, everyMs: number, handler: (tick: PluginTick) => void | Promise<void>): void;
311
473
  /** The space's search index, which `project_search` asks. */
312
474
  index: PluginIndex;
475
+ /** Wissen of a space: pages, tables and files the plugin writes, with their Kategorien. */
476
+ spaceData: PluginSpaceData;
313
477
  web: PluginWeb;
314
478
  documents: PluginDocuments;
315
479
  /**
@@ -1429,5 +1429,15 @@ export interface ItemsTotals {
1429
1429
  }
1430
1430
  export declare function itemsTotals(field: Field, value: unknown, values: Record<string, unknown>): ItemsTotals;
1431
1431
  export declare function allFields(def: WizardDefinition): Field[];
1432
+ /**
1433
+ * The language a wizard speaks to people, read from its own words (titles, labels, intro).
1434
+ * A definition carries none; German where it is not clearly English.
1435
+ */
1436
+ export declare function wizardLang(def: WizardDefinition): "de" | "en";
1437
+ /** The locale amounts and dates are written in for a wizard's language. */
1438
+ export declare const LOCALES: {
1439
+ readonly de: "de-DE";
1440
+ readonly en: "en-GB";
1441
+ };
1432
1442
  /** True when this app's schema reads the definition without losing anything of it. */
1433
1443
  export declare function readable(definition: unknown): boolean;
@@ -796,6 +796,31 @@ function round2(n) {
796
796
  export function allFields(def) {
797
797
  return def.steps.flatMap((s) => (s.type === "page" ? s.fields : []));
798
798
  }
799
+ const GERMAN = /\b(und|der|die|das|ist|nicht|für|mit|ein|eine|wird|zu|dein|deine|ihr|wir|was|wie|oder)\b|[äöüß]/gi;
800
+ const ENGLISH = /\b(and|the|is|not|for|with|a|an|your|you|we|what|how|or|to|of)\b/gi;
801
+ /**
802
+ * The language a wizard speaks to people, read from its own words (titles, labels, intro).
803
+ * A definition carries none; German where it is not clearly English.
804
+ */
805
+ export function wizardLang(def) {
806
+ const text = [
807
+ def.title,
808
+ def.description,
809
+ def.intro,
810
+ ...def.steps.flatMap((s) => [
811
+ s.title,
812
+ s.description,
813
+ ...(s.type === "page" ? s.fields.flatMap((f) => [f.label, f.help, f.placeholder]) : []),
814
+ ]),
815
+ ]
816
+ .filter(Boolean)
817
+ .join(" ");
818
+ const de = text.match(GERMAN)?.length ?? 0;
819
+ const en = text.match(ENGLISH)?.length ?? 0;
820
+ return en > de ? "en" : "de";
821
+ }
822
+ /** The locale amounts and dates are written in for a wizard's language. */
823
+ export const LOCALES = { de: "de-DE", en: "en-GB" };
799
824
  // --- can this app read a definition? ---------------------------------------------------------
800
825
  // The schema drops what it does not know. A definition written for a newer app would be read
801
826
  // without complaint and run wrongly, so it counts only when nothing of it is lost.
@@ -0,0 +1,199 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Wissen: what a space holds for its wizards' steps to look up. Pages (Markdown, with sub-pages),
4
+ * tables (typed columns; FAQ is a format of one) and files that could not be read into either.
5
+ * Kategorien run across all of them: typed properties a step filters on before it searches.
6
+ */
7
+ export declare const CATEGORY_TYPES: readonly ["choice", "date", "number", "text", "boolean"];
8
+ export type CategoryType = (typeof CATEGORY_TYPES)[number];
9
+ /** A value of a choice, how many items have it, and whether it has an Übersicht. */
10
+ export interface CategoryValueInfo {
11
+ id: string;
12
+ value: string;
13
+ count: number;
14
+ summary: boolean;
15
+ }
16
+ export interface SpaceCategory {
17
+ id: string;
18
+ name: string;
19
+ type: CategoryType;
20
+ /** A number's unit: `h`, `€`, `kg`. */
21
+ unit: string | null;
22
+ /** A choice: several values per item. */
23
+ multiple: boolean;
24
+ /** A choice whose values are ranked: their order is the rank. */
25
+ ordered: boolean;
26
+ /** Where its values come from, in a few words. */
27
+ hint: string | null;
28
+ /** The plugin that brought it. */
29
+ origin: string | null;
30
+ /** Proposed, not taken yet: never filled until a person takes it. */
31
+ proposed: boolean;
32
+ /** A column of a table whose cells are its values. */
33
+ tableId: string | null;
34
+ columnId: string | null;
35
+ /** How many items have it. */
36
+ count: number;
37
+ /** A choice's values, in their order. */
38
+ values: CategoryValueInfo[];
39
+ /** A date's or a number's lowest and highest value (a date as `YYYY-MM-DD`). */
40
+ range: {
41
+ min: string | number;
42
+ max: string | number;
43
+ } | null;
44
+ /** Pairs of values that look like one written two ways ("Graz-Süd", "Graz Süd"), by id. */
45
+ alike: [string, string][];
46
+ }
47
+ /** A value as an item has it: a choice's value, a date as `YYYY-MM-DD`, a number, a text, yes/no. */
48
+ export type CategoryCell = string | number | boolean;
49
+ /** What an item has of one Kategorie. */
50
+ export interface ItemCategory {
51
+ categoryId: string;
52
+ /** A choice with several values has several. */
53
+ values: CategoryCell[];
54
+ by: "person" | "origin" | "model";
55
+ }
56
+ /** What a writer sets: by the Kategorie's name; a list for a choice with several values. */
57
+ export type CategoryInput = Record<string, CategoryCell | CategoryCell[] | null>;
58
+ export type KnowledgeKind = "page" | "table" | "file";
59
+ /** An item of Wissen, as lists show it. Sub-pages stand under their page, not in the list. */
60
+ export interface KnowledgeItem {
61
+ /** `p:<id>`, `t:<id>`, `f:<id>`. */
62
+ key: string;
63
+ kind: KnowledgeKind;
64
+ id: string;
65
+ title: string;
66
+ /** How a step names it: `pages/handbuch/montage`, `tables/kettenprogramm`, `files/plan.dwg`. */
67
+ path: string;
68
+ /** A page's sub-pages. */
69
+ children: number;
70
+ /** A table's rows. */
71
+ rows: number | null;
72
+ format: "faq" | null;
73
+ /** What wrote it: a plugin's id and what it calls the source; null when a person did. */
74
+ origin: string | null;
75
+ originLabel: string | null;
76
+ /** A person changed what its origin wrote: the origin no longer writes it. */
77
+ kept: boolean;
78
+ /** Why a person should look at it; null when nothing is in doubt. */
79
+ review: string | null;
80
+ /** The file it was read from. */
81
+ fileId: string | null;
82
+ /** A file still being read, or that could not be read. */
83
+ status: "pending" | "ready" | "failed";
84
+ categories: ItemCategory[];
85
+ updatedAt: string;
86
+ }
87
+ export interface Knowledge {
88
+ items: KnowledgeItem[];
89
+ categories: SpaceCategory[];
90
+ /** The index finds by meaning too, not by keywords alone. */
91
+ embeddings: boolean;
92
+ /** A classifier checks what a search found. */
93
+ checked: boolean;
94
+ }
95
+ /** A value of a choice, opened: its Übersicht and its Inhaltsverzeichnis, the items that have it. */
96
+ export interface KnowledgeValue {
97
+ id: string;
98
+ value: string;
99
+ category: {
100
+ id: string;
101
+ name: string;
102
+ };
103
+ summary: string | null;
104
+ summaryAt: string | null;
105
+ /** Pages (sub-pages too), tables and files that have the value. */
106
+ items: KnowledgeItem[];
107
+ /** Rows of tables that have it, by table. */
108
+ rows: {
109
+ tableId: string;
110
+ title: string;
111
+ count: number;
112
+ }[];
113
+ }
114
+ /** A page with what Wissen knows about it, as the studio opens it. */
115
+ export interface KnowledgePage {
116
+ id: string;
117
+ path: string;
118
+ /** Its ancestors, outermost first. */
119
+ parents: {
120
+ id: string;
121
+ title: string;
122
+ }[];
123
+ children: {
124
+ id: string;
125
+ title: string;
126
+ path: string;
127
+ }[];
128
+ categories: ItemCategory[];
129
+ origin: string | null;
130
+ originLabel: string | null;
131
+ kept: boolean;
132
+ review: string | null;
133
+ file: {
134
+ id: string;
135
+ name: string;
136
+ mime: string;
137
+ } | null;
138
+ }
139
+ /**
140
+ * A filter on one Kategorie: a value (a choice is it, a text equals it, yes/no), one of several,
141
+ * or a range — numbers by `gte`/`lte`, dates by `after`/`before` (`YYYY-MM-DD`, both ends
142
+ * included), a text by `contains`, an ordered choice by `atLeast`.
143
+ */
144
+ export declare const whereValueSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>, z.ZodObject<{
145
+ gte: z.ZodOptional<z.ZodNumber>;
146
+ lte: z.ZodOptional<z.ZodNumber>;
147
+ after: z.ZodOptional<z.ZodString>;
148
+ before: z.ZodOptional<z.ZodString>;
149
+ contains: z.ZodOptional<z.ZodString>;
150
+ atLeast: z.ZodOptional<z.ZodString>;
151
+ }, z.core.$strict>]>;
152
+ export type WhereValue = z.infer<typeof whereValueSchema>;
153
+ /** Filters by the Kategorie's name; an item matches all of them. */
154
+ export declare const whereSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>, z.ZodObject<{
155
+ gte: z.ZodOptional<z.ZodNumber>;
156
+ lte: z.ZodOptional<z.ZodNumber>;
157
+ after: z.ZodOptional<z.ZodString>;
158
+ before: z.ZodOptional<z.ZodString>;
159
+ contains: z.ZodOptional<z.ZodString>;
160
+ atLeast: z.ZodOptional<z.ZodString>;
161
+ }, z.core.$strict>]>>;
162
+ export type Where = z.infer<typeof whereSchema>;
163
+ export interface KnowledgeHit {
164
+ /** `p:<id>`, `t:<id>` (a row of it), `f:<id>`, `v:<id>` (a value's Übersicht), `d:<id>` (a
165
+ * document read before Wissen had pages), `x:<plugin>:<key>` (a plugin's text). */
166
+ key: string;
167
+ kind: KnowledgeKind | "summary" | "document" | "plugin";
168
+ /** What the studio opens for it: `p:`, `t:` (a row's table), `f:`, `v:`; null for a plugin's text. */
169
+ open: string | null;
170
+ path: string;
171
+ title: string;
172
+ /** The Kategorien as `name: value`. */
173
+ categories: string[];
174
+ /** The best section's headings, outermost first, joined by " › ". */
175
+ heading: string | null;
176
+ /** The page of the original it stands on. */
177
+ sheet: number | null;
178
+ /** A row of a table: its id. */
179
+ rowId: string | null;
180
+ text: string;
181
+ /** P(relevant) the classifier gave; null when nothing checked it. */
182
+ relevance: number | null;
183
+ /** Where the studio shows a plugin's text. */
184
+ link: string | null;
185
+ plugin: string | null;
186
+ }
187
+ export interface KnowledgeSearch {
188
+ hits: KnowledgeHit[];
189
+ /** How many units the keywords and the vectors found together, before the classifier. */
190
+ candidates: number;
191
+ /** A classifier judged the candidates; false: they keep the order the index gave them. */
192
+ checked: boolean;
193
+ /** Items the filter left, when there was one. */
194
+ filtered: number | null;
195
+ }
196
+ /** A day as `YYYY-MM-DD` from what a person or a source wrote: ISO, `14.03.2026`, a timestamp. */
197
+ export declare function dayOf(value: unknown): string | null;
198
+ /** A slug for a path: lower case, `-` between words, umlauts spelled out. */
199
+ export declare function slugOf(title: string): string;
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Wissen: what a space holds for its wizards' steps to look up. Pages (Markdown, with sub-pages),
4
+ * tables (typed columns; FAQ is a format of one) and files that could not be read into either.
5
+ * Kategorien run across all of them: typed properties a step filters on before it searches.
6
+ */
7
+ export const CATEGORY_TYPES = ["choice", "date", "number", "text", "boolean"];
8
+ // --- filters --------------------------------------------------------------------
9
+ /**
10
+ * A filter on one Kategorie: a value (a choice is it, a text equals it, yes/no), one of several,
11
+ * or a range — numbers by `gte`/`lte`, dates by `after`/`before` (`YYYY-MM-DD`, both ends
12
+ * included), a text by `contains`, an ordered choice by `atLeast`.
13
+ */
14
+ export const whereValueSchema = z.union([
15
+ z.string().max(200),
16
+ z.number(),
17
+ z.boolean(),
18
+ z.array(z.union([z.string().max(200), z.number()])).max(50),
19
+ z
20
+ .object({
21
+ gte: z.number().optional(),
22
+ lte: z.number().optional(),
23
+ after: z.string().max(40).optional(),
24
+ before: z.string().max(40).optional(),
25
+ contains: z.string().max(200).optional(),
26
+ atLeast: z.string().max(200).optional(),
27
+ })
28
+ .strict(),
29
+ ]);
30
+ /** Filters by the Kategorie's name; an item matches all of them. */
31
+ export const whereSchema = z.record(z.string().max(120), whereValueSchema);
32
+ /** A day as `YYYY-MM-DD` from what a person or a source wrote: ISO, `14.03.2026`, a timestamp. */
33
+ export function dayOf(value) {
34
+ if (typeof value === "number" && Number.isFinite(value)) {
35
+ const d = new Date(value);
36
+ return Number.isNaN(d.getTime()) ? null : d.toISOString().slice(0, 10);
37
+ }
38
+ if (typeof value !== "string") {
39
+ return null;
40
+ }
41
+ const s = value.trim();
42
+ const iso = /^(\d{4})-(\d{2})-(\d{2})/.exec(s);
43
+ if (iso) {
44
+ return `${iso[1]}-${iso[2]}-${iso[3]}`;
45
+ }
46
+ const de = /^(\d{1,2})\.(\d{1,2})\.(\d{2,4})/.exec(s);
47
+ if (de) {
48
+ const year = de[3].length === 2 ? `20${de[3]}` : de[3];
49
+ return `${year}-${de[2].padStart(2, "0")}-${de[1].padStart(2, "0")}`;
50
+ }
51
+ return null;
52
+ }
53
+ /** A slug for a path: lower case, `-` between words, umlauts spelled out. */
54
+ export function slugOf(title) {
55
+ return (title
56
+ .toLowerCase()
57
+ .replace(/ä/g, "ae")
58
+ .replace(/ö/g, "oe")
59
+ .replace(/ü/g, "ue")
60
+ .replace(/ß/g, "ss")
61
+ .replace(/§/g, "p")
62
+ .normalize("NFKD")
63
+ .replace(/[̀-ͯ]/g, "")
64
+ .replace(/[^a-z0-9]+/g, "-")
65
+ .replace(/^-+|-+$/g, "")
66
+ .slice(0, 60)
67
+ .replace(/-+$/, "") || "seite");
68
+ }
@@ -52,7 +52,7 @@ export declare const PROJECT_LIMITS: {
52
52
  readonly files: {
53
53
  readonly logo: 8;
54
54
  readonly asset: 100;
55
- readonly document: 50;
55
+ readonly document: 2000;
56
56
  };
57
57
  readonly bytes: {
58
58
  readonly logo: 5000000;
@@ -63,18 +63,3 @@ export declare const PROJECT_LIMITS: {
63
63
  };
64
64
  /** The largest file of a kind and type a project takes. */
65
65
  export declare function projectFileLimit(kind: ProjectFileKind, mime: string): number;
66
- /** One passage the document index found for a question. */
67
- export interface ProjectSearchHit {
68
- /** The document the passage is of; null for a plugin's text. */
69
- fileId: string | null;
70
- /** The plugin whose text it is; null for a document. */
71
- plugin: string | null;
72
- /** The document's name, or the title of the plugin's text. */
73
- name: string;
74
- /** Where the studio shows a plugin's text. */
75
- link: string | null;
76
- /** The passage's place in its document, from 0. */
77
- index: number;
78
- text: string;
79
- score: number;
80
- }
@@ -84,7 +84,7 @@ export const PROJECT_FILE_KINDS = ["logo", "asset", "document"];
84
84
  export const PROJECT_LIMITS = {
85
85
  facts: 100,
86
86
  colors: 12,
87
- files: { logo: 8, asset: 100, document: 50 },
87
+ files: { logo: 8, asset: 100, document: 2000 },
88
88
  bytes: { logo: 5_000_000, asset: 15_000_000, video: 80_000_000, document: 30_000_000 },
89
89
  };
90
90
  /** The largest file of a kind and type a project takes. */