@segmentstream-cloud/ontology 0.6.0 → 0.7.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 (43) hide show
  1. package/README.md +14 -3
  2. package/dist/analyze.js +46 -13
  3. package/dist/connection.js +1 -1
  4. package/dist/entity.d.ts +6 -6
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/manifest.d.ts +468 -10
  8. package/dist/manifest.js +46 -6
  9. package/dist/migrate/apply.d.ts +1 -1
  10. package/dist/migrate/apply.js +5 -4
  11. package/dist/migrate/model.d.ts +112 -2
  12. package/dist/migrate/plan.js +7 -6
  13. package/dist/migrate/storage.d.ts +6 -6
  14. package/dist/migrate/storage.js +25 -15
  15. package/dist/names.d.ts +7 -10
  16. package/dist/names.js +10 -13
  17. package/dist/permissions.d.ts +2 -2
  18. package/dist/permissions.js +4 -4
  19. package/dist/query.d.ts +6 -6
  20. package/dist/query.js +41 -30
  21. package/dist/runtime/cursor.js +1 -1
  22. package/dist/runtime/generated-actions.d.ts +4 -4
  23. package/dist/runtime/generated-actions.js +4 -7
  24. package/dist/runtime/history.js +1 -1
  25. package/dist/runtime/model.d.ts +2 -2
  26. package/dist/runtime/model.js +1 -1
  27. package/dist/runtime/principals.js +15 -12
  28. package/dist/runtime/reads.js +1 -1
  29. package/dist/runtime/records.js +1 -1
  30. package/dist/runtime/sql-runtime.js +1 -1
  31. package/dist/service/errors.d.ts +1 -0
  32. package/dist/service/errors.js +5 -1
  33. package/dist/service/handlers.js +2 -2
  34. package/dist/service/runtime-contract.d.ts +1 -1
  35. package/dist/service/schemas.d.ts +201 -26
  36. package/dist/service/schemas.js +30 -21
  37. package/dist/service/service.js +2 -2
  38. package/dist/user.d.ts +3 -3
  39. package/dist/user.js +7 -4
  40. package/dist/warehouse-backed.js +3 -3
  41. package/dist/wire.d.ts +5 -0
  42. package/dist/wire.js +21 -0
  43. package/package.json +1 -1
@@ -4,14 +4,14 @@ import type { ApiPlan } from "./plan.js";
4
4
  export declare const IdParam: Schema.String;
5
5
  /** The schemas of one entity's records and queries. */
6
6
  export interface EntitySchemas {
7
- /** System fields, properties and `<relationship>Id` per `rel.one`. */
7
+ /** System fields, properties and `<relationship>_id` per `rel.one`. */
8
8
  readonly record: Schema.Top;
9
9
  /** A record with the relationships a query may include. */
10
10
  readonly result: Schema.Top;
11
11
  /** `POST /<entity>/query`: the entity's query from `src/query.ts`. */
12
12
  readonly query: Schema.Top;
13
13
  /** `GET /<entity>` and to-many traversals: the query as URL parameters. */
14
- readonly queryParams: Schema.Struct.Fields;
14
+ readonly queryParams: Schema.Top;
15
15
  /** `GET /<entity>/{id}/<rel.one>`. */
16
16
  readonly includeParams: Schema.Struct.Fields;
17
17
  readonly findResult: Schema.Top;
@@ -37,7 +37,7 @@ export declare const Validation: Schema.Struct<{
37
37
  readonly message: Schema.String;
38
38
  }>>;
39
39
  }>;
40
- export declare const HistoryEntry: Schema.Struct<{
40
+ export declare const HistoryEntry: Schema.decodeTo<Schema.Struct<{
41
41
  readonly id: Schema.String;
42
42
  readonly recordId: Schema.String;
43
43
  readonly action: Schema.String;
@@ -49,9 +49,21 @@ export declare const HistoryEntry: Schema.Struct<{
49
49
  readonly before: Schema.Codec<Schema.Json, Schema.Json, never, never>;
50
50
  readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
51
51
  }>>;
52
- }>;
53
- export declare const HistoryPage: Schema.Struct<{
54
- readonly items: Schema.$Array<Schema.Struct<{
52
+ }>, Schema.Struct<{
53
+ readonly id: Schema.toEncoded<Schema.String>;
54
+ readonly record_id: Schema.toEncoded<Schema.String>;
55
+ readonly action: Schema.toEncoded<Schema.String>;
56
+ readonly action_id: Schema.toEncoded<Schema.String>;
57
+ readonly kind: Schema.toEncoded<Schema.Literals<readonly ["create", "update", "delete", "link", "unlink"]>>;
58
+ readonly principal_id: Schema.toEncoded<Schema.NullOr<Schema.String>>;
59
+ readonly at: Schema.toEncoded<Schema.String>;
60
+ readonly changes: Schema.toEncoded<Schema.$Record<Schema.String, Schema.Struct<{
61
+ readonly before: Schema.Codec<Schema.Json, Schema.Json, never, never>;
62
+ readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
63
+ }>>>;
64
+ }>, never, never>;
65
+ export declare const HistoryPage: Schema.decodeTo<Schema.Struct<{
66
+ readonly items: Schema.$Array<Schema.decodeTo<Schema.Struct<{
55
67
  readonly id: Schema.String;
56
68
  readonly recordId: Schema.String;
57
69
  readonly action: Schema.String;
@@ -63,9 +75,48 @@ export declare const HistoryPage: Schema.Struct<{
63
75
  readonly before: Schema.Codec<Schema.Json, Schema.Json, never, never>;
64
76
  readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
65
77
  }>>;
66
- }>>;
78
+ }>, Schema.Struct<{
79
+ readonly id: Schema.toEncoded<Schema.String>;
80
+ readonly record_id: Schema.toEncoded<Schema.String>;
81
+ readonly action: Schema.toEncoded<Schema.String>;
82
+ readonly action_id: Schema.toEncoded<Schema.String>;
83
+ readonly kind: Schema.toEncoded<Schema.Literals<readonly ["create", "update", "delete", "link", "unlink"]>>;
84
+ readonly principal_id: Schema.toEncoded<Schema.NullOr<Schema.String>>;
85
+ readonly at: Schema.toEncoded<Schema.String>;
86
+ readonly changes: Schema.toEncoded<Schema.$Record<Schema.String, Schema.Struct<{
87
+ readonly before: Schema.Codec<Schema.Json, Schema.Json, never, never>;
88
+ readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
89
+ }>>>;
90
+ }>, never, never>>;
67
91
  readonly nextCursor: Schema.NullOr<Schema.String>;
68
- }>;
92
+ }>, Schema.Struct<{
93
+ readonly items: Schema.toEncoded<Schema.$Array<Schema.decodeTo<Schema.Struct<{
94
+ readonly id: Schema.String;
95
+ readonly recordId: Schema.String;
96
+ readonly action: Schema.String;
97
+ readonly actionId: Schema.String;
98
+ readonly kind: Schema.Literals<readonly ["create", "update", "delete", "link", "unlink"]>;
99
+ readonly principalId: Schema.NullOr<Schema.String>;
100
+ readonly at: Schema.String;
101
+ readonly changes: Schema.$Record<Schema.String, Schema.Struct<{
102
+ readonly before: Schema.Codec<Schema.Json, Schema.Json, never, never>;
103
+ readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
104
+ }>>;
105
+ }>, Schema.Struct<{
106
+ readonly id: Schema.toEncoded<Schema.String>;
107
+ readonly record_id: Schema.toEncoded<Schema.String>;
108
+ readonly action: Schema.toEncoded<Schema.String>;
109
+ readonly action_id: Schema.toEncoded<Schema.String>;
110
+ readonly kind: Schema.toEncoded<Schema.Literals<readonly ["create", "update", "delete", "link", "unlink"]>>;
111
+ readonly principal_id: Schema.toEncoded<Schema.NullOr<Schema.String>>;
112
+ readonly at: Schema.toEncoded<Schema.String>;
113
+ readonly changes: Schema.toEncoded<Schema.$Record<Schema.String, Schema.Struct<{
114
+ readonly before: Schema.Codec<Schema.Json, Schema.Json, never, never>;
115
+ readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
116
+ }>>>;
117
+ }>, never, never>>>;
118
+ readonly next_cursor: Schema.toEncoded<Schema.NullOr<Schema.String>>;
119
+ }>, never, never>;
69
120
  /** The body of `POST /<entity>/actions/<action>`, as documented. */
70
121
  export declare function actionRequest(input: Schema.Top): Schema.Top;
71
122
  /** The body of `POST /<entity>/actions/<action>`, as decoded: `runAction` validates the input. */
@@ -94,10 +145,52 @@ export declare const ActionResponse: Schema.Struct<{
94
145
  }>>;
95
146
  }>>;
96
147
  }>;
97
- export declare const Catalog: Schema.Struct<{
98
- readonly version: Schema.Int;
99
- readonly hash: Schema.String;
100
- readonly entities: Schema.$Array<Schema.Struct<{
148
+ export declare const Catalog: Schema.decodeTo<Schema.Struct<{
149
+ readonly entities: Schema.$Array<Schema.encodeKeys<Schema.Struct<{
150
+ readonly properties: Schema.$Array<Schema.encodeKeys<Schema.Struct<{
151
+ readonly column: Schema.optionalKey<Schema.String>;
152
+ readonly scope: Schema.optionalKey<Schema.$Array<Schema.String>>;
153
+ readonly name: Schema.String;
154
+ readonly type: Schema.Literals<readonly ["text", "email", "url", "integer", "number", "boolean", "date", "timestamp", "select", "json"]>;
155
+ readonly label: Schema.String;
156
+ readonly description: Schema.optionalKey<Schema.String>;
157
+ readonly optional: Schema.Boolean;
158
+ readonly unique: Schema.Boolean;
159
+ readonly managed: Schema.Boolean;
160
+ readonly default: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
161
+ readonly display: Schema.optionalKey<Schema.String>;
162
+ readonly renamedFrom: Schema.optionalKey<Schema.String>;
163
+ readonly min: Schema.optionalKey<Schema.Number>;
164
+ readonly max: Schema.optionalKey<Schema.Number>;
165
+ readonly maxLength: Schema.optionalKey<Schema.Number>;
166
+ readonly options: Schema.optionalKey<Schema.$Array<Schema.Struct<{
167
+ readonly value: Schema.String;
168
+ readonly label: Schema.String;
169
+ readonly description: Schema.String;
170
+ }>>>;
171
+ }>, {
172
+ readonly renamedFrom: "renamed_from";
173
+ readonly maxLength: "max_length";
174
+ }>>;
175
+ readonly relationships: Schema.$Array<Schema.encodeKeys<Schema.Struct<{
176
+ readonly on: Schema.optionalKey<Schema.encodeKeys<Schema.Struct<{
177
+ readonly property: Schema.String;
178
+ readonly targetProperty: Schema.String;
179
+ }>, {
180
+ readonly targetProperty: "target_property";
181
+ }>>;
182
+ readonly onDelete: Schema.optionalKey<Schema.decodeTo<Schema.Literals<readonly ["setNull", "cascade", "restrict"]>, Schema.Literals<readonly ["set_null", "cascade", "restrict"]>, never, never>>;
183
+ readonly name: Schema.String;
184
+ readonly to: Schema.String;
185
+ readonly inverse: Schema.String;
186
+ readonly cardinality: Schema.Literals<readonly ["one", "many"]>;
187
+ readonly optional: Schema.optionalKey<Schema.Boolean>;
188
+ readonly ordered: Schema.optionalKey<Schema.Boolean>;
189
+ readonly phrase: Schema.Tuple<readonly [Schema.String, Schema.String]>;
190
+ readonly description: Schema.String;
191
+ }>, {
192
+ readonly onDelete: "on_delete";
193
+ }>>;
101
194
  readonly source: Schema.optionalKey<Schema.Struct<{
102
195
  readonly asset: Schema.String;
103
196
  readonly key: Schema.$Array<Schema.String>;
@@ -120,7 +213,49 @@ export declare const Catalog: Schema.Struct<{
120
213
  readonly builtIn: Schema.Boolean;
121
214
  readonly renamedFrom: Schema.optionalKey<Schema.String>;
122
215
  readonly guide: Schema.optionalKey<Schema.String>;
123
- readonly properties: Schema.$Array<Schema.Struct<{
216
+ readonly indexes: Schema.$Array<Schema.Struct<{
217
+ readonly properties: Schema.$Array<Schema.String>;
218
+ readonly unique: Schema.Boolean;
219
+ }>>;
220
+ readonly actions: Schema.$Array<Schema.Struct<{
221
+ readonly name: Schema.String;
222
+ readonly description: Schema.String;
223
+ readonly permission: Schema.optionalKey<Schema.String>;
224
+ readonly input: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>;
225
+ readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
226
+ }>>;
227
+ readonly permissions: Schema.optionalKey<Schema.Struct<{
228
+ readonly read: Schema.String;
229
+ readonly create: Schema.optionalKey<Schema.String>;
230
+ readonly update: Schema.optionalKey<Schema.String>;
231
+ readonly delete: Schema.optionalKey<Schema.String>;
232
+ readonly link: Schema.optionalKey<Schema.String>;
233
+ readonly unlink: Schema.optionalKey<Schema.String>;
234
+ }>>;
235
+ }>, {
236
+ readonly builtIn: "built_in";
237
+ readonly renamedFrom: "renamed_from";
238
+ }>>;
239
+ readonly roles: Schema.$Array<Schema.encodeKeys<Schema.Struct<{
240
+ readonly name: Schema.String;
241
+ readonly description: Schema.String;
242
+ readonly builtIn: Schema.Boolean;
243
+ readonly permissions: Schema.optionalKey<Schema.$Array<Schema.String>>;
244
+ }>, {
245
+ readonly builtIn: "built_in";
246
+ }>>;
247
+ readonly version: Schema.Int;
248
+ readonly hash: Schema.String;
249
+ readonly signIn: Schema.optionalKey<Schema.Struct<{
250
+ readonly policy: Schema.String;
251
+ }>>;
252
+ readonly connections: Schema.optionalKey<Schema.$Array<Schema.Struct<{
253
+ readonly key: Schema.String;
254
+ readonly description: Schema.String;
255
+ }>>>;
256
+ }>, Schema.Struct<{
257
+ readonly entities: Schema.toEncoded<Schema.$Array<Schema.encodeKeys<Schema.Struct<{
258
+ readonly properties: Schema.$Array<Schema.encodeKeys<Schema.Struct<{
124
259
  readonly column: Schema.optionalKey<Schema.String>;
125
260
  readonly scope: Schema.optionalKey<Schema.$Array<Schema.String>>;
126
261
  readonly name: Schema.String;
@@ -141,22 +276,51 @@ export declare const Catalog: Schema.Struct<{
141
276
  readonly label: Schema.String;
142
277
  readonly description: Schema.String;
143
278
  }>>>;
279
+ }>, {
280
+ readonly renamedFrom: "renamed_from";
281
+ readonly maxLength: "max_length";
144
282
  }>>;
145
- readonly relationships: Schema.$Array<Schema.Struct<{
146
- readonly on: Schema.optionalKey<Schema.Struct<{
283
+ readonly relationships: Schema.$Array<Schema.encodeKeys<Schema.Struct<{
284
+ readonly on: Schema.optionalKey<Schema.encodeKeys<Schema.Struct<{
147
285
  readonly property: Schema.String;
148
286
  readonly targetProperty: Schema.String;
287
+ }>, {
288
+ readonly targetProperty: "target_property";
149
289
  }>>;
290
+ readonly onDelete: Schema.optionalKey<Schema.decodeTo<Schema.Literals<readonly ["setNull", "cascade", "restrict"]>, Schema.Literals<readonly ["set_null", "cascade", "restrict"]>, never, never>>;
150
291
  readonly name: Schema.String;
151
292
  readonly to: Schema.String;
152
293
  readonly inverse: Schema.String;
153
294
  readonly cardinality: Schema.Literals<readonly ["one", "many"]>;
154
295
  readonly optional: Schema.optionalKey<Schema.Boolean>;
155
- readonly onDelete: Schema.optionalKey<Schema.Literals<readonly ["setNull", "cascade", "restrict"]>>;
156
296
  readonly ordered: Schema.optionalKey<Schema.Boolean>;
157
297
  readonly phrase: Schema.Tuple<readonly [Schema.String, Schema.String]>;
158
298
  readonly description: Schema.String;
299
+ }>, {
300
+ readonly onDelete: "on_delete";
159
301
  }>>;
302
+ readonly source: Schema.optionalKey<Schema.Struct<{
303
+ readonly asset: Schema.String;
304
+ readonly key: Schema.$Array<Schema.String>;
305
+ readonly currency: Schema.optionalKey<Schema.String>;
306
+ }>>;
307
+ readonly measures: Schema.optionalKey<Schema.$Array<Schema.Struct<{
308
+ readonly name: Schema.String;
309
+ readonly label: Schema.String;
310
+ readonly description: Schema.String;
311
+ readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
312
+ readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
313
+ readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
314
+ }>>>;
315
+ readonly id: Schema.String;
316
+ readonly name: Schema.String;
317
+ readonly label: Schema.String;
318
+ readonly plural: Schema.String;
319
+ readonly title: Schema.String;
320
+ readonly description: Schema.String;
321
+ readonly builtIn: Schema.Boolean;
322
+ readonly renamedFrom: Schema.optionalKey<Schema.String>;
323
+ readonly guide: Schema.optionalKey<Schema.String>;
160
324
  readonly indexes: Schema.$Array<Schema.Struct<{
161
325
  readonly properties: Schema.$Array<Schema.String>;
162
326
  readonly unique: Schema.Boolean;
@@ -176,21 +340,28 @@ export declare const Catalog: Schema.Struct<{
176
340
  readonly link: Schema.optionalKey<Schema.String>;
177
341
  readonly unlink: Schema.optionalKey<Schema.String>;
178
342
  }>>;
179
- }>>;
180
- readonly roles: Schema.$Array<Schema.Struct<{
343
+ }>, {
344
+ readonly builtIn: "built_in";
345
+ readonly renamedFrom: "renamed_from";
346
+ }>>>;
347
+ readonly roles: Schema.toEncoded<Schema.$Array<Schema.encodeKeys<Schema.Struct<{
181
348
  readonly name: Schema.String;
182
349
  readonly description: Schema.String;
183
350
  readonly builtIn: Schema.Boolean;
184
351
  readonly permissions: Schema.optionalKey<Schema.$Array<Schema.String>>;
185
- }>>;
186
- readonly signIn: Schema.optionalKey<Schema.Struct<{
352
+ }>, {
353
+ readonly builtIn: "built_in";
354
+ }>>>;
355
+ readonly version: Schema.toEncoded<Schema.Int>;
356
+ readonly hash: Schema.toEncoded<Schema.String>;
357
+ readonly sign_in: Schema.toEncoded<Schema.optionalKey<Schema.Struct<{
187
358
  readonly policy: Schema.String;
188
- }>>;
189
- readonly connections: Schema.optionalKey<Schema.$Array<Schema.Struct<{
359
+ }>>>;
360
+ readonly connections: Schema.toEncoded<Schema.optionalKey<Schema.$Array<Schema.Struct<{
190
361
  readonly key: Schema.String;
191
362
  readonly description: Schema.String;
192
- }>>>;
193
- }>;
363
+ }>>>>;
364
+ }>, never, never>;
194
365
  export declare const Graph: Schema.Struct<{
195
366
  readonly hash: Schema.String;
196
367
  readonly entities: Schema.$Array<Schema.Struct<{
@@ -212,11 +383,15 @@ export declare const Graph: Schema.Struct<{
212
383
  readonly description: Schema.String;
213
384
  }>>;
214
385
  }>;
215
- export declare const Status: Schema.Struct<{
386
+ export declare const Status: Schema.decodeTo<Schema.Struct<{
216
387
  readonly hash: Schema.String;
217
388
  readonly appliedHash: Schema.NullOr<Schema.String>;
218
389
  readonly upToDate: Schema.Boolean;
219
- }>;
390
+ }>, Schema.Struct<{
391
+ readonly hash: Schema.toEncoded<Schema.String>;
392
+ readonly applied_hash: Schema.toEncoded<Schema.NullOr<Schema.String>>;
393
+ readonly up_to_date: Schema.toEncoded<Schema.Boolean>;
394
+ }>, never, never>;
220
395
  /** The longest address `POST /api/ontology/sign-in/check` reads; RFC 5321 allows 254. */
221
396
  export declare const SIGN_IN_CHECK_MAX_EMAIL_LENGTH = 320;
222
397
  /** `POST /api/ontology/sign-in/check`: the address a person wants to sign in with. */
@@ -1,5 +1,6 @@
1
1
  import { Schema, SchemaTransformation } from "effect";
2
- import { OntologyManifest } from "../manifest.js";
2
+ import { wireStruct } from "../wire.js";
3
+ import { OntologyManifestWire } from "../manifest.js";
3
4
  import { entityQuerySchemas, queryLinks } from "../query.js";
4
5
  import { ValidationIssue as RuntimeValidationIssue } from "../runtime/errors.js";
5
6
  import { Edit, HistoryEntry as RuntimeHistoryEntry } from "../runtime/history.js";
@@ -78,12 +79,12 @@ function recordFields(entity) {
78
79
  }
79
80
  const fields = {
80
81
  id: Id.annotate({ description: "The record's id." }),
81
- createdAt: Timestamp,
82
- updatedAt: Timestamp,
83
- createdBy: Schema.NullOr(Id).annotate({
82
+ created_at: Timestamp,
83
+ updated_at: Timestamp,
84
+ created_by: Schema.NullOr(Id).annotate({
84
85
  description: "The user who created the record; null when the platform wrote it.",
85
86
  }),
86
- updatedBy: Schema.NullOr(Id).annotate({
87
+ updated_by: Schema.NullOr(Id).annotate({
87
88
  description: "The user who last updated the record; null when the platform wrote it.",
88
89
  }),
89
90
  };
@@ -94,7 +95,7 @@ function recordFields(entity) {
94
95
  for (const { name, relationship } of entity.relationships) {
95
96
  if (relationship.cardinality === "one") {
96
97
  const key = Id.annotate({ description: relationship.description });
97
- fields[`${name}Id`] = relationship.optional ? Schema.NullOr(key) : key;
98
+ fields[`${name}_id`] = relationship.optional ? Schema.NullOr(key) : key;
98
99
  }
99
100
  }
100
101
  return fields;
@@ -105,11 +106,11 @@ function splitList(value) {
105
106
  .map((part) => part.trim())
106
107
  .filter((part) => part.length > 0);
107
108
  }
108
- /** `createdAt:desc,email` as URL parameter, `[{ field, direction }]` once decoded. */
109
+ /** `created_at:desc,email` as URL parameter, `[{ field, direction }]` once decoded. */
109
110
  function orderByParam(orderBy) {
110
111
  // URL parameters are documented by their encoded side, so the description goes on the string.
111
112
  const encoded = Schema.String.annotate({
112
- description: "Comma-separated fields, each optionally followed by :asc or :desc, as in createdAt:desc,email.",
113
+ description: "Comma-separated fields, each optionally followed by :asc or :desc, as in created_at:desc,email.",
113
114
  });
114
115
  return encoded.pipe(Schema.decodeTo(orderBy, SchemaTransformation.transform({
115
116
  decode: (value) => splitList(value).map((part) => {
@@ -164,7 +165,7 @@ function schemasOf(plan, planned, recordOf) {
164
165
  const { manifest } = plan.ontology;
165
166
  const pascal = pascalCase(name);
166
167
  const record = recordOf(name);
167
- const query = entityQuerySchemas(manifest, name);
168
+ const query = entityQuerySchemas(manifest, name, true);
168
169
  const links = queryLinks(manifest, name);
169
170
  const includedFields = {};
170
171
  for (const link of links) {
@@ -181,13 +182,13 @@ function schemasOf(plan, planned, recordOf) {
181
182
  record,
182
183
  result,
183
184
  query: query.query,
184
- queryParams: {
185
+ queryParams: wireStruct({
185
186
  where: Schema.optionalKey(whereParam(query.where)),
186
187
  orderBy: Schema.optionalKey(orderByParam(query.orderBy)),
187
188
  include: Schema.optionalKey(includeParam(query.include)),
188
189
  after: Schema.optionalKey(query.after),
189
190
  limit: Schema.optionalKey(query.limit),
190
- },
191
+ }, { orderBy: "order_by" }),
191
192
  includeParams: {
192
193
  include: Schema.optionalKey(includeParam(query.include)),
193
194
  },
@@ -196,7 +197,9 @@ function schemasOf(plan, planned, recordOf) {
196
197
  nextCursor: Schema.NullOr(Schema.String).annotate({
197
198
  description: "Pass as after to read the next page; null on the last page.",
198
199
  }),
199
- }).annotate({ identifier: `${pascal}Page` }),
200
+ })
201
+ .pipe(Schema.encodeKeys({ nextCursor: "next_cursor" }))
202
+ .annotate({ identifier: `${pascal}Page` }),
200
203
  };
201
204
  }
202
205
  const HistoryLimit = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 200 })).annotate({
@@ -204,7 +207,7 @@ const HistoryLimit = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 20
204
207
  });
205
208
  /** The query parameters of a record's history. */
206
209
  export const HistoryParams = {
207
- after: Schema.optionalKey(Schema.String.annotate({ description: "The nextCursor of the previous page." })),
210
+ after: Schema.optionalKey(Schema.String.annotate({ description: "The next_cursor of the previous page." })),
208
211
  limit: Schema.optionalKey(HistoryLimit),
209
212
  };
210
213
  export const ValidationIssue = RuntimeValidationIssue.annotate({ identifier: "ValidationIssue" });
@@ -212,7 +215,7 @@ export const Validation = Schema.Struct({
212
215
  ok: Schema.Boolean,
213
216
  issues: Schema.Array(ValidationIssue),
214
217
  }).annotate({ identifier: "Validation" });
215
- export const HistoryEntry = RuntimeHistoryEntry.annotate({
218
+ export const HistoryEntry = RuntimeHistoryEntry.pipe(Schema.encodeKeys({ recordId: "record_id", actionId: "action_id", principalId: "principal_id" })).annotate({
216
219
  identifier: "HistoryEntry",
217
220
  description: "What one action changed on the record: the previous and new value of each property, " +
218
221
  "relationship key or link.",
@@ -220,12 +223,14 @@ export const HistoryEntry = RuntimeHistoryEntry.annotate({
220
223
  export const HistoryPage = Schema.Struct({
221
224
  items: Schema.Array(HistoryEntry),
222
225
  nextCursor: Schema.NullOr(Schema.String),
223
- }).annotate({ identifier: "HistoryPage" });
224
- const ActionOptions = Schema.Struct({
226
+ })
227
+ .pipe(Schema.encodeKeys({ nextCursor: "next_cursor" }))
228
+ .annotate({ identifier: "HistoryPage" });
229
+ const ActionOptions = wireStruct({
225
230
  validateOnly: Schema.optionalKey(Schema.Boolean.annotate({
226
231
  description: "Validate the input and report issues without a write.",
227
232
  })),
228
- });
233
+ }, { validateOnly: "validate_only" });
229
234
  /** The body of `POST /<entity>/actions/<action>`, as documented. */
230
235
  export function actionRequest(input) {
231
236
  return Schema.Struct({ input, options: Schema.optionalKey(ActionOptions) });
@@ -233,7 +238,7 @@ export function actionRequest(input) {
233
238
  /** The body of `POST /<entity>/actions/<action>`, as decoded: `runAction` validates the input. */
234
239
  export const ActionEnvelope = closedStruct({
235
240
  input: Schema.Unknown,
236
- options: Schema.optionalKey(closedStruct({ validateOnly: Schema.optionalKey(Schema.Boolean) }, "Unknown option; use validateOnly")),
241
+ options: Schema.optionalKey(wireStruct({ validateOnly: Schema.optionalKey(Schema.Boolean) }, { validateOnly: "validate_only" })),
237
242
  }, "Unknown key; use input and options");
238
243
  export const ActionResponse = Schema.Struct({
239
244
  result: Schema.Unknown.annotate({ description: "What the action returned." }),
@@ -242,7 +247,7 @@ export const ActionResponse = Schema.Struct({
242
247
  description: "The records the action changed, as their history records each change.",
243
248
  }),
244
249
  }).annotate({ identifier: "ActionResponse" });
245
- export const Catalog = OntologyManifest.annotate({ identifier: "Catalog" });
250
+ export const Catalog = OntologyManifestWire.annotate({ identifier: "Catalog" });
246
251
  const GraphEntity = Schema.Struct({
247
252
  name: Schema.String,
248
253
  label: Schema.String,
@@ -272,7 +277,9 @@ export const Status = Schema.Struct({
272
277
  description: "The manifest hash recorded by the last migration of the workspace database.",
273
278
  }),
274
279
  upToDate: Schema.Boolean.annotate({ description: "Whether the two hashes match." }),
275
- }).annotate({ identifier: "Status" });
280
+ })
281
+ .pipe(Schema.encodeKeys({ appliedHash: "applied_hash", upToDate: "up_to_date" }))
282
+ .annotate({ identifier: "Status" });
276
283
  /** The longest address `POST /api/ontology/sign-in/check` reads; RFC 5321 allows 254. */
277
284
  export const SIGN_IN_CHECK_MAX_EMAIL_LENGTH = 320;
278
285
  /** `POST /api/ontology/sign-in/check`: the address a person wants to sign in with. */
@@ -321,7 +328,9 @@ function errorBody(identifier, status, codes, extra = {}) {
321
328
  detail: Schema.String,
322
329
  requestId: Schema.String.annotate({ description: "Quote it when reporting a problem." }),
323
330
  ...extra,
324
- }).annotate({ identifier, httpApiStatus: status });
331
+ })
332
+ .pipe(Schema.encodeKeys({ requestId: "request_id" }))
333
+ .annotate({ identifier, httpApiStatus: status });
325
334
  }
326
335
  export const ErrorSchemas = {
327
336
  invalidRequest: errorBody("InvalidRequest", 400, ["invalid_request", "invalid_json"], {
@@ -5,7 +5,7 @@ import { sqlServiceRuntime } from "../runtime/service-binding.js";
5
5
  import { ErrorResponsesLive, buildApi } from "./api.js";
6
6
  import { authenticationLayer, tokenVerifier } from "./authentication.js";
7
7
  import { OntologyServiceConfigError, } from "./config.js";
8
- import { RequestId, failureResponse, internalError, routeNotFound, toServerResponse, } from "./errors.js";
8
+ import { RequestId, failureResponse, internalError, errorBodyForWire, routeNotFound, toServerResponse, } from "./errors.js";
9
9
  import { deployedConnections, localConnections } from "./connections.js";
10
10
  import { handlersLayer } from "./handlers.js";
11
11
  import { planApi } from "./plan.js";
@@ -77,7 +77,7 @@ export function createOntologyService(ontology, config) {
77
77
  // The runtime could not be built or the router itself failed: log everything, answer little.
78
78
  console.error("ontology.request.failed", { requestId, error });
79
79
  const failure = internalError(requestId);
80
- response = Response.json(failure.body, { status: failure.status });
80
+ response = Response.json(errorBodyForWire(failure.body), { status: failure.status });
81
81
  }
82
82
  return decorate(response, requestId, hash);
83
83
  },
package/dist/user.d.ts CHANGED
@@ -13,8 +13,8 @@ export declare const User: import("./entity.js").DraftEntity<"user", {
13
13
  readonly email: import("effect/Schema").optionalKey<import("./property.js").Property<string, false, true>>;
14
14
  readonly kind: import("./property.js").Property<"member" | "service", false, false>;
15
15
  readonly active: import("./property.js").Property<boolean, true, false>;
16
- readonly identityProvider: import("effect/Schema").optionalKey<import("./property.js").Property<string, false, false>>;
17
- readonly identitySubject: import("effect/Schema").optionalKey<import("./property.js").Property<string, false, false>>;
18
- readonly lastSignInAt: import("effect/Schema").optionalKey<import("./property.js").Property<string, false, false>>;
16
+ readonly identity_provider: import("effect/Schema").optionalKey<import("./property.js").Property<string, false, false>>;
17
+ readonly identity_subject: import("effect/Schema").optionalKey<import("./property.js").Property<string, false, false>>;
18
+ readonly last_sign_in_at: import("effect/Schema").optionalKey<import("./property.js").Property<string, false, false>>;
19
19
  }, import("./entity.js").Relationships>;
20
20
  export type User = typeof User.Type;
package/dist/user.js CHANGED
@@ -41,24 +41,27 @@ export const User = entity("user", {
41
41
  managed: true,
42
42
  description: "Inactive users cannot sign in or run actions.",
43
43
  }),
44
- identityProvider: p.text({
44
+ identity_provider: p.text({
45
+ renamedFrom: "identityProvider",
45
46
  optional: true,
46
47
  managed: true,
47
48
  description: "Who signs the member in, for example neon-auth. Empty for services.",
48
49
  }),
49
- identitySubject: p.text({
50
+ identity_subject: p.text({
51
+ renamedFrom: "identitySubject",
50
52
  optional: true,
51
53
  managed: true,
52
54
  description: "The member's id at the identity provider: the subject of their token.",
53
55
  }),
54
56
  // Optional so that a table created before it gains the column without a backfill: a member
55
57
  // who has not signed in since simply has none.
56
- lastSignInAt: p.timestamp({
58
+ last_sign_in_at: p.timestamp({
59
+ renamedFrom: "lastSignInAt",
57
60
  optional: true,
58
61
  managed: true,
59
62
  label: "Last sign-in",
60
63
  description: "When the member last signed in to the app. Empty until they do.",
61
64
  }),
62
65
  },
63
- indexes: [{ properties: ["identityProvider", "identitySubject"], unique: true }],
66
+ indexes: [{ properties: ["identity_provider", "identity_subject"], unique: true }],
64
67
  });
@@ -1,4 +1,4 @@
1
- import { CAMEL_CASE_NAME } from "./names.js";
1
+ import { SNAKE_CASE_NAME } from "./names.js";
2
2
  const columnName = /^[A-Za-z_][A-Za-z0-9_]*$/;
3
3
  const types = new Set(["text", "integer", "number", "boolean", "date", "timestamp", "select"]);
4
4
  /** Cross-field validation runs after relationships have resolved their target entities. */
@@ -73,11 +73,11 @@ export function checkWarehouseSource(entities, report) {
73
73
  }
74
74
  for (const [name, measure] of Object.entries(measures)) {
75
75
  const field = `${path}.measures.${name}`;
76
- if (!CAMEL_CASE_NAME.test(name) ||
76
+ if (!SNAKE_CASE_NAME.test(name) ||
77
77
  properties.has(name) ||
78
78
  entity.relationships.some((r) => r.name === name) ||
79
79
  !measure.description?.trim()) {
80
- report("invalid-measure", field, "Measures require unique camelCase names and a description.");
80
+ report("invalid-measure", field, "Measures require unique snake_case names and a description.");
81
81
  }
82
82
  if (measure.unit !== undefined &&
83
83
  !["number", "currency", "ratio", "percent"].includes(measure.unit)) {
package/dist/wire.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Schema } from "effect";
2
+ /** A closed wire object with explicit key mappings and an unchanged decoded TypeScript shape. */
3
+ export declare function wireStruct<const F extends Schema.Struct.Fields, const M extends {
4
+ readonly [K in keyof F]?: string;
5
+ }>(fields: F, mapping: M): Schema.encodeKeys<Schema.Struct<F>, M>;
package/dist/wire.js ADDED
@@ -0,0 +1,21 @@
1
+ import { Schema, SchemaTransformation } from "effect";
2
+ /** A closed wire object with explicit key mappings and an unchanged decoded TypeScript shape. */
3
+ export function wireStruct(fields, mapping) {
4
+ const codec = Schema.Struct(fields).pipe(Schema.encodeKeys(mapping));
5
+ const encodedFields = Object.fromEntries(Object.entries(fields).map(([key, field]) => [
6
+ mapping[key] ?? key,
7
+ field,
8
+ ]));
9
+ const names = Object.keys(encodedFields).join("|");
10
+ const unknown = Schema.String.check(Schema.isPattern(new RegExp(`^(?!(?:${names})$)`)));
11
+ const reverse = Object.fromEntries(Object.entries(mapping).map(([key, value]) => [value, key]));
12
+ const rename = (value, keys) => Object.fromEntries(Object.entries(value).map(([key, child]) => [keys[key] ?? key, child]));
13
+ const encoded = Schema.StructWithRest(Schema.Struct(encodedFields), [
14
+ Schema.Record(unknown, Schema.Never.annotate({ message: "Unknown wire key" })),
15
+ ]);
16
+ const decoded = Schema.toType(codec);
17
+ return encoded.pipe(Schema.decodeTo(decoded, SchemaTransformation.transform({
18
+ decode: (value) => rename(value, reverse),
19
+ encode: (value) => rename(value, mapping),
20
+ })));
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@segmentstream-cloud/ontology",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Define a SegmentStream workspace app's ontology: entities, relationships, roles and actions, written with Effect.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "SegmentStream",