@liquidmetal-ai/drizzle 0.0.1

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 (76) hide show
  1. package/.changeset/README.md +4 -0
  2. package/.changeset/config.json +11 -0
  3. package/.changeset/odd-plums-dress.md +7 -0
  4. package/.turbo/turbo-build.log +6 -0
  5. package/dist/appify/build.d.ts +130 -0
  6. package/dist/appify/build.d.ts.map +1 -0
  7. package/dist/appify/build.js +703 -0
  8. package/dist/appify/build.test.d.ts +2 -0
  9. package/dist/appify/build.test.d.ts.map +1 -0
  10. package/dist/appify/build.test.js +111 -0
  11. package/dist/appify/index.d.ts +8 -0
  12. package/dist/appify/index.d.ts.map +1 -0
  13. package/dist/appify/index.js +28 -0
  14. package/dist/appify/index.test.d.ts +2 -0
  15. package/dist/appify/index.test.d.ts.map +1 -0
  16. package/dist/appify/index.test.js +40 -0
  17. package/dist/appify/parse.d.ts +151 -0
  18. package/dist/appify/parse.d.ts.map +1 -0
  19. package/dist/appify/parse.js +579 -0
  20. package/dist/appify/parse.test.d.ts +2 -0
  21. package/dist/appify/parse.test.d.ts.map +1 -0
  22. package/dist/appify/parse.test.js +319 -0
  23. package/dist/appify/validate.d.ts +22 -0
  24. package/dist/appify/validate.d.ts.map +1 -0
  25. package/dist/appify/validate.js +346 -0
  26. package/dist/appify/validate.test.d.ts +2 -0
  27. package/dist/appify/validate.test.d.ts.map +1 -0
  28. package/dist/appify/validate.test.js +327 -0
  29. package/dist/codestore.d.ts +34 -0
  30. package/dist/codestore.d.ts.map +1 -0
  31. package/dist/codestore.js +54 -0
  32. package/dist/codestore.test.d.ts +2 -0
  33. package/dist/codestore.test.d.ts.map +1 -0
  34. package/dist/codestore.test.js +84 -0
  35. package/dist/liquidmetal/v1alpha1/catalog_connect.d.ts +147 -0
  36. package/dist/liquidmetal/v1alpha1/catalog_connect.d.ts.map +1 -0
  37. package/dist/liquidmetal/v1alpha1/catalog_connect.js +150 -0
  38. package/dist/liquidmetal/v1alpha1/catalog_pb.d.ts +965 -0
  39. package/dist/liquidmetal/v1alpha1/catalog_pb.d.ts.map +1 -0
  40. package/dist/liquidmetal/v1alpha1/catalog_pb.js +1486 -0
  41. package/dist/liquidmetal/v1alpha1/rainbow_auth_connect.d.ts +49 -0
  42. package/dist/liquidmetal/v1alpha1/rainbow_auth_connect.d.ts.map +1 -0
  43. package/dist/liquidmetal/v1alpha1/rainbow_auth_connect.js +52 -0
  44. package/dist/liquidmetal/v1alpha1/rainbow_auth_pb.d.ts +271 -0
  45. package/dist/liquidmetal/v1alpha1/rainbow_auth_pb.d.ts.map +1 -0
  46. package/dist/liquidmetal/v1alpha1/rainbow_auth_pb.js +381 -0
  47. package/dist/liquidmetal/v1alpha1/raindrop_pb.d.ts +38 -0
  48. package/dist/liquidmetal/v1alpha1/raindrop_pb.d.ts.map +1 -0
  49. package/dist/liquidmetal/v1alpha1/raindrop_pb.js +52 -0
  50. package/dist/unsafe/codestore.d.ts +10 -0
  51. package/dist/unsafe/codestore.d.ts.map +1 -0
  52. package/dist/unsafe/codestore.js +23 -0
  53. package/dist/unsafe/codestore.test.d.ts +2 -0
  54. package/dist/unsafe/codestore.test.d.ts.map +1 -0
  55. package/dist/unsafe/codestore.test.js +27 -0
  56. package/eslint.config.mjs +4 -0
  57. package/package.json +45 -0
  58. package/src/appify/build.test.ts +116 -0
  59. package/src/appify/build.ts +783 -0
  60. package/src/appify/index.test.ts +43 -0
  61. package/src/appify/index.ts +33 -0
  62. package/src/appify/parse.test.ts +337 -0
  63. package/src/appify/parse.ts +744 -0
  64. package/src/appify/validate.test.ts +341 -0
  65. package/src/appify/validate.ts +435 -0
  66. package/src/codestore.test.ts +98 -0
  67. package/src/codestore.ts +93 -0
  68. package/src/liquidmetal/v1alpha1/catalog_connect.ts +153 -0
  69. package/src/liquidmetal/v1alpha1/catalog_pb.ts +1827 -0
  70. package/src/liquidmetal/v1alpha1/rainbow_auth_connect.ts +55 -0
  71. package/src/liquidmetal/v1alpha1/rainbow_auth_pb.ts +476 -0
  72. package/src/liquidmetal/v1alpha1/raindrop_pb.ts +63 -0
  73. package/src/unsafe/codestore.test.ts +34 -0
  74. package/src/unsafe/codestore.ts +29 -0
  75. package/tsconfig.json +31 -0
  76. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,783 @@
1
+ import {
2
+ ArrayNode,
3
+ AssignmentNode,
4
+ BlockNode,
5
+ fmt,
6
+ NodeType,
7
+ StanzaNode,
8
+ TokenBoolean,
9
+ TokenNumber,
10
+ TokenString,
11
+ } from './parse.js';
12
+
13
+ export function valueOf(token: TokenString): string;
14
+ export function valueOf(token: TokenNumber): number;
15
+ export function valueOf(token: TokenBoolean): boolean;
16
+
17
+ // valueOf extracts the value of a token in the appropriate format.
18
+ export function valueOf(token: TokenString | TokenNumber | TokenBoolean): string | number | boolean {
19
+ if (token.type === 'number') {
20
+ return Number(token.value);
21
+ }
22
+ if (token.type === 'boolean') {
23
+ return token.value == 'true';
24
+ }
25
+ if (token.type === 'string') {
26
+ return token.value.slice(1, -1);
27
+ }
28
+ throw new Error(`unexpected token type`);
29
+ }
30
+
31
+ export interface ConfigObject {
32
+ line: number;
33
+ column: number;
34
+ start: number;
35
+ end: number;
36
+ }
37
+
38
+ export class ConfigError {
39
+ message: string;
40
+ line: number;
41
+ column: number;
42
+ start: number;
43
+ end: number;
44
+
45
+ constructor(message: string, obj: ConfigObject) {
46
+ this.message = message;
47
+ this.line = obj.line;
48
+ this.column = obj.column;
49
+ this.start = obj.start;
50
+ this.end = obj.end;
51
+ }
52
+ }
53
+
54
+ export function buildManifest(node: BlockNode): [Application[], ConfigError[]] {
55
+ const applications: Application[] = [];
56
+ const errors: ConfigError[] = [];
57
+
58
+ for (const child of node.children) {
59
+ switch (child.type) {
60
+ case 'stanza':
61
+ if (child.name === 'application') {
62
+ buildStanza(buildApplication, child, applications, errors);
63
+ } else {
64
+ errors.push({ message: 'expected application', ...child });
65
+ }
66
+ break;
67
+ case 'comment':
68
+ case 'newline':
69
+ break;
70
+ default:
71
+ errors.push({ message: 'expected application', ...child });
72
+ }
73
+ }
74
+ return [applications, errors];
75
+ }
76
+
77
+ // buildName1 is a helper function for parsing a name from a stanza
78
+ // node arguments and asserting there should only be 1, string
79
+ // argument.
80
+ function buildName1(node: StanzaNode): [TokenString, ConfigError[]] {
81
+ const errors: ConfigError[] = [];
82
+ const invalid = {
83
+ type: 'string',
84
+ value: '"<invalid>"',
85
+ line: node.line,
86
+ column: node.column,
87
+ start: node.start,
88
+ end: node.block?.start || node.end,
89
+ } as TokenString;
90
+ if (node.args.length === 0) {
91
+ errors.push({ message: 'expected name', ...node });
92
+ return [invalid, errors];
93
+ }
94
+ if (node.args.length > 1) {
95
+ errors.push({ message: 'unexpected additional args', ...node });
96
+ }
97
+ if (node.args[0]?.type !== 'string') {
98
+ errors.push({ message: 'expected string', ...node.args[0]! });
99
+ return [invalid, errors];
100
+ }
101
+ return [node.args[0], errors];
102
+ }
103
+
104
+ // buildStanza is a helper to build different stanzas into a parent
105
+ // object.
106
+ function buildStanza<T>(
107
+ handler: (child: StanzaNode) => [T | undefined, ConfigError[]],
108
+ child: StanzaNode,
109
+ into: T[],
110
+ errors: ConfigError[],
111
+ ) {
112
+ const [buildItem, buildErrors] = handler(child);
113
+ if (buildItem) {
114
+ into.push(buildItem);
115
+ }
116
+ errors.push(...buildErrors);
117
+ }
118
+
119
+ // buildAssignment is a helper to build assignments into an object. It
120
+ // validates type, throws errors on duplicate assignment, and makes
121
+ // sure the field exists on the object and is the right type.
122
+ function buildAssignment<T>(obj: T, field: keyof T, expected: NodeType, child: AssignmentNode, errors: ConfigError[]) {
123
+ if (obj[field]) {
124
+ // FYI: field name in manifest is not necessarily field name on IR
125
+ // because of snake_case vs camelCase. So we use the left-hand
126
+ // side of the assignment in the error.
127
+ errors.push({ message: `duplicate ${fmt(child.key)} assignment ${fmt(child.value)}`, ...child.key });
128
+ }
129
+ if (child.value.type !== expected) {
130
+ errors.push({ message: `expected ${expected}`, ...child.value });
131
+ } else {
132
+ // We could get fancy with the type system, but for the sake of
133
+ // GTD, just cast because we know we're the right type.
134
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
+ obj[field] = child.value as any;
136
+ }
137
+ }
138
+
139
+ export function buildApplication(node: StanzaNode): [Application | undefined, ConfigError[]] {
140
+ const errors: ConfigError[] = [];
141
+ // Build out the application name.
142
+ const [name, nameErrors] = buildName1(node);
143
+ errors.push(...nameErrors);
144
+ const app = new Application(name, node);
145
+ const children = node.block ? node.block.children : [];
146
+
147
+ // Iterate over child nodes
148
+ for (const child of children) {
149
+ switch (child.type) {
150
+ case 'stanza':
151
+ switch (child.name) {
152
+ case 'service':
153
+ buildStanza(buildService, child, app.service, errors);
154
+ break;
155
+ case 'env':
156
+ buildStanza(buildEnv, child, app.env, errors);
157
+ break;
158
+ case 'observer':
159
+ buildStanza(buildObserver, child, app.observer, errors);
160
+ break;
161
+ case 'actor':
162
+ buildStanza(buildActor, child, app.actor, errors);
163
+ break;
164
+ case 'bucket':
165
+ buildStanza(buildBucket, child, app.bucket, errors);
166
+ break;
167
+ case 'queue':
168
+ buildStanza(buildQueue, child, app.queue, errors);
169
+ break;
170
+ case 'vector_index':
171
+ buildStanza(buildVectorIndex, child, app.vectorIndex, errors);
172
+ break;
173
+ case 'sql_database':
174
+ buildStanza(buildSqlDatabase, child, app.sqlDatabase, errors);
175
+ break;
176
+ default:
177
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
178
+ }
179
+ break;
180
+ case 'assignment':
181
+ errors.push({ message: 'unexpected assignment', ...child });
182
+ break;
183
+ case 'comment':
184
+ break;
185
+ case 'newline':
186
+ break;
187
+ default:
188
+ errors.push({ message: `unexpected ${child.type}`, ...child });
189
+ }
190
+ }
191
+ return [app, errors];
192
+ }
193
+
194
+ function buildService(stanza: StanzaNode): [Service, ConfigError[]] {
195
+ const errors: ConfigError[] = [];
196
+ const [name, nameErrors] = buildName1(stanza);
197
+ errors.push(...nameErrors);
198
+ const service = new Service(name, stanza);
199
+ const children = stanza.block ? stanza.block.children : [];
200
+
201
+ for (const child of children) {
202
+ switch (child.type) {
203
+ case 'stanza':
204
+ switch (child.name) {
205
+ case 'route':
206
+ buildStanza(buildRoute, child, service.routes, errors);
207
+ break;
208
+ case 'domain':
209
+ buildStanza(buildDomain, child, service.domains, errors);
210
+ break;
211
+ case 'binding':
212
+ buildStanza(buildBinding, child, service.bindings, errors);
213
+ break;
214
+ case 'env':
215
+ buildStanza(buildEnv, child, service.env, errors);
216
+ break;
217
+ default:
218
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
219
+ }
220
+ break;
221
+ case 'assignment':
222
+ switch (child.key.value) {
223
+ case 'visibility':
224
+ buildAssignment(service, 'visibility', 'string', child, errors);
225
+ break;
226
+ default:
227
+ errors.push({ message: 'unexpected assignment', ...child });
228
+ }
229
+ break;
230
+ case 'comment':
231
+ case 'newline':
232
+ break;
233
+ default:
234
+ errors.push({ message: 'unexpected node', ...child });
235
+ }
236
+ }
237
+ return [service, errors];
238
+ }
239
+
240
+ function buildBucket(stanza: StanzaNode): [Bucket, ConfigError[]] {
241
+ const errors: ConfigError[] = [];
242
+ const [name, nameErrors] = buildName1(stanza);
243
+ errors.push(...nameErrors);
244
+ const bucket = new Bucket(name, stanza);
245
+ for (const child of stanza.block?.children ?? []) {
246
+ switch (child.type) {
247
+ case 'stanza':
248
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
249
+ break;
250
+ case 'assignment':
251
+ switch (child.key.value) {
252
+ case 'visibility':
253
+ buildAssignment(bucket, 'visibility', 'string', child, errors);
254
+ break;
255
+ default:
256
+ errors.push({ message: 'unexpected assignment', ...child });
257
+ }
258
+ break;
259
+ case 'comment':
260
+ case 'newline':
261
+ break;
262
+ default:
263
+ errors.push({ message: `unexpected ${child.type}`, ...child });
264
+ }
265
+ }
266
+ return [bucket, errors];
267
+ }
268
+
269
+ function buildQueue(stanza: StanzaNode): [Queue, ConfigError[]] {
270
+ const errors: ConfigError[] = [];
271
+ const [name, nameErrors] = buildName1(stanza);
272
+ errors.push(...nameErrors);
273
+ const queue = new Queue(name, stanza);
274
+ for (const child of stanza.block?.children ?? []) {
275
+ switch (child.type) {
276
+ case 'stanza':
277
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
278
+ break;
279
+ case 'assignment':
280
+ switch (child.key.value) {
281
+ case 'visibility':
282
+ buildAssignment(queue, 'visibility', 'string', child, errors);
283
+ break;
284
+ default:
285
+ errors.push({ message: 'unexpected assignment', ...child });
286
+ }
287
+ break;
288
+ case 'comment':
289
+ case 'newline':
290
+ break;
291
+ default:
292
+ errors.push({ message: `unexpected ${child.type}`, ...child });
293
+ }
294
+ }
295
+ return [queue, errors];
296
+ }
297
+
298
+ function buildVectorIndex(stanza: StanzaNode): [Bucket, ConfigError[]] {
299
+ const errors: ConfigError[] = [];
300
+ const [name, nameErrors] = buildName1(stanza);
301
+ errors.push(...nameErrors);
302
+ const bucket = new VectorIndex(name, stanza);
303
+ for (const child of stanza.block?.children ?? []) {
304
+ switch (child.type) {
305
+ case 'stanza':
306
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
307
+ break;
308
+ case 'assignment':
309
+ switch (child.key.value) {
310
+ case 'visibility':
311
+ buildAssignment(bucket, 'visibility', 'string', child, errors);
312
+ break;
313
+ case 'dimension':
314
+ buildAssignment(bucket, 'dimension', 'number', child, errors);
315
+ break;
316
+ case 'metric':
317
+ buildAssignment(bucket, 'metric', 'string', child, errors);
318
+ break;
319
+ default:
320
+ errors.push({ message: 'unexpected assignment', ...child });
321
+ }
322
+ break;
323
+ case 'comment':
324
+ case 'newline':
325
+ break;
326
+ default:
327
+ errors.push({ message: `unexpected ${child.type}`, ...child });
328
+ }
329
+ }
330
+ return [bucket, errors];
331
+ }
332
+
333
+ function buildSqlDatabase(stanza: StanzaNode): [SqlDatabase, ConfigError[]] {
334
+ const errors: ConfigError[] = [];
335
+ const [name, nameErrors] = buildName1(stanza);
336
+ errors.push(...nameErrors);
337
+ const sqlDatabase = new SqlDatabase(name, stanza);
338
+ for (const child of stanza.block?.children ?? []) {
339
+ switch (child.type) {
340
+ case 'stanza':
341
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
342
+ break;
343
+ case 'assignment':
344
+ switch (child.key.value) {
345
+ case 'visibility':
346
+ buildAssignment(sqlDatabase, 'visibility', 'string', child, errors);
347
+ break;
348
+ case 'schema':
349
+ buildAssignment(sqlDatabase, 'schema', 'string', child, errors);
350
+ break;
351
+ default:
352
+ errors.push({ message: 'unexpected assignment', ...child });
353
+ }
354
+ break;
355
+ case 'comment':
356
+ case 'newline':
357
+ break;
358
+ default:
359
+ errors.push({ message: `unexpected ${child.type}`, ...child });
360
+ }
361
+ }
362
+ return [sqlDatabase, errors];
363
+ }
364
+
365
+ function buildObserver(stanza: StanzaNode): [Observer, ConfigError[]] {
366
+ const errors: ConfigError[] = [];
367
+ const [name, nameErrors] = buildName1(stanza);
368
+ errors.push(...nameErrors);
369
+ const observer = new Observer(name as TokenString, stanza);
370
+ const children = stanza.block ? stanza.block.children : [];
371
+
372
+ for (const child of children) {
373
+ switch (child.type) {
374
+ case 'stanza':
375
+ switch (child.name) {
376
+ case 'source':
377
+ buildStanza(buildSource, child, observer.source, errors);
378
+ break;
379
+ case 'binding':
380
+ buildStanza(buildBinding, child, observer.bindings, errors);
381
+ break;
382
+ case 'env':
383
+ buildStanza(buildEnv, child, observer.env, errors);
384
+ break;
385
+ default:
386
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
387
+ }
388
+ break;
389
+ case 'assignment':
390
+ errors.push({ message: 'unexpected assignment', ...child });
391
+ break;
392
+ case 'comment':
393
+ case 'newline':
394
+ break;
395
+ default:
396
+ errors.push({ message: `unexpected ${child.type}`, ...child });
397
+ }
398
+ }
399
+ return [observer, errors];
400
+ }
401
+
402
+ function buildActor(stanza: StanzaNode): [Actor, ConfigError[]] {
403
+ const errors: ConfigError[] = [];
404
+ const [name, nameErrors] = buildName1(stanza);
405
+ errors.push(...nameErrors);
406
+ const actor = new Actor(name, stanza);
407
+ const children = stanza.block ? stanza.block.children : [];
408
+
409
+ for (const child of children) {
410
+ switch (child.type) {
411
+ case 'stanza':
412
+ switch (child.name) {
413
+ case 'binding':
414
+ buildStanza(buildBinding, child, actor.bindings, errors);
415
+ break;
416
+ case 'env':
417
+ buildStanza(buildEnv, child, actor.env, errors);
418
+ break;
419
+ default:
420
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
421
+ }
422
+ break;
423
+ case 'assignment':
424
+ switch (child.key.value) {
425
+ case 'visibility':
426
+ buildAssignment(actor, 'visibility', 'string', child, errors);
427
+ break;
428
+ default:
429
+ errors.push({ message: 'unexpected assignment', ...child });
430
+ }
431
+ break;
432
+ case 'comment':
433
+ break;
434
+ case 'newline':
435
+ break;
436
+ default:
437
+ errors.push({ message: `unexpected ${child.type}`, ...child });
438
+ }
439
+ }
440
+ return [actor, errors];
441
+ }
442
+
443
+ function buildSource(stanza: StanzaNode): [Source, ConfigError[]] {
444
+ const errors: ConfigError[] = [];
445
+ const source = new Source(stanza);
446
+ for (const child of stanza.block?.children ?? []) {
447
+ if (child.type === 'assignment') {
448
+ switch (child.key.value) {
449
+ case 'bucket':
450
+ buildAssignment(source, 'bucket', 'string', child, errors);
451
+ break;
452
+ case 'queue':
453
+ buildAssignment(source, 'queue', 'string', child, errors);
454
+ break;
455
+ case 'batch_size':
456
+ // Note the subtle difference between the field name in the
457
+ // IR and the manifest.
458
+ buildAssignment(source, 'batchSize', 'number', child, errors);
459
+ break;
460
+ case 'max_wait_time_ms':
461
+ // Note the subtle difference between the field name in the
462
+ // IR and the manifest.
463
+ buildAssignment(source, 'maxWaitTimeMs', 'number', child, errors);
464
+ break;
465
+ case 'max_retries':
466
+ // Note the subtle difference between the field name in the
467
+ // IR and the manifest.
468
+ buildAssignment(source, 'maxRetries', 'number', child, errors);
469
+ break;
470
+ default:
471
+ errors.push({ message: 'unexpected assignment', ...child });
472
+ }
473
+ } else if (child.type === 'stanza') {
474
+ if (child.name === 'rule') {
475
+ source.rule = source.rule ?? [];
476
+ const rule = new Rule(child);
477
+ for (const ruleChild of child.block?.children ?? []) {
478
+ if (ruleChild.type === 'stanza') {
479
+ errors.push({ message: 'unexpected stanza', ...ruleChild });
480
+ } else if (ruleChild.type === 'assignment') {
481
+ switch (ruleChild.key.value) {
482
+ case 'actions':
483
+ buildAssignment(rule, 'actions', 'array', ruleChild, errors);
484
+ break;
485
+ case 'prefix':
486
+ buildAssignment(rule, 'prefix', 'string', ruleChild, errors);
487
+ break;
488
+ case 'suffix':
489
+ buildAssignment(rule, 'suffix', 'string', ruleChild, errors);
490
+ break;
491
+ default:
492
+ errors.push({ message: `unexpected assignment ${ruleChild.key}`, ...ruleChild });
493
+ }
494
+ }
495
+ }
496
+ source.rule.push(rule);
497
+ } else {
498
+ errors.push({ message: `unexpected stanza ${child.name}`, ...child });
499
+ }
500
+ }
501
+ }
502
+ return [source, errors];
503
+ }
504
+
505
+ function buildRoute(stanza: StanzaNode): [Route | undefined, ConfigError[]] {
506
+ const errors: ConfigError[] = [];
507
+ if (stanza.args.length > 0) {
508
+ errors.push({ message: 'unexpected arguments/names', ...stanza });
509
+ }
510
+ const route = new Route(stanza);
511
+ for (const child of stanza.block?.children ?? []) {
512
+ if (child.type === 'assignment') {
513
+ switch (child.key.value) {
514
+ case 'zone':
515
+ buildAssignment(route, 'zone', 'string', child, errors);
516
+ break;
517
+ case 'pattern':
518
+ buildAssignment(route, 'pattern', 'string', child, errors);
519
+ break;
520
+ default:
521
+ errors.push({ message: `unexpected assignment ${child.key}`, ...child });
522
+ }
523
+ }
524
+ }
525
+ if (!route.zone) {
526
+ errors.push({ message: 'missing zone assignment', ...stanza });
527
+ return [undefined, errors];
528
+ }
529
+ if (!route.pattern) {
530
+ errors.push({ message: 'missing pattern assignment', ...stanza });
531
+ return [undefined, errors];
532
+ }
533
+ // TODO [ian]: validate zone and pattern contents.
534
+ return [route, errors];
535
+ }
536
+
537
+ function buildDomain(stanza: StanzaNode): [Domain, ConfigError[]] {
538
+ const errors: ConfigError[] = [];
539
+ if (stanza.args.length > 0) {
540
+ errors.push({ message: 'unexpected arguments/names', ...stanza });
541
+ }
542
+ const domain = new Domain(stanza);
543
+ for (const child of stanza.block?.children ?? []) {
544
+ if (child.type === 'assignment') {
545
+ switch (child.key.value) {
546
+ case 'fqdn':
547
+ buildAssignment(domain, 'fqdn', 'string', child, errors);
548
+ break;
549
+ default:
550
+ errors.push({ message: `unexpected assignment ${child.key}`, ...child });
551
+ }
552
+ }
553
+ }
554
+ return [domain, errors];
555
+ }
556
+
557
+ function buildBinding(stanza: StanzaNode): [Binding, ConfigError[]] {
558
+ const errors: ConfigError[] = [];
559
+ const bindings: AssignmentNode[] = [];
560
+ function hasBinding(key: string) {
561
+ return bindings.some((binding) => binding.key.value === key);
562
+ }
563
+ for (const child of stanza.block?.children ?? []) {
564
+ if (child.type === 'assignment') {
565
+ if (hasBinding(child.key.value)) {
566
+ errors.push({ message: `duplicate binding ${child.key.value}`, ...child.key });
567
+ }
568
+ if (child.value.type === 'string') {
569
+ bindings.push(child);
570
+ } else {
571
+ errors.push({ message: 'expected string', ...child.value });
572
+ }
573
+ }
574
+ }
575
+ // TODO [ian]: validate key and value contents.
576
+ return [new Binding(bindings, stanza), errors];
577
+ }
578
+
579
+ function buildEnv(stanza: StanzaNode): [Env | undefined, ConfigError[]] {
580
+ const errors: ConfigError[] = [];
581
+ const [name, nameErrors] = buildName1(stanza);
582
+ errors.push(...nameErrors);
583
+ const env = new Env(name, stanza);
584
+ for (const child of stanza.block?.children ?? []) {
585
+ if (child.type === 'assignment') {
586
+ switch (child.key.value) {
587
+ case 'secret':
588
+ buildAssignment(env, 'secret', 'boolean', child, errors);
589
+ break;
590
+ case 'default':
591
+ buildAssignment(env, 'default', 'string', child, errors);
592
+ break;
593
+ default:
594
+ errors.push({ message: `unexpected assignment ${child.key}`, ...child });
595
+ }
596
+ }
597
+ }
598
+ return [env, errors];
599
+ }
600
+
601
+ export class Application {
602
+ obj: ConfigObject;
603
+ name: TokenString;
604
+
605
+ service: Service[] = [];
606
+ observer: Observer[] = [];
607
+ actor: Actor[] = [];
608
+ bucket: Bucket[] = [];
609
+ queue: Queue[] = [];
610
+ env: Env[] = [];
611
+ sqlDatabase: SqlDatabase[] = [];
612
+ vectorIndex: VectorIndex[] = [];
613
+
614
+ constructor(name: TokenString, obj: ConfigObject) {
615
+ this.name = name;
616
+ this.obj = obj;
617
+ }
618
+
619
+ // Return all objects that require code handlers.
620
+ handlers(): (Service | Observer | Actor)[] {
621
+ return [...this.service, ...this.observer, ...this.actor];
622
+ }
623
+ }
624
+
625
+ export class Service {
626
+ obj: ConfigObject;
627
+ name: TokenString;
628
+ visibility?: TokenString;
629
+ routes: Route[] = [];
630
+ domains: Domain[] = [];
631
+ bindings: Binding[] = [];
632
+ env: Env[] = [];
633
+
634
+ constructor(name: TokenString, obj: ConfigObject) {
635
+ this.name = name;
636
+ this.obj = obj;
637
+ }
638
+ }
639
+
640
+ export class Actor {
641
+ obj: ConfigObject;
642
+ name: TokenString;
643
+ bindings: Binding[] = [];
644
+ env: Env[] = [];
645
+ visibility?: TokenString;
646
+
647
+ constructor(name: TokenString, obj: ConfigObject) {
648
+ this.name = name;
649
+ this.obj = obj;
650
+ }
651
+ }
652
+
653
+ export class Observer {
654
+ obj: ConfigObject;
655
+ name: TokenString;
656
+ source: Source[] = [];
657
+ bindings: Binding[] = [];
658
+ env: Env[] = [];
659
+
660
+ constructor(name: TokenString, obj: ConfigObject) {
661
+ this.name = name;
662
+ this.obj = obj;
663
+ }
664
+ }
665
+
666
+ export class Source {
667
+ obj: ConfigObject;
668
+
669
+ bucket?: TokenString;
670
+ rule?: Rule[];
671
+
672
+ queue?: TokenString;
673
+
674
+ batchSize?: TokenNumber;
675
+ maxWaitTimeMs?: TokenNumber;
676
+ maxRetries?: TokenNumber;
677
+
678
+ constructor(obj: ConfigObject) {
679
+ this.obj = obj;
680
+ }
681
+ }
682
+
683
+ export class Rule {
684
+ obj: ConfigObject;
685
+ actions?: ArrayNode;
686
+ prefix?: TokenString;
687
+ suffix?: TokenString;
688
+
689
+ constructor(obj: ConfigObject) {
690
+ this.obj = obj;
691
+ }
692
+ }
693
+
694
+ export class Route {
695
+ obj: ConfigObject;
696
+ zone?: TokenString;
697
+ pattern?: TokenString;
698
+
699
+ constructor(obj: ConfigObject) {
700
+ this.obj = obj;
701
+ }
702
+ }
703
+
704
+ export class Domain {
705
+ obj: ConfigObject;
706
+ fqdn?: TokenString;
707
+
708
+ constructor(obj: ConfigObject) {
709
+ this.obj = obj;
710
+ }
711
+ }
712
+
713
+ export class Binding {
714
+ bindings: AssignmentNode[];
715
+ obj: ConfigObject;
716
+
717
+ constructor(bindings: AssignmentNode[], obj: ConfigObject) {
718
+ this.bindings = bindings;
719
+ this.obj = obj;
720
+ }
721
+ }
722
+
723
+ export class Env {
724
+ name: TokenString;
725
+ secret?: TokenBoolean;
726
+ default?: TokenString;
727
+ obj: ConfigObject;
728
+
729
+ constructor(name: TokenString, obj: ConfigObject) {
730
+ this.name = name;
731
+ this.obj = obj;
732
+ }
733
+ }
734
+
735
+ export class Bucket {
736
+ name: TokenString;
737
+ visibility?: TokenString;
738
+ obj: ConfigObject;
739
+
740
+ constructor(name: TokenString, obj: ConfigObject) {
741
+ this.name = name;
742
+ this.obj = obj;
743
+ }
744
+ }
745
+
746
+ export class Queue {
747
+ name: TokenString;
748
+ visibility?: TokenString;
749
+ obj: ConfigObject;
750
+
751
+ constructor(name: TokenString, obj: ConfigObject) {
752
+ this.name = name;
753
+ this.obj = obj;
754
+ }
755
+ }
756
+
757
+ export class VectorIndex {
758
+ name: TokenString;
759
+ visibility?: TokenString;
760
+ dimension?: TokenNumber;
761
+ metric?: TokenString;
762
+ obj: ConfigObject;
763
+
764
+ constructor(name: TokenString, obj: ConfigObject) {
765
+ this.name = name;
766
+ this.obj = obj;
767
+ }
768
+ }
769
+
770
+ export class SqlDatabase {
771
+ name: TokenString;
772
+ schema?: TokenString;
773
+ visibility?: TokenString;
774
+ obj: ConfigObject;
775
+
776
+ constructor(name: TokenString, obj: ConfigObject) {
777
+ this.name = name;
778
+ this.obj = obj;
779
+ }
780
+ }
781
+
782
+ export const VISIBILITIES = ['none', 'public', 'private', 'protected', 'application', 'suite', 'tenant'] as const;
783
+ export type Visibility = (typeof VISIBILITIES)[number];