@liquidmetal-ai/drizzle 0.1.3 → 0.2.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.
- package/.turbo/turbo-lint.log +6 -0
- package/.turbo/turbo-test.log +6 -0
- package/dist/appify/build.d.ts +32 -2
- package/dist/appify/build.d.ts.map +1 -1
- package/dist/appify/build.js +166 -24
- package/dist/appify/build.test.js +75 -3
- package/dist/appify/validate.d.ts +2 -1
- package/dist/appify/validate.d.ts.map +1 -1
- package/dist/appify/validate.js +71 -2
- package/dist/appify/validate.test.js +74 -4
- package/dist/codestore.js +2 -2
- package/dist/liquidmetal/v1alpha1/catalog_connect.d.ts +81 -78
- package/dist/liquidmetal/v1alpha1/catalog_connect.d.ts.map +1 -1
- package/dist/liquidmetal/v1alpha1/catalog_connect.js +81 -78
- package/dist/liquidmetal/v1alpha1/catalog_pb.d.ts +572 -610
- package/dist/liquidmetal/v1alpha1/catalog_pb.d.ts.map +1 -1
- package/dist/liquidmetal/v1alpha1/catalog_pb.js +675 -741
- package/package.json +1 -1
- package/src/appify/build.test.ts +85 -3
- package/src/appify/build.ts +181 -25
- package/src/appify/validate.test.ts +77 -4
- package/src/appify/validate.ts +78 -1
- package/src/codestore.ts +2 -2
- package/src/liquidmetal/v1alpha1/catalog_connect.ts +81 -78
- package/src/liquidmetal/v1alpha1/catalog_pb.ts +872 -970
- package/.turbo/turbo-build.log +0 -30
package/package.json
CHANGED
package/src/appify/build.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
|
-
import { buildManifest } from './build.js';
|
|
2
|
+
import { buildManifest, valueOf } from './build.js';
|
|
3
3
|
import { Parser, Tokenizer } from './parse.js';
|
|
4
4
|
|
|
5
5
|
const CONFIG = `
|
|
@@ -9,7 +9,7 @@ application "my-app" {
|
|
|
9
9
|
service "my-service" {
|
|
10
10
|
route {
|
|
11
11
|
zone = "testymctestface.com"
|
|
12
|
-
|
|
12
|
+
domain = "testymctestface.com"
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
domain {
|
|
@@ -36,6 +36,10 @@ application "my-app" {
|
|
|
36
36
|
visibility = 'public'
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
smartbucket "my-smart-bucket" {
|
|
40
|
+
location_hint = "enam"
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
observer "my-observer" {
|
|
40
44
|
env "API_KEY" {}
|
|
41
45
|
source {
|
|
@@ -63,9 +67,15 @@ application "my-app" {
|
|
|
63
67
|
sql_database "my-database" {}
|
|
64
68
|
|
|
65
69
|
vector_index "my-index" {
|
|
66
|
-
|
|
70
|
+
dimensions = 768
|
|
67
71
|
metric = 'cosine'
|
|
68
72
|
}
|
|
73
|
+
|
|
74
|
+
task "my-task" {
|
|
75
|
+
type = "cron"
|
|
76
|
+
cron = "* * * * *"
|
|
77
|
+
visibility = 'protected'
|
|
78
|
+
}
|
|
69
79
|
}
|
|
70
80
|
`;
|
|
71
81
|
|
|
@@ -112,3 +122,75 @@ application "foo" {
|
|
|
112
122
|
},
|
|
113
123
|
]);
|
|
114
124
|
});
|
|
125
|
+
|
|
126
|
+
test('smartbucket parsing', () => {
|
|
127
|
+
const CONFIG = `
|
|
128
|
+
application "my-app" {
|
|
129
|
+
smartbucket "my-smart-bucket" {
|
|
130
|
+
location_hint = "enam"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
const tokenizer = new Tokenizer(CONFIG);
|
|
135
|
+
const parser = new Parser(tokenizer);
|
|
136
|
+
const ast = parser.parse();
|
|
137
|
+
expect(parser.errors).toEqual([]);
|
|
138
|
+
|
|
139
|
+
const [apps, errors] = buildManifest(ast);
|
|
140
|
+
expect(errors).toEqual([]);
|
|
141
|
+
|
|
142
|
+
// Check that we have exactly one application
|
|
143
|
+
expect(apps.length).toBe(1);
|
|
144
|
+
|
|
145
|
+
// Check that we have exactly one smartbucket
|
|
146
|
+
expect(apps[0]!.smartBucket.length).toBe(1);
|
|
147
|
+
|
|
148
|
+
// Check the smartbucket properties
|
|
149
|
+
const smartBucket = apps[0]!.smartBucket[0]!;
|
|
150
|
+
expect(valueOf(smartBucket.name)).toBe("my-smart-bucket");
|
|
151
|
+
expect(smartBucket.locationHint).toBeDefined();
|
|
152
|
+
expect(valueOf(smartBucket.locationHint!)).toBe("enam");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('smartbucket with invalid properties', () => {
|
|
156
|
+
const CONFIG = `
|
|
157
|
+
application "my-app" {
|
|
158
|
+
smartbucket "my-smart-bucket" {
|
|
159
|
+
location_hint = "enam"
|
|
160
|
+
invalid_property = "should fail"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
`;
|
|
164
|
+
const tokenizer = new Tokenizer(CONFIG);
|
|
165
|
+
const parser = new Parser(tokenizer);
|
|
166
|
+
const ast = parser.parse();
|
|
167
|
+
expect(parser.errors).toEqual([]);
|
|
168
|
+
|
|
169
|
+
const [, errors] = buildManifest(ast);
|
|
170
|
+
|
|
171
|
+
// Should have one error for the invalid property
|
|
172
|
+
expect(errors.length).toBe(1);
|
|
173
|
+
expect(errors[0]!.message).toContain('unexpected assignment');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('smartbucket with duplicate assignment', () => {
|
|
177
|
+
const CONFIG = `
|
|
178
|
+
application "foo" {
|
|
179
|
+
smartbucket "my-smart-bucket" {
|
|
180
|
+
location_hint = "enam"
|
|
181
|
+
location_hint = "other-location"
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
const tokenizer = new Tokenizer(CONFIG);
|
|
186
|
+
const parser = new Parser(tokenizer);
|
|
187
|
+
const ast = parser.parse();
|
|
188
|
+
expect(parser.errors).toEqual([]);
|
|
189
|
+
|
|
190
|
+
const [, errors] = buildManifest(ast);
|
|
191
|
+
expect(errors).toMatchObject([
|
|
192
|
+
{
|
|
193
|
+
message: 'duplicate location_hint assignment "other-location"',
|
|
194
|
+
},
|
|
195
|
+
]);
|
|
196
|
+
});
|
package/src/appify/build.ts
CHANGED
|
@@ -25,7 +25,7 @@ export function valueOf(token: TokenString | TokenNumber | TokenBoolean): string
|
|
|
25
25
|
if (token.type === 'string') {
|
|
26
26
|
return token.value.slice(1, -1);
|
|
27
27
|
}
|
|
28
|
-
throw new Error(`unexpected token type`);
|
|
28
|
+
throw new Error(`unexpected token type: ${token}`);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface ConfigObject {
|
|
@@ -161,6 +161,9 @@ export function buildApplication(node: StanzaNode): [Application | undefined, Co
|
|
|
161
161
|
case 'actor':
|
|
162
162
|
buildStanza(buildActor, child, app.actor, errors);
|
|
163
163
|
break;
|
|
164
|
+
case 'task':
|
|
165
|
+
buildStanza(buildTask, child, app.task, errors);
|
|
166
|
+
break;
|
|
164
167
|
case 'bucket':
|
|
165
168
|
buildStanza(buildBucket, child, app.bucket, errors);
|
|
166
169
|
break;
|
|
@@ -173,8 +176,14 @@ export function buildApplication(node: StanzaNode): [Application | undefined, Co
|
|
|
173
176
|
case 'sql_database':
|
|
174
177
|
buildStanza(buildSqlDatabase, child, app.sqlDatabase, errors);
|
|
175
178
|
break;
|
|
179
|
+
case 'kv_store':
|
|
180
|
+
buildStanza(buildKvStore, child, app.kvStore, errors);
|
|
181
|
+
break;
|
|
182
|
+
case 'smartbucket':
|
|
183
|
+
buildStanza(buildSmartBucket, child, app.smartBucket, errors);
|
|
184
|
+
break;
|
|
176
185
|
default:
|
|
177
|
-
errors.push({ message:
|
|
186
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
178
187
|
}
|
|
179
188
|
break;
|
|
180
189
|
case 'assignment':
|
|
@@ -215,7 +224,7 @@ function buildService(stanza: StanzaNode): [Service, ConfigError[]] {
|
|
|
215
224
|
buildStanza(buildEnv, child, service.env, errors);
|
|
216
225
|
break;
|
|
217
226
|
default:
|
|
218
|
-
errors.push({ message:
|
|
227
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
219
228
|
}
|
|
220
229
|
break;
|
|
221
230
|
case 'assignment':
|
|
@@ -245,13 +254,16 @@ function buildBucket(stanza: StanzaNode): [Bucket, ConfigError[]] {
|
|
|
245
254
|
for (const child of stanza.block?.children ?? []) {
|
|
246
255
|
switch (child.type) {
|
|
247
256
|
case 'stanza':
|
|
248
|
-
errors.push({ message:
|
|
257
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
249
258
|
break;
|
|
250
259
|
case 'assignment':
|
|
251
260
|
switch (child.key.value) {
|
|
252
261
|
case 'visibility':
|
|
253
262
|
buildAssignment(bucket, 'visibility', 'string', child, errors);
|
|
254
263
|
break;
|
|
264
|
+
case 'location_hint':
|
|
265
|
+
buildAssignment(bucket, 'locationHint', 'string', child, errors);
|
|
266
|
+
break;
|
|
255
267
|
default:
|
|
256
268
|
errors.push({ message: 'unexpected assignment', ...child });
|
|
257
269
|
}
|
|
@@ -274,7 +286,7 @@ function buildQueue(stanza: StanzaNode): [Queue, ConfigError[]] {
|
|
|
274
286
|
for (const child of stanza.block?.children ?? []) {
|
|
275
287
|
switch (child.type) {
|
|
276
288
|
case 'stanza':
|
|
277
|
-
errors.push({ message:
|
|
289
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
278
290
|
break;
|
|
279
291
|
case 'assignment':
|
|
280
292
|
switch (child.key.value) {
|
|
@@ -303,7 +315,7 @@ function buildVectorIndex(stanza: StanzaNode): [Bucket, ConfigError[]] {
|
|
|
303
315
|
for (const child of stanza.block?.children ?? []) {
|
|
304
316
|
switch (child.type) {
|
|
305
317
|
case 'stanza':
|
|
306
|
-
errors.push({ message:
|
|
318
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
307
319
|
break;
|
|
308
320
|
case 'assignment':
|
|
309
321
|
switch (child.key.value) {
|
|
@@ -338,7 +350,7 @@ function buildSqlDatabase(stanza: StanzaNode): [SqlDatabase, ConfigError[]] {
|
|
|
338
350
|
for (const child of stanza.block?.children ?? []) {
|
|
339
351
|
switch (child.type) {
|
|
340
352
|
case 'stanza':
|
|
341
|
-
errors.push({ message:
|
|
353
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
342
354
|
break;
|
|
343
355
|
case 'assignment':
|
|
344
356
|
switch (child.key.value) {
|
|
@@ -359,6 +371,62 @@ function buildSqlDatabase(stanza: StanzaNode): [SqlDatabase, ConfigError[]] {
|
|
|
359
371
|
return [sqlDatabase, errors];
|
|
360
372
|
}
|
|
361
373
|
|
|
374
|
+
function buildKvStore(stanza: StanzaNode): [KvStore, ConfigError[]] {
|
|
375
|
+
const errors: ConfigError[] = [];
|
|
376
|
+
const [name, nameErrors] = buildName1(stanza);
|
|
377
|
+
errors.push(...nameErrors);
|
|
378
|
+
const kvStore = new KvStore(name, stanza);
|
|
379
|
+
|
|
380
|
+
for (const child of stanza.block?.children ?? []) {
|
|
381
|
+
switch (child.type) {
|
|
382
|
+
case 'stanza':
|
|
383
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
384
|
+
break;
|
|
385
|
+
case 'assignment':
|
|
386
|
+
switch (child.key.value) {
|
|
387
|
+
case 'visibility':
|
|
388
|
+
buildAssignment(kvStore, 'visibility', 'string', child, errors);
|
|
389
|
+
break;
|
|
390
|
+
default:
|
|
391
|
+
errors.push({ message: 'unexpected assignment', ...child });
|
|
392
|
+
}
|
|
393
|
+
break;
|
|
394
|
+
case 'comment':
|
|
395
|
+
case 'newline':
|
|
396
|
+
break;
|
|
397
|
+
default:
|
|
398
|
+
errors.push({ message: `unexpected ${child.type}`, ...child });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return [kvStore, errors];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function buildSmartBucket(stanza: StanzaNode): [SmartBucket, ConfigError[]] {
|
|
405
|
+
const errors: ConfigError[] = [];
|
|
406
|
+
const [name, nameErrors] = buildName1(stanza);
|
|
407
|
+
errors.push(...nameErrors);
|
|
408
|
+
const smartBucket = new SmartBucket(name, stanza);
|
|
409
|
+
for (const child of stanza.block?.children ?? []) {
|
|
410
|
+
switch (child.type) {
|
|
411
|
+
case 'assignment':
|
|
412
|
+
switch (child.key.value) {
|
|
413
|
+
case 'location_hint':
|
|
414
|
+
buildAssignment(smartBucket, 'locationHint', 'string', child, errors);
|
|
415
|
+
break;
|
|
416
|
+
default:
|
|
417
|
+
errors.push({ message: 'unexpected assignment', ...child });
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
case 'comment':
|
|
421
|
+
case 'newline':
|
|
422
|
+
break;
|
|
423
|
+
default:
|
|
424
|
+
errors.push({ message: `unexpected ${child.type}`, ...child });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return [smartBucket, errors];
|
|
428
|
+
}
|
|
429
|
+
|
|
362
430
|
function buildObserver(stanza: StanzaNode): [Observer, ConfigError[]] {
|
|
363
431
|
const errors: ConfigError[] = [];
|
|
364
432
|
const [name, nameErrors] = buildName1(stanza);
|
|
@@ -380,7 +448,7 @@ function buildObserver(stanza: StanzaNode): [Observer, ConfigError[]] {
|
|
|
380
448
|
buildStanza(buildEnv, child, observer.env, errors);
|
|
381
449
|
break;
|
|
382
450
|
default:
|
|
383
|
-
errors.push({ message:
|
|
451
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
384
452
|
}
|
|
385
453
|
break;
|
|
386
454
|
case 'assignment':
|
|
@@ -414,7 +482,7 @@ function buildActor(stanza: StanzaNode): [Actor, ConfigError[]] {
|
|
|
414
482
|
buildStanza(buildEnv, child, actor.env, errors);
|
|
415
483
|
break;
|
|
416
484
|
default:
|
|
417
|
-
errors.push({ message:
|
|
485
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
418
486
|
}
|
|
419
487
|
break;
|
|
420
488
|
case 'assignment':
|
|
@@ -437,6 +505,48 @@ function buildActor(stanza: StanzaNode): [Actor, ConfigError[]] {
|
|
|
437
505
|
return [actor, errors];
|
|
438
506
|
}
|
|
439
507
|
|
|
508
|
+
function buildTask(stanza: StanzaNode): [Task, ConfigError[]] {
|
|
509
|
+
const errors: ConfigError[] = [];
|
|
510
|
+
const [name, nameErrors] = buildName1(stanza);
|
|
511
|
+
errors.push(...nameErrors);
|
|
512
|
+
const task = new Task(name, stanza);
|
|
513
|
+
const children = stanza.block ? stanza.block.children : [];
|
|
514
|
+
|
|
515
|
+
for (const child of children) {
|
|
516
|
+
switch (child.type) {
|
|
517
|
+
case 'stanza':
|
|
518
|
+
switch (child.name) {
|
|
519
|
+
case 'binding':
|
|
520
|
+
buildStanza(buildBinding, child, task.bindings, errors);
|
|
521
|
+
break;
|
|
522
|
+
case 'env':
|
|
523
|
+
buildStanza(buildEnv, child, task.env, errors);
|
|
524
|
+
break;
|
|
525
|
+
default:
|
|
526
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
case 'assignment':
|
|
530
|
+
switch (child.key.value) {
|
|
531
|
+
case 'type':
|
|
532
|
+
buildAssignment(task, 'type', 'string', child, errors);
|
|
533
|
+
break;
|
|
534
|
+
case 'cron':
|
|
535
|
+
buildAssignment(task, 'cron', 'string', child, errors);
|
|
536
|
+
break;
|
|
537
|
+
case 'visibility':
|
|
538
|
+
buildAssignment(task, 'visibility', 'string', child, errors);
|
|
539
|
+
break;
|
|
540
|
+
default:
|
|
541
|
+
errors.push({ message: 'unexpected assignment', ...child });
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return [task, errors];
|
|
548
|
+
}
|
|
549
|
+
|
|
440
550
|
function buildSource(stanza: StanzaNode): [Source, ConfigError[]] {
|
|
441
551
|
const errors: ConfigError[] = [];
|
|
442
552
|
const source = new Source(stanza);
|
|
@@ -486,13 +596,13 @@ function buildSource(stanza: StanzaNode): [Source, ConfigError[]] {
|
|
|
486
596
|
buildAssignment(rule, 'suffix', 'string', ruleChild, errors);
|
|
487
597
|
break;
|
|
488
598
|
default:
|
|
489
|
-
errors.push({ message:
|
|
599
|
+
errors.push({ message: 'unexpected assignment', ...ruleChild });
|
|
490
600
|
}
|
|
491
601
|
}
|
|
492
602
|
}
|
|
493
603
|
source.rule.push(rule);
|
|
494
604
|
} else {
|
|
495
|
-
errors.push({ message:
|
|
605
|
+
errors.push({ message: 'unexpected stanza', ...child });
|
|
496
606
|
}
|
|
497
607
|
}
|
|
498
608
|
}
|
|
@@ -508,26 +618,26 @@ function buildRoute(stanza: StanzaNode): [Route | undefined, ConfigError[]] {
|
|
|
508
618
|
for (const child of stanza.block?.children ?? []) {
|
|
509
619
|
if (child.type === 'assignment') {
|
|
510
620
|
switch (child.key.value) {
|
|
621
|
+
case 'cname':
|
|
622
|
+
buildAssignment(route, 'cname', 'string', child, errors);
|
|
623
|
+
break;
|
|
624
|
+
case 'domain':
|
|
625
|
+
buildAssignment(route, 'domain', 'string', child, errors);
|
|
626
|
+
break;
|
|
627
|
+
case 'path':
|
|
628
|
+
buildAssignment(route, 'path', 'string', child, errors);
|
|
629
|
+
break;
|
|
511
630
|
case 'zone':
|
|
512
631
|
buildAssignment(route, 'zone', 'string', child, errors);
|
|
513
632
|
break;
|
|
514
|
-
case 'pattern':
|
|
515
|
-
buildAssignment(route, 'pattern', 'string', child, errors);
|
|
516
|
-
break;
|
|
517
633
|
default:
|
|
518
634
|
errors.push({ message: `unexpected assignment ${child.key}`, ...child });
|
|
519
635
|
}
|
|
520
636
|
}
|
|
521
637
|
}
|
|
522
|
-
if (
|
|
523
|
-
errors.push({ message: '
|
|
524
|
-
return [undefined, errors];
|
|
525
|
-
}
|
|
526
|
-
if (!route.pattern) {
|
|
527
|
-
errors.push({ message: 'missing pattern assignment', ...stanza });
|
|
528
|
-
return [undefined, errors];
|
|
638
|
+
if (route.cname && route.domain) {
|
|
639
|
+
errors.push({ message: 'cname cannot be used with domain', ...stanza });
|
|
529
640
|
}
|
|
530
|
-
// TODO [ian]: validate zone and pattern contents.
|
|
531
641
|
return [route, errors];
|
|
532
642
|
}
|
|
533
643
|
|
|
@@ -605,11 +715,14 @@ export class Application {
|
|
|
605
715
|
service: Service[] = [];
|
|
606
716
|
observer: Observer[] = [];
|
|
607
717
|
actor: Actor[] = [];
|
|
718
|
+
task: Task[] = [];
|
|
608
719
|
bucket: Bucket[] = [];
|
|
609
720
|
queue: Queue[] = [];
|
|
610
721
|
env: Env[] = [];
|
|
611
722
|
sqlDatabase: SqlDatabase[] = [];
|
|
612
723
|
vectorIndex: VectorIndex[] = [];
|
|
724
|
+
kvStore: KvStore[] = [];
|
|
725
|
+
smartBucket: SmartBucket[] = [];
|
|
613
726
|
|
|
614
727
|
constructor(name: TokenString, obj: ConfigObject) {
|
|
615
728
|
this.name = name;
|
|
@@ -617,8 +730,8 @@ export class Application {
|
|
|
617
730
|
}
|
|
618
731
|
|
|
619
732
|
// Return all objects that require code handlers.
|
|
620
|
-
handlers(): (Service | Observer | Actor)[] {
|
|
621
|
-
return [...this.service, ...this.observer, ...this.actor];
|
|
733
|
+
handlers(): (Service | Observer | Actor | Task)[] {
|
|
734
|
+
return [...this.service, ...this.observer, ...this.actor, ...this.task];
|
|
622
735
|
}
|
|
623
736
|
}
|
|
624
737
|
|
|
@@ -650,6 +763,21 @@ export class Actor {
|
|
|
650
763
|
}
|
|
651
764
|
}
|
|
652
765
|
|
|
766
|
+
export class Task {
|
|
767
|
+
obj: ConfigObject;
|
|
768
|
+
name: TokenString;
|
|
769
|
+
type?: TokenString;
|
|
770
|
+
cron?: TokenString;
|
|
771
|
+
bindings: Binding[] = [];
|
|
772
|
+
env: Env[] = [];
|
|
773
|
+
visibility?: TokenString;
|
|
774
|
+
|
|
775
|
+
constructor(name: TokenString, obj: ConfigObject) {
|
|
776
|
+
this.name = name;
|
|
777
|
+
this.obj = obj;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
653
781
|
export class Observer {
|
|
654
782
|
obj: ConfigObject;
|
|
655
783
|
name: TokenString;
|
|
@@ -694,7 +822,9 @@ export class Rule {
|
|
|
694
822
|
export class Route {
|
|
695
823
|
obj: ConfigObject;
|
|
696
824
|
zone?: TokenString;
|
|
697
|
-
|
|
825
|
+
domain?: TokenString;
|
|
826
|
+
cname?: TokenString;
|
|
827
|
+
path?: TokenString;
|
|
698
828
|
|
|
699
829
|
constructor(obj: ConfigObject) {
|
|
700
830
|
this.obj = obj;
|
|
@@ -734,6 +864,18 @@ export class Env {
|
|
|
734
864
|
}
|
|
735
865
|
|
|
736
866
|
export class Bucket {
|
|
867
|
+
name: TokenString;
|
|
868
|
+
visibility?: TokenString;
|
|
869
|
+
locationHint?: TokenString;
|
|
870
|
+
obj: ConfigObject;
|
|
871
|
+
|
|
872
|
+
constructor(name: TokenString, obj: ConfigObject) {
|
|
873
|
+
this.name = name;
|
|
874
|
+
this.obj = obj;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
export class KvStore {
|
|
737
879
|
name: TokenString;
|
|
738
880
|
visibility?: TokenString;
|
|
739
881
|
obj: ConfigObject;
|
|
@@ -780,5 +922,19 @@ export class SqlDatabase {
|
|
|
780
922
|
}
|
|
781
923
|
}
|
|
782
924
|
|
|
925
|
+
export class SmartBucket {
|
|
926
|
+
name: TokenString;
|
|
927
|
+
locationHint?: TokenString;
|
|
928
|
+
obj: ConfigObject;
|
|
929
|
+
|
|
930
|
+
constructor(name: TokenString, obj: ConfigObject) {
|
|
931
|
+
this.name = name;
|
|
932
|
+
this.obj = obj;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
783
936
|
export const VISIBILITIES = ['none', 'public', 'private', 'protected', 'application', 'suite', 'tenant'] as const;
|
|
784
937
|
export type Visibility = (typeof VISIBILITIES)[number];
|
|
938
|
+
|
|
939
|
+
export const TASK_TYPES = ['cron'] as const;
|
|
940
|
+
export type TaskType = (typeof TASK_TYPES)[number];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from 'vitest';
|
|
1
|
+
import { expect, test, describe, it } from 'vitest';
|
|
2
2
|
import { buildManifest } from './build.js';
|
|
3
3
|
import { Parser, Tokenizer } from './parse.js';
|
|
4
4
|
import { validate, VALIDATORS } from './validate.js';
|
|
@@ -11,6 +11,10 @@ application "my-app" {
|
|
|
11
11
|
observer "my-observer" {}
|
|
12
12
|
bucket "my-bucket" {}
|
|
13
13
|
queue "my-queue" {}
|
|
14
|
+
task "my-cron" {
|
|
15
|
+
type = "cron"
|
|
16
|
+
cron = "* * * * *"
|
|
17
|
+
}
|
|
14
18
|
}
|
|
15
19
|
`;
|
|
16
20
|
const tokenizer = new Tokenizer(manifest);
|
|
@@ -280,7 +284,7 @@ application "my-app" {
|
|
|
280
284
|
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
281
285
|
expect(validateErrors).toMatchObject([
|
|
282
286
|
{
|
|
283
|
-
message: 'vector index must have a
|
|
287
|
+
message: 'vector index must have a dimensions',
|
|
284
288
|
},
|
|
285
289
|
{
|
|
286
290
|
message: 'vector index metric must be either cosine or euclidean',
|
|
@@ -293,7 +297,7 @@ test('validate vector index parameters', async () => {
|
|
|
293
297
|
application "my-app" {
|
|
294
298
|
service "my-service" {}
|
|
295
299
|
vector_index "my-index" {
|
|
296
|
-
|
|
300
|
+
dimensions = 128.9
|
|
297
301
|
metric = "cosine"
|
|
298
302
|
}
|
|
299
303
|
}
|
|
@@ -305,7 +309,7 @@ application "my-app" {
|
|
|
305
309
|
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
306
310
|
expect(validateErrors).toMatchObject([
|
|
307
311
|
{
|
|
308
|
-
message: 'vector index
|
|
312
|
+
message: 'vector index dimensions must be an integer',
|
|
309
313
|
},
|
|
310
314
|
]);
|
|
311
315
|
});
|
|
@@ -433,3 +437,72 @@ application "my-app" {
|
|
|
433
437
|
},
|
|
434
438
|
]);
|
|
435
439
|
});
|
|
440
|
+
|
|
441
|
+
describe('task validator', async () => {
|
|
442
|
+
it('validates task types', async () => {
|
|
443
|
+
const manifest = `
|
|
444
|
+
application "my-app" {
|
|
445
|
+
task "my-task" {
|
|
446
|
+
type = "thingie" // not a valid task type
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
`;
|
|
450
|
+
const tokenizer = new Tokenizer(manifest);
|
|
451
|
+
const parser = new Parser(tokenizer);
|
|
452
|
+
const parsedManifest = parser.parse();
|
|
453
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
454
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
455
|
+
expect(validateErrors).toMatchObject([
|
|
456
|
+
{
|
|
457
|
+
message: 'task type must be one of: cron',
|
|
458
|
+
},
|
|
459
|
+
]);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('validates task cron expressions', async () => {
|
|
463
|
+
const manifest = `
|
|
464
|
+
application "my-app" {
|
|
465
|
+
task "my-task" {
|
|
466
|
+
type = "cron"
|
|
467
|
+
cron = "* * * * */30"
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
`;
|
|
471
|
+
const tokenizer = new Tokenizer(manifest);
|
|
472
|
+
const parser = new Parser(tokenizer);
|
|
473
|
+
const parsedManifest = parser.parse();
|
|
474
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
475
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
476
|
+
expect(validateErrors).toMatchObject([]);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('invalidates task cron expressions', async () => {
|
|
480
|
+
const invalidManifests = [
|
|
481
|
+
`
|
|
482
|
+
application "my-app" {
|
|
483
|
+
task "my-task" {
|
|
484
|
+
type = "cron"
|
|
485
|
+
cron = "* * * *" // invalid, should have 5 parts
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
`,
|
|
489
|
+
`
|
|
490
|
+
application "my-app" {
|
|
491
|
+
task "my-task" {
|
|
492
|
+
type = "cron"
|
|
493
|
+
cron = "* * * * x" // invalid last part
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
`,
|
|
497
|
+
];
|
|
498
|
+
for (const manifest of invalidManifests) {
|
|
499
|
+
const tokenizer = new Tokenizer(manifest);
|
|
500
|
+
const parser = new Parser(tokenizer);
|
|
501
|
+
const parsedManifest = parser.parse();
|
|
502
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
503
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
504
|
+
expect(validateErrors.length).toBe(1);
|
|
505
|
+
expect(validateErrors[0]?.message).toContain('task cron expression is malformed');
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
});
|