@sanoma-ai/workflows 0.2.0 → 0.4.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 (144) hide show
  1. package/README.md +121 -62
  2. package/dist/approvals.d.ts +11 -6
  3. package/dist/approvals.js +38 -10
  4. package/dist/approvals.js.map +1 -1
  5. package/dist/call-workflow.d.ts +16 -0
  6. package/dist/call-workflow.js +35 -0
  7. package/dist/call-workflow.js.map +1 -0
  8. package/dist/call.d.ts +33 -4
  9. package/dist/call.js +107 -42
  10. package/dist/call.js.map +1 -1
  11. package/dist/cedar.d.ts +87 -0
  12. package/dist/cedar.js +365 -0
  13. package/dist/cedar.js.map +1 -0
  14. package/dist/client.d.ts +85 -7
  15. package/dist/client.js +289 -25
  16. package/dist/client.js.map +1 -1
  17. package/dist/collection.d.ts +107 -0
  18. package/dist/collection.js +306 -0
  19. package/dist/collection.js.map +1 -0
  20. package/dist/config.d.ts +42 -14
  21. package/dist/config.js +82 -18
  22. package/dist/config.js.map +1 -1
  23. package/dist/constructors.d.ts +2 -0
  24. package/dist/constructors.js +5 -0
  25. package/dist/constructors.js.map +1 -0
  26. package/dist/data.d.ts +2 -0
  27. package/dist/data.js +3 -0
  28. package/dist/data.js.map +1 -0
  29. package/dist/datafile.d.ts +28 -2
  30. package/dist/datafile.js +118 -19
  31. package/dist/datafile.js.map +1 -1
  32. package/dist/db.d.ts +5 -3
  33. package/dist/db.js +88 -21
  34. package/dist/db.js.map +1 -1
  35. package/dist/define.d.ts +10 -4
  36. package/dist/define.js +1 -5
  37. package/dist/define.js.map +1 -1
  38. package/dist/describe.d.ts +45 -5
  39. package/dist/describe.js +72 -7
  40. package/dist/describe.js.map +1 -1
  41. package/dist/direct.d.ts +9 -0
  42. package/dist/direct.js +11 -0
  43. package/dist/direct.js.map +1 -0
  44. package/dist/errors.d.ts +16 -1
  45. package/dist/errors.js +30 -1
  46. package/dist/errors.js.map +1 -1
  47. package/dist/expectations.d.ts +3 -3
  48. package/dist/expectations.js +16 -7
  49. package/dist/expectations.js.map +1 -1
  50. package/dist/index.d.ts +8 -4
  51. package/dist/index.js +10 -4
  52. package/dist/index.js.map +1 -1
  53. package/dist/ledger.d.ts +12 -1
  54. package/dist/ledger.js +18 -11
  55. package/dist/ledger.js.map +1 -1
  56. package/dist/lint.d.ts +5 -4
  57. package/dist/lint.js +24 -7
  58. package/dist/lint.js.map +1 -1
  59. package/dist/op.d.ts +6 -0
  60. package/dist/op.js.map +1 -1
  61. package/dist/outline.d.ts +2 -0
  62. package/dist/outline.js +1 -1
  63. package/dist/outline.js.map +1 -1
  64. package/dist/paths.d.ts +4 -0
  65. package/dist/paths.js +17 -3
  66. package/dist/paths.js.map +1 -1
  67. package/dist/person.d.ts +61 -0
  68. package/dist/person.js +69 -0
  69. package/dist/person.js.map +1 -0
  70. package/dist/plugin.js +1 -1
  71. package/dist/plugin.js.map +1 -1
  72. package/dist/policy.d.ts +51 -55
  73. package/dist/policy.js +14 -36
  74. package/dist/policy.js.map +1 -1
  75. package/dist/resource.d.ts +21 -5
  76. package/dist/resource.js +12 -3
  77. package/dist/resource.js.map +1 -1
  78. package/dist/resources.d.ts +58 -6
  79. package/dist/resources.js +119 -26
  80. package/dist/resources.js.map +1 -1
  81. package/dist/run.d.ts +17 -1
  82. package/dist/run.js.map +1 -1
  83. package/dist/scenario.d.ts +16 -9
  84. package/dist/scenario.js +93 -20
  85. package/dist/scenario.js.map +1 -1
  86. package/dist/scenarios.d.ts +10 -6
  87. package/dist/scenarios.js +8 -4
  88. package/dist/scenarios.js.map +1 -1
  89. package/dist/schema.d.ts +32 -0
  90. package/dist/schema.js +225 -0
  91. package/dist/schema.js.map +1 -0
  92. package/dist/sdk.d.ts +34 -0
  93. package/dist/sdk.js +168 -0
  94. package/dist/sdk.js.map +1 -0
  95. package/dist/shared.d.ts +181 -5
  96. package/dist/shared.js +160 -7
  97. package/dist/shared.js.map +1 -1
  98. package/dist/sites.d.ts +52 -0
  99. package/dist/sites.js +174 -0
  100. package/dist/sites.js.map +1 -0
  101. package/dist/version.d.ts +3 -2
  102. package/dist/version.js +3 -2
  103. package/dist/version.js.map +1 -1
  104. package/dist/worker.d.ts +3 -2
  105. package/dist/worker.js +24 -4
  106. package/dist/worker.js.map +1 -1
  107. package/oxlint.json +5 -16
  108. package/package.json +8 -3
  109. package/src/approvals.ts +49 -13
  110. package/src/call-workflow.ts +44 -0
  111. package/src/call.ts +157 -45
  112. package/src/cedar.ts +493 -0
  113. package/src/client.ts +368 -37
  114. package/src/collection.ts +431 -0
  115. package/src/config.ts +131 -34
  116. package/src/constructors.ts +4 -0
  117. package/src/data.ts +10 -0
  118. package/src/datafile.ts +141 -21
  119. package/src/db.ts +90 -22
  120. package/src/define.ts +11 -9
  121. package/src/describe.ts +113 -10
  122. package/src/direct.ts +15 -0
  123. package/src/errors.ts +36 -1
  124. package/src/expectations.ts +17 -8
  125. package/src/index.ts +33 -2
  126. package/src/ledger.ts +35 -17
  127. package/src/lint.ts +29 -12
  128. package/src/op.ts +6 -0
  129. package/src/outline.ts +1 -1
  130. package/src/paths.ts +18 -3
  131. package/src/person.ts +75 -0
  132. package/src/plugin.ts +1 -1
  133. package/src/policy.ts +59 -63
  134. package/src/resource.ts +35 -8
  135. package/src/resources.ts +165 -24
  136. package/src/run.ts +18 -1
  137. package/src/scenario.ts +128 -33
  138. package/src/scenarios.ts +15 -8
  139. package/src/schema.ts +261 -0
  140. package/src/sdk.ts +211 -0
  141. package/src/shared.ts +271 -8
  142. package/src/sites.ts +220 -0
  143. package/src/version.ts +3 -2
  144. package/src/worker.ts +29 -6
@@ -0,0 +1,107 @@
1
+ import { z } from "zod";
2
+ import { type Db } from "./db.ts";
3
+ import { type Fake } from "./fake.ts";
4
+ import { type Connector, type Driver, type Op, type OpSpec } from "./op.ts";
5
+ import { DATA_VENDOR, type FIELD_TYPES, type ListQuery, type Saved } from "./shared.ts";
6
+ export type FieldType = (typeof FIELD_TYPES)[number];
7
+ /** A field of a collection: its type, the type marked optional, or one of a list of strings. */
8
+ export type Field = FieldType | {
9
+ type: FieldType;
10
+ optional?: true;
11
+ } | {
12
+ enum: readonly [string, ...string[]];
13
+ optional?: true;
14
+ };
15
+ type ValueOfType<T> = T extends "number" ? number : T extends "boolean" ? boolean : string;
16
+ type ValueOf<F> = F extends {
17
+ enum: readonly (infer E)[];
18
+ } ? E : F extends {
19
+ type: infer T;
20
+ } ? ValueOfType<T> : ValueOfType<F>;
21
+ type OptionalKeys<F> = {
22
+ [K in keyof F]: F[K] extends {
23
+ optional: true;
24
+ } ? K : never;
25
+ }[keyof F];
26
+ type Flat<T> = {
27
+ [K in keyof T]: T[K];
28
+ };
29
+ /** A record of a collection with these fields: text, a person and a date are strings, an enum one of its strings. */
30
+ export type RecordOf<F extends Record<string, Field>> = Flat<{
31
+ -readonly [K in Exclude<keyof F & string, OptionalKeys<F>>]: ValueOf<F[K]>;
32
+ } & {
33
+ -readonly [K in OptionalKeys<F> & string]?: ValueOf<F[K]>;
34
+ }>;
35
+ /** The fields that may be a collection's key: required text. */
36
+ export type RequiredTextKeys<F extends Record<string, Field>> = {
37
+ [K in keyof F & string]: F[K] extends "text" | {
38
+ type: "text";
39
+ optional?: undefined;
40
+ } ? K : never;
41
+ }[keyof F & string];
42
+ /**
43
+ * Where a collection keeps its brand: a registered symbol, so a copy of this package reads a
44
+ * collection another copy made, as `VENDOR` and `RESOURCE`.
45
+ */
46
+ export declare const COLLECTION: unique symbol;
47
+ /** A collection, as `collection()` declares it, with its four operations. */
48
+ export interface Collection<N extends string = string, F extends Record<string, Field> = Record<string, Field>, K extends string = string> {
49
+ readonly [COLLECTION]: true;
50
+ readonly kind: "collection";
51
+ readonly name: N;
52
+ readonly key: K;
53
+ readonly fields: F;
54
+ readonly title?: string;
55
+ readonly get: Op<typeof DATA_VENDOR, N, "get", {
56
+ key: string;
57
+ }, Saved<RecordOf<F>> | null>;
58
+ readonly list: Op<typeof DATA_VENDOR, N, "list", ListQuery<RecordOf<F>>, Saved<RecordOf<F>>[]>;
59
+ readonly save: Op<typeof DATA_VENDOR, N, "save", {
60
+ record: RecordOf<F>;
61
+ }, Saved<RecordOf<F>>>;
62
+ readonly delete: Op<typeof DATA_VENDOR, N, "delete", {
63
+ key: string;
64
+ }, {
65
+ deleted: boolean;
66
+ }>;
67
+ }
68
+ /**
69
+ * Declares a collection of records, in `resources/data.ts`:
70
+ * `collection({ name: "outbound", key: "company", fields: { company: "text", stage: { enum: ["new", "won"] } } })`.
71
+ * Its operations are `data.<name>.get`, `.list`, `.save` and `.delete`, which a workflow names in
72
+ * `uses` and calls on `ctx.data.<name>`. The name is a lowercase letter then letters and digits;
73
+ * the key is a required text field, each record's own and unique.
74
+ */
75
+ export declare function collection<const N extends string, const F extends Record<string, Field>, const K extends RequiredTextKeys<F>>(declaration: {
76
+ name: N;
77
+ key: K;
78
+ fields: F;
79
+ title?: string;
80
+ }): Collection<N, F, K>;
81
+ /** True for a collection `collection()` made, in this copy of the package or another: read by its brand. */
82
+ export declare const isCollection: (x: unknown) => x is Collection;
83
+ /** The schema a collection's records are saved under: its fields, and nothing else. */
84
+ export declare const recordSchemaOf: (c: Collection) => z.ZodType;
85
+ /** Where the record no longer fits the collection's fields, as `Saved.issues` says it; none when it fits. */
86
+ export declare function recordIssues(c: Collection, record: unknown): NonNullable<Saved["issues"]>;
87
+ type Stored = Omit<Saved, "issues">;
88
+ type DataOps = Record<string, Record<string, OpSpec>>;
89
+ /** The vendor `data` as a connector of the collections' operations, the very ones each `collection()` made. */
90
+ export declare function collectionsConnector(list: readonly Collection[]): Connector<typeof DATA_VENDOR, DataOps>;
91
+ /**
92
+ * The collections' driver: their records in `DATA_TABLE` of the database `store` is on, through
93
+ * the pool a run's calls share, or one connection in a transaction (a direct write, with its
94
+ * ledger record). Every value reaches Postgres as a parameter, field names included; the field
95
+ * names and operators are the ones the input's schema accepts.
96
+ */
97
+ export declare function collectionsDriver(list: readonly Collection[], store: {
98
+ db(): Promise<Db>;
99
+ }): Driver;
100
+ /** The collections' records in memory, by collection and key: what a sandbox run's fake holds. */
101
+ export type CollectionsState = Record<string, Record<string, Stored>>;
102
+ /**
103
+ * An in-memory fake of the collections' driver, which starts empty: a scenario seeds it
104
+ * (`Given data.<name> "<key>" has`), and a test may through `update`.
105
+ */
106
+ export declare function collectionsFake(list: readonly Collection[]): Fake<CollectionsState>;
107
+ export {};
@@ -0,0 +1,306 @@
1
+ import { z } from "zod";
2
+ import { DATA_TABLE } from "./db.js";
3
+ import { defineFake } from "./fake.js";
4
+ import { isObject } from "./resource.js";
5
+ import { defineConnector, defineDriver, } from "./op.js";
6
+ import { checkCollection, DATA_VENDOR, LIST_LIMIT } from "./shared.js";
7
+ /**
8
+ * Where a collection keeps its brand: a registered symbol, so a copy of this package reads a
9
+ * collection another copy made, as `VENDOR` and `RESOURCE`.
10
+ */
11
+ export const COLLECTION = Symbol.for("sanoma.collection");
12
+ /**
13
+ * Declares a collection of records, in `resources/data.ts`:
14
+ * `collection({ name: "outbound", key: "company", fields: { company: "text", stage: { enum: ["new", "won"] } } })`.
15
+ * Its operations are `data.<name>.get`, `.list`, `.save` and `.delete`, which a workflow names in
16
+ * `uses` and calls on `ctx.data.<name>`. The name is a lowercase letter then letters and digits;
17
+ * the key is a required text field, each record's own and unique.
18
+ */
19
+ export function collection(declaration) {
20
+ checkCollection(declaration);
21
+ const { name, key, fields, title } = declaration;
22
+ const record = z.strictObject(Object.fromEntries(Object.entries(fields).map(([field, kind]) => {
23
+ const value = field === key ? z.string().min(1) : valueSchema(kind);
24
+ return [field, isObject(kind) && kind.optional ? value.optional() : value];
25
+ })));
26
+ const ops = defineConnector(DATA_VENDOR, { [name]: specsOf(name, key, fields, record) })[name];
27
+ return Object.freeze({
28
+ [COLLECTION]: true,
29
+ kind: "collection",
30
+ name,
31
+ key,
32
+ fields,
33
+ ...(title !== undefined && { title }),
34
+ ...ops,
35
+ });
36
+ }
37
+ /**
38
+ * A date or a date and time with its offset, as ISO 8601 writes them, marked `format: "date"` in
39
+ * its JSON Schema (beside the union's own), which a form reads to draw a date's input.
40
+ */
41
+ const DATE = z.union([z.iso.date(), z.iso.datetime({ offset: true })]).meta({ format: "date" });
42
+ /**
43
+ * A person's id: text that is not empty, marked `format: "person"` in its JSON Schema, which a form
44
+ * reads to draw a person's input.
45
+ */
46
+ const PERSON = z.string().min(1).meta({ format: "person" });
47
+ /** The schema of a field's value, required. */
48
+ function valueSchema(kind) {
49
+ if (typeof kind === "object" && "enum" in kind)
50
+ return z.enum(kind.enum);
51
+ const type = typeof kind === "string" ? kind : kind.type;
52
+ return { text: z.string(), person: PERSON, date: DATE, number: z.number(), boolean: z.boolean() }[type];
53
+ }
54
+ /**
55
+ * What `get`, `list` and `save` answer: the record as it is stored, which an older declaration
56
+ * may have accepted, so it is not checked here; `issues` say where the fields refuse it.
57
+ */
58
+ const SAVED = z.object({
59
+ key: z.string(),
60
+ record: z.record(z.string(), z.unknown()),
61
+ updatedAt: z.string(),
62
+ updatedBy: z.string(),
63
+ issues: z.array(z.object({ path: z.array(z.union([z.string(), z.number()])), message: z.string() })).optional(),
64
+ });
65
+ /** A collection's four operations, for `defineConnector`. */
66
+ function specsOf(name, key, fields, record) {
67
+ const at = (k) => `${DATA_VENDOR}:${name}/${k}`;
68
+ const byKey = z.object({ key: z.string().min(1) });
69
+ const names = Object.keys(fields);
70
+ const where = z.strictObject(Object.fromEntries(Object.entries(fields).map(([field, kind]) => {
71
+ const value = valueSchema(kind);
72
+ const range = z.strictObject({ gt: value, gte: value, lt: value, lte: value }).partial();
73
+ return [field, z.union([value, range]).optional()];
74
+ })));
75
+ return {
76
+ get: {
77
+ effect: "read",
78
+ idempotent: true,
79
+ description: `Get a record of ${name} by its ${key}`,
80
+ input: byKey,
81
+ output: SAVED.nullable(),
82
+ target: ({ key: k }) => at(k),
83
+ },
84
+ list: {
85
+ effect: "read",
86
+ idempotent: true,
87
+ description: `List records of ${name}: fields equal to a value or within a range, sorted, at most \`limit\``,
88
+ input: z.object({
89
+ where: where.optional(),
90
+ order: z.enum([...names, ...names.map((n) => `-${n}`)]).optional(),
91
+ limit: z.number().int().min(1).max(LIST_LIMIT.max).default(LIST_LIMIT.default),
92
+ }),
93
+ output: z.array(SAVED),
94
+ target: () => `${DATA_VENDOR}:${name}`,
95
+ },
96
+ save: {
97
+ effect: "write",
98
+ idempotent: true,
99
+ description: `Save a record of ${name}: added, or replacing the one with its ${key}`,
100
+ input: z.object({ record }),
101
+ output: SAVED,
102
+ target: ({ record: r }) => at(r[key]),
103
+ },
104
+ delete: {
105
+ effect: "write",
106
+ idempotent: true,
107
+ description: `Delete the record of ${name} with this ${key}`,
108
+ input: byKey,
109
+ output: z.object({ deleted: z.boolean() }),
110
+ target: ({ key: k }) => at(k),
111
+ },
112
+ };
113
+ }
114
+ /** True for a collection `collection()` made, in this copy of the package or another: read by its brand. */
115
+ export const isCollection = (x) => isObject(x) && x[COLLECTION] === true && x.kind === "collection";
116
+ /** The schema a collection's records are saved under: its fields, and nothing else. */
117
+ export const recordSchemaOf = (c) => c.save.input.shape.record;
118
+ /** Where the record no longer fits the collection's fields, as `Saved.issues` says it; none when it fits. */
119
+ export function recordIssues(c, record) {
120
+ const parsed = recordSchemaOf(c).safeParse(record);
121
+ if (parsed.success)
122
+ return [];
123
+ return parsed.error.issues.map(({ path, message }) => ({
124
+ path: path.filter((p) => typeof p !== "symbol"),
125
+ message,
126
+ }));
127
+ }
128
+ /** The record as `get`, `list` and `save` answer it: with its issues, when it has any. */
129
+ function answer(c, stored) {
130
+ const issues = recordIssues(c, stored.record);
131
+ return issues.length ? { ...stored, issues } : stored;
132
+ }
133
+ /** Who a save is by: the person a direct call is made as, else the run. */
134
+ const updatedBy = (call) => call.by ?? `run:${call.runId}`;
135
+ /** The vendor `data` as a connector of the collections' operations, the very ones each `collection()` made. */
136
+ export function collectionsConnector(list) {
137
+ const ops = list.map((c) => [c.name, { get: c.get, list: c.list, save: c.save, delete: c.delete }]);
138
+ return { ...defineConnector(DATA_VENDOR, {}), ...Object.fromEntries(ops) };
139
+ }
140
+ const COLUMNS = "key, record, updated_at, updated_by";
141
+ const RANGE = { gt: ">", gte: ">=", lt: "<", lte: "<=" };
142
+ /**
143
+ * The collections' driver: their records in `DATA_TABLE` of the database `store` is on, through
144
+ * the pool a run's calls share, or one connection in a transaction (a direct write, with its
145
+ * ledger record). Every value reaches Postgres as a parameter, field names included; the field
146
+ * names and operators are the ones the input's schema accepts.
147
+ */
148
+ export function collectionsDriver(list, store) {
149
+ const query = async (sql, params) => (await store.db()).query(sql, params);
150
+ const impl = list.map((c) => {
151
+ const saved = (row) => answer(c, {
152
+ key: row.key,
153
+ record: row.record,
154
+ updatedAt: row.updated_at.toISOString(),
155
+ updatedBy: row.updated_by,
156
+ });
157
+ const ops = {
158
+ get: async ({ key }) => {
159
+ const sql = `SELECT ${COLUMNS} FROM ${DATA_TABLE} WHERE collection = $1 AND key = $2`;
160
+ const [row] = (await query(sql, [c.name, key])).rows;
161
+ return row ? saved(row) : null;
162
+ },
163
+ list: async (listing) => (await query(...listSql(c, listing))).rows.map(saved),
164
+ save: async ({ record }, call) => {
165
+ const sql = `INSERT INTO ${DATA_TABLE} (collection, key, record, updated_by) VALUES ($1, $2, $3, $4) ` +
166
+ "ON CONFLICT (collection, key) DO UPDATE SET record = EXCLUDED.record, updated_at = now(), " +
167
+ `updated_by = EXCLUDED.updated_by RETURNING ${COLUMNS}`;
168
+ const params = [c.name, record[c.key], JSON.stringify(record), updatedBy(call)];
169
+ return saved((await query(sql, params)).rows[0]);
170
+ },
171
+ delete: async ({ key }) => {
172
+ const sql = `DELETE FROM ${DATA_TABLE} WHERE collection = $1 AND key = $2`;
173
+ return { deleted: ((await query(sql, [c.name, key])).rowCount ?? 0) > 0 };
174
+ },
175
+ };
176
+ return [c.name, ops];
177
+ });
178
+ return defineDriver(collectionsConnector(list), Object.fromEntries(impl));
179
+ }
180
+ /** Each field's `JsonType`, by name: what `list` compares its values as. */
181
+ const jsonTypesOf = (c) => Object.fromEntries(Object.entries(c.fields).map(([name, kind]) => {
182
+ const type = typeof kind === "string" ? kind : "type" in kind ? kind.type : "text";
183
+ return [name, type === "number" || type === "boolean" ? type : "string"];
184
+ }));
185
+ const SQL_TYPE = { string: "text", number: "numeric", boolean: "boolean" };
186
+ /**
187
+ * The `list` query on Postgres, and its parameters. A field's value is compared as its declared
188
+ * type, text byte by byte (`COLLATE "C"`, whatever the database's collation, as the fake
189
+ * compares it); a missing value, or one of another type, is SQL's null: it matches no filter and
190
+ * sorts last.
191
+ */
192
+ function listSql(c, { where = {}, order, limit }) {
193
+ const types = jsonTypesOf(c);
194
+ const params = [c.name];
195
+ const param = (value) => `$${params.push(value)}`;
196
+ const field = (name) => {
197
+ const type = types[name];
198
+ const json = `record -> ${param(name)}::text`;
199
+ const typed = `CASE WHEN jsonb_typeof(${json}) = '${type}' THEN (${json} #>> '{}')::${SQL_TYPE[type]} END`;
200
+ return type === "string" ? `(${typed}) COLLATE "C"` : typed;
201
+ };
202
+ const value = (name, v) => `${param(v)}::${SQL_TYPE[types[name]]}`;
203
+ const conditions = ["collection = $1"];
204
+ for (const [name, condition] of Object.entries(where)) {
205
+ if (condition === undefined)
206
+ continue;
207
+ if (!isObject(condition))
208
+ conditions.push(`${field(name)} = ${value(name, condition)}`);
209
+ else {
210
+ for (const [op, v] of Object.entries(condition)) {
211
+ const sign = RANGE[op];
212
+ if (v !== undefined)
213
+ conditions.push(`${field(name)} ${sign} ${value(name, v)}`);
214
+ }
215
+ }
216
+ }
217
+ const byKey = `key COLLATE "C"`;
218
+ const by = order === undefined
219
+ ? byKey
220
+ : `${field(order.replace(/^-/, ""))} ${order.startsWith("-") ? "DESC" : "ASC"} NULLS LAST, ${byKey}`;
221
+ const sql = `SELECT ${COLUMNS} FROM ${DATA_TABLE} WHERE ${conditions.join(" AND ")} ORDER BY ${by} LIMIT ${param(limit)}`;
222
+ return [sql, params];
223
+ }
224
+ /** Orders two values of one type as Postgres does under `COLLATE "C"`: strings by their UTF-8 bytes. */
225
+ const compare = (a, b) => typeof a === "string" && typeof b === "string"
226
+ ? Buffer.compare(Buffer.from(a), Buffer.from(b))
227
+ : a < b
228
+ ? -1
229
+ : a > b
230
+ ? 1
231
+ : 0;
232
+ /** The record's value of a field when it is of the field's type; none otherwise, as for a missing one. */
233
+ const valueIn = (record, name, type) => typeof record[name] === type ? record[name] : undefined;
234
+ /** Whether the record meets each condition of `where`, as the driver's SQL decides it. */
235
+ function matches(record, where, types) {
236
+ return Object.entries(where).every(([name, condition]) => {
237
+ if (condition === undefined)
238
+ return true;
239
+ const value = valueIn(record, name, types[name]);
240
+ if (value === undefined)
241
+ return false;
242
+ if (!isObject(condition))
243
+ return compare(value, condition) === 0;
244
+ const { gt, gte, lt, lte } = condition;
245
+ return ((gt === undefined || compare(value, gt) > 0) &&
246
+ (gte === undefined || compare(value, gte) >= 0) &&
247
+ (lt === undefined || compare(value, lt) < 0) &&
248
+ (lte === undefined || compare(value, lte) <= 0));
249
+ });
250
+ }
251
+ const byKey = (a, b) => compare(a.key, b.key);
252
+ /** Sorts as the driver's SQL does: by the field (missing last), then by key; by key alone without one. */
253
+ function byOrder(order, types) {
254
+ if (order === undefined)
255
+ return byKey;
256
+ const name = order.replace(/^-/, "");
257
+ const sign = order.startsWith("-") ? -1 : 1;
258
+ return (a, b) => {
259
+ const x = valueIn(a.record, name, types[name]);
260
+ const y = valueIn(b.record, name, types[name]);
261
+ if ((x === undefined) !== (y === undefined))
262
+ return x === undefined ? 1 : -1;
263
+ return (x === undefined ? 0 : sign * compare(x, y)) || byKey(a, b);
264
+ };
265
+ }
266
+ /**
267
+ * An in-memory fake of the collections' driver, which starts empty: a scenario seeds it
268
+ * (`Given data.<name> "<key>" has`), and a test may through `update`.
269
+ */
270
+ export function collectionsFake(list) {
271
+ return defineFake(collectionsConnector(list), {
272
+ // Null-prototype records, so any key is a record's own, `__proto__` too, as it is in Postgres.
273
+ initial: () => Object.fromEntries(list.map((c) => [c.name, Object.create(null)])),
274
+ ops: (state) => Object.fromEntries(list.map((c) => {
275
+ const records = () => state[c.name];
276
+ const types = jsonTypesOf(c);
277
+ const saved = (stored) => answer(c, structuredClone(stored));
278
+ const ops = {
279
+ get: async ({ key }) => (Object.hasOwn(records(), key) ? saved(records()[key]) : null),
280
+ list: async ({ where = {}, order, limit }) => Object.values(records())
281
+ .filter((s) => matches(s.record, where, types))
282
+ .toSorted(byOrder(order, types))
283
+ .slice(0, limit)
284
+ .map(saved),
285
+ save: async ({ record }, call) => {
286
+ const key = record[c.key];
287
+ const stored = {
288
+ key,
289
+ record: structuredClone(record),
290
+ updatedAt: new Date().toISOString(),
291
+ updatedBy: updatedBy(call),
292
+ };
293
+ records()[key] = stored;
294
+ return saved(stored);
295
+ },
296
+ delete: async ({ key }) => {
297
+ const had = Object.hasOwn(records(), key);
298
+ delete records()[key];
299
+ return { deleted: had };
300
+ },
301
+ };
302
+ return [c.name, ops];
303
+ })),
304
+ });
305
+ }
306
+ //# sourceMappingURL=collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { type Collection } from "./collection.ts";
2
+ import { type LazyPool } from "./db.ts";
1
3
  import { type WorkflowDefinition } from "./define.ts";
2
4
  import type { Fake } from "./fake.ts";
3
5
  import type { LedgerStore } from "./ledger.ts";
@@ -6,11 +8,11 @@ import type { Policy } from "./policy.ts";
6
8
  import { type Resource } from "./resource.ts";
7
9
  import type { Steps } from "./steps.ts";
8
10
  /**
9
- * What a worker runs, on which connectors, under which policy: the argument to `startWorker`.
11
+ * What a worker runs, on which connectors: the argument to `startWorker`.
10
12
  * A project usually keeps it in `sanoma.config.ts` and passes it in; nothing loads that file
11
13
  * automatically. The rest is convention: each connector carries its driver and its fake
12
- * (`withSetup`), the app's name is its `package.json`'s, and the scenarios and data files are
13
- * `scenarios/` and `resources/` in `root`.
14
+ * (`withSetup`), the app's name is its `package.json`'s, and the scenarios, data files and
15
+ * policies are `scenarios/`, `resources/` and `policies/` in `root`.
14
16
  */
15
17
  export interface SanomaConfig {
16
18
  workflows: WorkflowDefinition<any, any>[];
@@ -22,8 +24,6 @@ export interface SanomaConfig {
22
24
  * package attaches the maker of.
23
25
  */
24
26
  connectors: Connector<any, any>[];
25
- /** Checked before every operation call. Required: `allowAll` says that every call is allowed. */
26
- policy: Policy;
27
27
  /**
28
28
  * Where the audit record goes. Default: `postgresLedger` on the config's database, which the
29
29
  * worker, the app and other processes share; the worker and the client each make their own
@@ -50,8 +50,15 @@ export interface SanomaConfig {
50
50
  appName?: never;
51
51
  /** @deprecated Gone: the scenarios are `<root>/scenarios/`. Leave it out. */
52
52
  scenarios?: never;
53
+ /** @deprecated Gone: the policy is `<root>/policies/**\/*.cedar`. Leave it out. */
54
+ policy?: never;
53
55
  /** The company's own steps for its scenarios (`defineSteps`), besides those its connectors ship. */
54
56
  steps?: Steps[];
57
+ /**
58
+ * The company's collections (`collection()`): their operations are `data.<name>.<op>`, their
59
+ * records in the config's database, and in memory for a sandbox run.
60
+ */
61
+ collections?: readonly Collection[];
55
62
  }
56
63
  /** A config, checked, with everything a worker, client or app derives from it. */
57
64
  export interface ResolvedConfig {
@@ -65,7 +72,10 @@ export interface ResolvedConfig {
65
72
  version: string;
66
73
  /** The DBOS queue runs are started on: `sanoma:<appName>`. */
67
74
  queueName: string;
68
- /** The operations the connectors declare, by id, and the built-in `sanoma.source.resolve` when one declares a host type. */
75
+ /**
76
+ * The operations the connectors declare, by id, the built-in `sanoma.source.resolve` when one
77
+ * declares a host type, and the collections' (`data.<name>.<op>`).
78
+ */
69
79
  ops: Map<string, Op>;
70
80
  /** The drivers' functions, by operation id, each guarded by its driver's `env` (`indexDrivers`). */
71
81
  drivers: Map<string, DriverFn>;
@@ -82,12 +92,27 @@ export interface ResolvedConfig {
82
92
  types: ReadonlyMap<string, Resource>;
83
93
  /** The steps scenarios can use besides the generic ones: each connector's (`withSetup`), once each, then the config's own. */
84
94
  steps: readonly Steps[];
95
+ /** The config's collections, by name: their operations are in `ops` and `drivers`, their fake in `fakes`, under `DATA_VENDOR`. */
96
+ collections: ReadonlyMap<string, Collection>;
85
97
  /**
86
- * By name; each checked against `ops` and `drivers`. The config's, and the built-ins `drift`
87
- * (`DRIFT_WORKFLOW`) and `apply` (`APPLY_WORKFLOW`) when its connectors declare resource types.
98
+ * The pool the collections' driver keeps their records through, when there are any: opened on
99
+ * its first call, and closed by whoever calls the driver (the worker, and the client, which
100
+ * calls it directly, when each stops).
101
+ */
102
+ dataStore?: LazyPool;
103
+ /**
104
+ * By name; each checked against `ops` and `drivers`. The config's, the built-ins `drift`
105
+ * (`DRIFT_WORKFLOW`) and `apply` (`APPLY_WORKFLOW`) when its connectors declare resource types,
106
+ * and `call` (`CALL_WORKFLOW`) when an operation may be called directly (`isDirect`).
88
107
  */
89
108
  workflows: Map<string, WorkflowDefinition<any, any>>;
109
+ /**
110
+ * Checked before every operation call and run start: the root's `policies/**\/*.cedar`
111
+ * (`policyAt`), `allowAll` without any, and one that refuses everything while they have problems.
112
+ */
90
113
  policy: Policy;
114
+ /** What is wrong in the policies, one printable line each (`file:line:col: <policy id>: <message>`). */
115
+ policyProblems: string[];
91
116
  /** The config's `ledger`, when it gives one; the worker and the client otherwise make `postgresLedger` on `databaseUrl` (`ownedLedger`). */
92
117
  ledger?: LedgerStore;
93
118
  /**
@@ -110,14 +135,17 @@ export declare function resolveDatabaseUrl(config: {
110
135
  export declare function rootOf(config: Pick<SanomaConfig, "root" | "file">): string | undefined;
111
136
  /**
112
137
  * Checks a config and derives what the runtime needs from it: the drivers and fakes from the
113
- * connectors' setups (`withSetup`), and from `root`, the app's name (`package.json`) and the
114
- * scenarios (`scenarios/`). Throws what `startWorker` would refuse, with the same message, so
115
- * `describeConfig`, `SanomaClient.connect` and the app fail the same way the worker does. It
116
- * opens nothing and reads no file outside `root`. Each entry
117
- * point resolves once: the worker and the client when they start, the app at `startApp`, the
118
- * `sanoma` bin when it loads the config.
138
+ * connectors' setups (`withSetup`), the collections' operations, driver and fake, and from
139
+ * `root`, the app's name (`package.json`), the scenarios (`scenarios/`) and the policy
140
+ * (`policies/`, `policyAt`). Throws what
141
+ * `startWorker` would refuse, with the same message, so `describeConfig`, `SanomaClient.connect`
142
+ * and the app fail the same way the worker does. It opens nothing and reads no file outside
143
+ * `root`. Each entry point resolves once: the worker and the client when they start, the app at
144
+ * `startApp`, the `sanoma` bin when it loads the config.
119
145
  */
120
146
  export declare function resolveConfig(config: SanomaConfig): ResolvedConfig;
147
+ /** The config's connectors and the runtime's people types' (`peopleConnector`): what its data files are read against. */
148
+ export declare const connectorsOf: (config: Pick<SanomaConfig, "connectors">) => Connector<any, any>[];
121
149
  /**
122
150
  * The drivers' functions by operation id, refusing one no connector declares. A driver that
123
151
  * declares an `env` has each function guarded by it (`guarded`); a fake declares none, so a