@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,319 @@
1
+ import { expect, test } from 'vitest';
2
+ import { JSONTranslator, Parser, Tokenizer, fmt } from './parse.js';
3
+ const CONFIG = `
4
+ application "my-app" {
5
+ service "my-service" {
6
+ domain {
7
+ fqdn = "testymctestface.com"
8
+ }
9
+
10
+ binding {
11
+ // This is a binding of foo to "bar".
12
+ foo = "bar" // this is a comment
13
+ }
14
+
15
+ // comment
16
+ env "API_KEY" {
17
+ secret = true
18
+ }
19
+
20
+ env "API_URL" {
21
+ // Empty but has a comment.
22
+ }
23
+ env "API_PATH" {
24
+ }
25
+ }
26
+ observer "my-observer" {
27
+ source {
28
+ bucket = "my-bucket"
29
+ rule {
30
+ actions = ['PutObject', 'DeleteObject']
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ `;
38
+ const CONFIG_FORMATTED = `
39
+ application "my-app" {
40
+ service "my-service" {
41
+ domain {
42
+ fqdn = "testymctestface.com"
43
+ }
44
+
45
+ binding {
46
+ // This is a binding of foo to "bar".
47
+ foo = "bar" // this is a comment
48
+ }
49
+
50
+ // comment
51
+ env "API_KEY" {
52
+ secret = true
53
+ }
54
+
55
+ env "API_URL" {
56
+ // Empty but has a comment.
57
+ }
58
+ env "API_PATH" {}
59
+ }
60
+ observer "my-observer" {
61
+ source {
62
+ bucket = "my-bucket"
63
+ rule {
64
+ actions = ['PutObject', 'DeleteObject']
65
+ }
66
+ }
67
+ }
68
+ }
69
+ `;
70
+ test('parser', () => {
71
+ const tokenizer = new Tokenizer(CONFIG);
72
+ const parser = new Parser(tokenizer);
73
+ const translator = new JSONTranslator();
74
+ const ast = parser.parse();
75
+ expect(parser.errors).toEqual([]);
76
+ expect(translator.translate(ast)).toEqual({
77
+ application: [
78
+ {
79
+ name: 'my-app',
80
+ observer: [
81
+ {
82
+ name: 'my-observer',
83
+ source: [
84
+ {
85
+ bucket: 'my-bucket',
86
+ rule: [
87
+ {
88
+ actions: ['PutObject', 'DeleteObject'],
89
+ },
90
+ ],
91
+ },
92
+ ],
93
+ },
94
+ ],
95
+ service: [
96
+ {
97
+ name: 'my-service',
98
+ domain: [
99
+ {
100
+ fqdn: 'testymctestface.com',
101
+ },
102
+ ],
103
+ binding: [
104
+ {
105
+ foo: 'bar',
106
+ },
107
+ ],
108
+ env: [
109
+ {
110
+ name: 'API_KEY',
111
+ secret: true,
112
+ },
113
+ {
114
+ name: 'API_URL',
115
+ },
116
+ {
117
+ name: 'API_PATH',
118
+ },
119
+ ],
120
+ },
121
+ ],
122
+ },
123
+ ],
124
+ });
125
+ });
126
+ test('fmt', () => {
127
+ const tokenizer = new Tokenizer(CONFIG);
128
+ const parser = new Parser(tokenizer);
129
+ const ast = parser.parse();
130
+ expect(fmt(ast)).toEqual(CONFIG_FORMATTED);
131
+ });
132
+ test('parser errors', () => {
133
+ const tokenizer = new Tokenizer(`
134
+ application "my-app" }
135
+ `);
136
+ const parser = new Parser(tokenizer);
137
+ parser.parse();
138
+ expect(parser.errors).toHaveLength(1);
139
+ const tokenizer2 = new Tokenizer(`
140
+ application "my-app" {
141
+ service "my-service" {
142
+ domain {
143
+ fqdn = "testymctestface.com"
144
+ }
145
+ }
146
+ `);
147
+ const parser2 = new Parser(tokenizer2);
148
+ parser2.parse();
149
+ expect(parser2.errors).toHaveLength(1);
150
+ });
151
+ test('wrong curly brace', () => {
152
+ const tokenizer = new Tokenizer(`
153
+ application "my-app" {
154
+ service "my-service" {
155
+ domain {
156
+ fqdn = "testymctestface.com"
157
+ }
158
+ {
159
+ }
160
+ `);
161
+ const parser = new Parser(tokenizer);
162
+ parser.parse();
163
+ expect(parser.errors).toHaveLength(2);
164
+ });
165
+ test('root parse runs into something unexpected', () => {
166
+ const tokenizer = new Tokenizer(` "foo" `);
167
+ const parser = new Parser(tokenizer);
168
+ parser.parse();
169
+ expect(parser.errors).toHaveLength(1);
170
+ });
171
+ test('empty is valid (just borring)', () => {
172
+ const tokenizer = new Tokenizer(` `);
173
+ const parser = new Parser(tokenizer);
174
+ parser.parse();
175
+ expect(parser.errors).toHaveLength(0);
176
+ });
177
+ test('identifier followed by not {, string, number, boolean, operator', () => {
178
+ const tokenizer = new Tokenizer(`
179
+ application "my-app" {
180
+ service "my-service" {
181
+ domain {
182
+ fqdn = "testymctestface.com"
183
+ }
184
+ foo
185
+ }
186
+ }
187
+ `);
188
+ const parser = new Parser(tokenizer);
189
+ parser.parse();
190
+ expect(parser.errors).toHaveLength(1);
191
+ });
192
+ test("stanza doesn't have string, number, boolean arguments", () => {
193
+ const tokenizer = new Tokenizer(`
194
+ application "my-app" {
195
+ service "my-service" indentifier {
196
+ domain {
197
+ fqdn = "testymctestface.com"
198
+ }
199
+ }
200
+ }
201
+ `);
202
+ const parser = new Parser(tokenizer);
203
+ parser.parse();
204
+ expect(parser.errors).toHaveLength(1);
205
+ });
206
+ test('unexpected newline in expression', () => {
207
+ const tokenizer = new Tokenizer(`
208
+ application "my-app" {
209
+ service "my-service" {
210
+ domain {
211
+ fqdn = "testymctestface.com"
212
+ }
213
+ foo =
214
+ }
215
+ }
216
+ `);
217
+ const parser = new Parser(tokenizer);
218
+ parser.parse();
219
+ expect(parser.errors).toHaveLength(1);
220
+ });
221
+ test('unexpected trash in an array', () => {
222
+ const tokenizer = new Tokenizer(`
223
+ application "my-app" {
224
+ service "my-service" {
225
+ domain {
226
+ fqdn = "testymctestface.com"
227
+ }
228
+ foo = [1, 2, 3, } ]
229
+ }
230
+ }
231
+ `);
232
+ const parser = new Parser(tokenizer);
233
+ parser.parse();
234
+ expect(parser.errors).toHaveLength(1);
235
+ });
236
+ test('unclosed array', () => {
237
+ const tokenizer = new Tokenizer(`
238
+ application "my-app" {
239
+ service "my-service" {
240
+ domain {
241
+ fqdn = "testymctestface.com"
242
+ }
243
+ foo = [1, 2, 3, }
244
+ }
245
+ }
246
+ `);
247
+ const parser = new Parser(tokenizer);
248
+ parser.parse();
249
+ expect(parser.errors).toHaveLength(2); // unexpecte curly and expected ]
250
+ });
251
+ test('parse object', () => {
252
+ const tokenizer = new Tokenizer(`
253
+ application "my-app" {
254
+ service "my-service" {
255
+ domain {
256
+ fqdn = "testymctestface.com"
257
+ }
258
+ foo = {
259
+ bar = "baz"
260
+ }
261
+ }
262
+ }
263
+ `);
264
+ const parser = new Parser(tokenizer);
265
+ parser.parse();
266
+ console.log(parser.errors);
267
+ expect(parser.errors).toHaveLength(0);
268
+ });
269
+ test('parse object with trash', () => {
270
+ const tokenizer = new Tokenizer(`
271
+ application "my-app" {
272
+ service "my-service" {
273
+ domain {
274
+ fqdn = "testymctestface.com"
275
+ }
276
+ foo = {
277
+ bar = "baz"
278
+ baz
279
+ }
280
+ }
281
+ }
282
+ `);
283
+ const parser = new Parser(tokenizer);
284
+ parser.parse();
285
+ expect(parser.errors).toHaveLength(2); // missing = and expression
286
+ });
287
+ test('parse object with more trash', () => {
288
+ const tokenizer = new Tokenizer(`
289
+ application "my-app" {
290
+ service "my-service" {
291
+ domain {
292
+ fqdn = "testymctestface.com"
293
+ }
294
+ foo = {
295
+ bar = "baz"
296
+ true = false
297
+ }
298
+ }
299
+ }
300
+ `);
301
+ const parser = new Parser(tokenizer);
302
+ parser.parse();
303
+ expect(parser.errors).toHaveLength(3);
304
+ });
305
+ test('parse object that EOFs', () => {
306
+ const tokenizer = new Tokenizer(`
307
+ application "my-app" {
308
+ service "my-service" {
309
+ domain {
310
+ fqdn = "testymctestface.com"
311
+ }
312
+ foo = {
313
+ bar = "baz"
314
+ `);
315
+ const parser = new Parser(tokenizer);
316
+ parser.parse();
317
+ // Failed to close 3 curly braces.
318
+ expect(parser.errors).toHaveLength(3);
319
+ });
@@ -0,0 +1,22 @@
1
+ import { Actor, Application, Binding, Bucket, ConfigObject, Domain, Env, Observer, Queue, Service, SqlDatabase, VectorIndex } from './build.js';
2
+ export type ValidationError = ConfigObject & {
3
+ message: string;
4
+ severity: 'info' | 'error' | 'warning';
5
+ };
6
+ export type On<T> = (app: Application, obj: T) => Promise<ValidationError[]>;
7
+ export type Validator = {
8
+ onApplication?: On<Application>;
9
+ onService?: On<Service>;
10
+ onActor?: On<Actor>;
11
+ onObserver?: On<Observer>;
12
+ onBucket?: On<Bucket>;
13
+ onQueue?: On<Queue>;
14
+ onEnv?: On<Env>;
15
+ onBinding?: On<Binding>;
16
+ onDomain?: On<Domain>;
17
+ onSqlDatabase?: On<SqlDatabase>;
18
+ onVectorIndex?: On<VectorIndex>;
19
+ };
20
+ export declare function validate(apps: Application[], validators: Validator[]): Promise<ValidationError[]>;
21
+ export declare const VALIDATORS: Validator[];
22
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/appify/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,YAAY,EACZ,MAAM,EACN,GAAG,EACH,QAAQ,EACR,KAAK,EACL,OAAO,EACP,WAAW,EAEX,WAAW,EAEZ,MAAM,YAAY,CAAC;AAOpB,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,SAAS,GAAG;IACtB,aAAa,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IACxB,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IACpB,UAAU,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IACpB,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAChB,SAAS,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;IACtB,aAAa,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC;CACjC,CAAC;AAcF,wBAAsB,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAgBvG;AAiWD,eAAO,MAAM,UAAU,EAAE,SAAS,EAUjC,CAAC"}
@@ -0,0 +1,346 @@
1
+ import { valueOf, VISIBILITIES, } from './build.js';
2
+ async function validateHelper(validatorFn, application, obj, errors) {
3
+ if (validatorFn) {
4
+ const returnedErrors = await validatorFn(application, obj);
5
+ errors.push(...returnedErrors);
6
+ }
7
+ }
8
+ export async function validate(apps, validators) {
9
+ const errors = [];
10
+ for (const app of apps) {
11
+ for (const validator of validators) {
12
+ await validateHelper(validator.onApplication, app, app, errors);
13
+ await validateServices(app, validator, errors);
14
+ await validateActors(app, validator, errors);
15
+ await validateObservers(app, validator, errors);
16
+ await validateBuckets(app, validator, errors);
17
+ await validateQueues(app, validator, errors);
18
+ await validateEnvs(app, app, validator, errors);
19
+ await validateVectorIndexes(app, validator, errors);
20
+ await validateSqlDatabases(app, validator, errors);
21
+ }
22
+ }
23
+ return errors;
24
+ }
25
+ async function validateServices(app, validator, errors) {
26
+ for (const service of app.service) {
27
+ await validateHelper(validator.onService, app, service, errors);
28
+ await validateBindings(app, service, validator, errors);
29
+ await validateDomains(app, service, validator, errors);
30
+ await validateEnvs(app, service, validator, errors);
31
+ }
32
+ }
33
+ async function validateActors(app, validator, errors) {
34
+ for (const actor of app.actor) {
35
+ await validateHelper(validator.onActor, app, actor, errors);
36
+ await validateBindings(app, actor, validator, errors);
37
+ await validateEnvs(app, actor, validator, errors);
38
+ }
39
+ }
40
+ async function validateObservers(app, validator, errors) {
41
+ for (const observer of app.observer) {
42
+ await validateHelper(validator.onObserver, app, observer, errors);
43
+ await validateBindings(app, observer, validator, errors);
44
+ await validateEnvs(app, observer, validator, errors);
45
+ }
46
+ }
47
+ async function validateBuckets(app, validator, errors) {
48
+ for (const bucket of app.bucket) {
49
+ await validateHelper(validator.onBucket, app, bucket, errors);
50
+ }
51
+ }
52
+ async function validateQueues(app, validator, errors) {
53
+ for (const queue of app.queue) {
54
+ await validateHelper(validator.onQueue, app, queue, errors);
55
+ }
56
+ }
57
+ async function validateVectorIndexes(app, validator, errors) {
58
+ for (const vectorIndex of app.vectorIndex) {
59
+ await validateHelper(validator.onVectorIndex, app, vectorIndex, errors);
60
+ }
61
+ }
62
+ async function validateSqlDatabases(app, validator, errors) {
63
+ for (const sqlDatabase of app.sqlDatabase) {
64
+ await validateHelper(validator.onSqlDatabase, app, sqlDatabase, errors);
65
+ }
66
+ }
67
+ async function validateEnvs(app, obj, validator, errors) {
68
+ for (const env of obj.env) {
69
+ await validateHelper(validator.onEnv, app, env, errors);
70
+ }
71
+ }
72
+ async function validateBindings(app, obj, validator, errors) {
73
+ for (const binding of obj.bindings) {
74
+ await validateHelper(validator.onBinding, app, binding, errors);
75
+ }
76
+ }
77
+ async function validateDomains(app, obj, validator, errors) {
78
+ for (const domain of obj.domains) {
79
+ await validateHelper(validator.onDomain, app, domain, errors);
80
+ }
81
+ }
82
+ // Validators
83
+ const bindingNameValidator = {
84
+ onBinding: async (app, binding) => {
85
+ const errors = [];
86
+ for (const bind of binding.bindings) {
87
+ if (!bind.key.value.match(/^[A-Z][A-Z_]*$/)) {
88
+ errors.push({
89
+ message: 'binding names should be UPPER_SNAKE_CASE',
90
+ severity: 'warning',
91
+ ...bind.key,
92
+ });
93
+ }
94
+ }
95
+ return errors;
96
+ },
97
+ };
98
+ const bindingValueValidator = {
99
+ onBinding: async (app, binding) => {
100
+ const errors = [];
101
+ for (const bind of binding.bindings) {
102
+ if (bind.value.type !== 'string') {
103
+ errors.push({
104
+ message: 'bindings must be to names of modules',
105
+ severity: 'error',
106
+ ...bind.value,
107
+ });
108
+ }
109
+ else {
110
+ const ref = valueOf(bind.value);
111
+ // Walk the app to see if this is a module.
112
+ let all = [];
113
+ all = all.concat(app.bucket, app.env, app.observer, app.queue, app.service, app.sqlDatabase, app.vectorIndex);
114
+ if (!all.some((b) => valueOf(b.name) === ref)) {
115
+ errors.push({
116
+ message: `binding ${bind.value.value} not found`,
117
+ severity: 'error',
118
+ ...bind.value,
119
+ });
120
+ }
121
+ else {
122
+ // TODO [ian] It _is_ found, so let's make sure it's not
123
+ // stomping on an implicit binding and check visibility
124
+ // rules.
125
+ }
126
+ }
127
+ }
128
+ return errors;
129
+ },
130
+ };
131
+ const domainValidator = {
132
+ onDomain: async (app, domain) => {
133
+ const errors = [];
134
+ if (domain.fqdn === undefined) {
135
+ errors.push({
136
+ message: 'domain must have an fqdn',
137
+ severity: 'error',
138
+ ...domain.obj,
139
+ });
140
+ }
141
+ else if (!/^[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/.test(valueOf(domain.fqdn))) {
142
+ errors.push({
143
+ message: `domain ${domain.fqdn.value} is an invalid domain name`,
144
+ severity: 'warning',
145
+ ...domain.fqdn,
146
+ });
147
+ }
148
+ return errors;
149
+ },
150
+ };
151
+ const envValidator = {
152
+ onEnv: async (app, env) => {
153
+ const errors = [];
154
+ if (!valueOf(env.name).match(/^[A-Z][A-Z_]*$/)) {
155
+ errors.push({
156
+ message: 'env names should be UPPER_SNAKE_CASE',
157
+ severity: 'warning',
158
+ ...env.name,
159
+ });
160
+ }
161
+ if (env.secret?.value === 'true' && env.default !== undefined) {
162
+ errors.push({
163
+ message: 'secret env vars cannot have defaults',
164
+ severity: 'error',
165
+ ...env.default,
166
+ });
167
+ }
168
+ return errors;
169
+ },
170
+ };
171
+ const observerSourceValidator = {
172
+ onObserver: async (app, observer) => {
173
+ const errors = [];
174
+ const seen = new Set();
175
+ for (const source of observer.source) {
176
+ if (source.bucket && source.queue) {
177
+ errors.push({
178
+ message: 'observer source can only have one of bucket or queue',
179
+ severity: 'error',
180
+ ...source.obj,
181
+ });
182
+ }
183
+ if (!source.bucket && !source.queue) {
184
+ errors.push({
185
+ message: 'observer source must have either a bucket or a queue',
186
+ severity: 'error',
187
+ ...source.obj,
188
+ });
189
+ }
190
+ if (source.bucket !== undefined &&
191
+ !app.bucket.some((b) => valueOf(b.name) === (source.bucket && valueOf(source.bucket)))) {
192
+ errors.push({
193
+ message: `bucket ${source.bucket.value} not found`,
194
+ severity: 'error',
195
+ ...source.bucket,
196
+ });
197
+ }
198
+ if (source.queue !== undefined &&
199
+ !app.queue.some((q) => valueOf(q.name) === (source.queue && valueOf(source.queue)))) {
200
+ errors.push({
201
+ message: `queue ${source.queue.value} not found`,
202
+ severity: 'error',
203
+ ...source.queue,
204
+ });
205
+ }
206
+ const sourceName = (source.bucket && valueOf(source.bucket)) || (source.queue && valueOf(source.queue));
207
+ if (sourceName !== undefined) {
208
+ if (seen.has(sourceName)) {
209
+ errors.push({
210
+ message: `source ${sourceName} is duplicated`,
211
+ severity: 'error',
212
+ ...source.obj,
213
+ });
214
+ }
215
+ seen.add(sourceName);
216
+ }
217
+ if (source.rule !== undefined && source.queue !== undefined) {
218
+ errors.push({
219
+ message: 'queue sources do not have bucket notification rules',
220
+ severity: 'error',
221
+ ...source.obj,
222
+ });
223
+ }
224
+ }
225
+ return errors;
226
+ },
227
+ };
228
+ async function visibilityValidatorFn(app, obj) {
229
+ const errors = [];
230
+ if (obj.visibility && !VISIBILITIES.includes(valueOf(obj.visibility))) {
231
+ errors.push({
232
+ message: `visibility must be one of ${VISIBILITIES.join(', ')}`,
233
+ severity: 'error',
234
+ ...(obj.visibility || obj),
235
+ });
236
+ }
237
+ return errors;
238
+ }
239
+ const visibilityValidator = {
240
+ onService: visibilityValidatorFn,
241
+ onActor: visibilityValidatorFn,
242
+ onQueue: visibilityValidatorFn,
243
+ onBucket: visibilityValidatorFn,
244
+ };
245
+ async function nameValidatorFn(app, obj) {
246
+ const errors = [];
247
+ if (!valueOf(obj.name).match(/^[a-z][a-z0-9-]*$/)) {
248
+ errors.push({
249
+ message: `name must be lowercase and dash-separated`,
250
+ severity: 'error',
251
+ ...obj.name,
252
+ });
253
+ }
254
+ return errors;
255
+ }
256
+ const nameValidator = {
257
+ onApplication: nameValidatorFn,
258
+ onService: nameValidatorFn,
259
+ onObserver: nameValidatorFn,
260
+ onBucket: nameValidatorFn,
261
+ onQueue: nameValidatorFn,
262
+ onActor: nameValidatorFn,
263
+ onSqlDatabase: nameValidatorFn,
264
+ onVectorIndex: nameValidatorFn,
265
+ };
266
+ const vectorIndexValidator = {
267
+ onVectorIndex: async (app, vectorIndex) => {
268
+ const errors = [];
269
+ if (vectorIndex.dimension === undefined) {
270
+ errors.push({
271
+ message: 'vector index must have a dimension',
272
+ severity: 'error',
273
+ ...vectorIndex.obj,
274
+ });
275
+ }
276
+ if (vectorIndex.metric === undefined ||
277
+ (valueOf(vectorIndex.metric) !== 'cosine' && valueOf(vectorIndex.metric) !== 'euclidean')) {
278
+ errors.push({
279
+ message: 'vector index metric must be either cosine or euclidean',
280
+ severity: 'error',
281
+ ...(vectorIndex.metric || vectorIndex.obj),
282
+ });
283
+ }
284
+ if (errors.length > 0) {
285
+ return errors;
286
+ }
287
+ // We have to re-check because the above is too complex for TS.
288
+ if (vectorIndex.dimension === undefined) {
289
+ return errors;
290
+ }
291
+ const dimension = valueOf(vectorIndex.dimension);
292
+ if (!Number.isInteger(dimension)) {
293
+ errors.push({
294
+ message: 'vector index dimension must be an integer',
295
+ severity: 'error',
296
+ ...vectorIndex.dimension,
297
+ });
298
+ return errors;
299
+ }
300
+ if (dimension < 1 || dimension > 1536) {
301
+ errors.push({
302
+ message: 'vector index dimension must be between 1 and 1536',
303
+ severity: 'error',
304
+ ...vectorIndex.dimension,
305
+ });
306
+ }
307
+ return errors;
308
+ },
309
+ };
310
+ const duplicateModuleValidator = {
311
+ onApplication: async (app, _application) => {
312
+ const errors = [];
313
+ const seen = new Set();
314
+ for (const module of [
315
+ ...app.bucket,
316
+ ...app.env,
317
+ ...app.observer,
318
+ ...app.queue,
319
+ ...app.service,
320
+ ...app.sqlDatabase,
321
+ ...app.vectorIndex,
322
+ ]) {
323
+ const name = valueOf(module.name);
324
+ if (seen.has(name)) {
325
+ errors.push({
326
+ message: `module ${name} has already been defined`,
327
+ severity: 'error',
328
+ ...module.name,
329
+ });
330
+ }
331
+ seen.add(name);
332
+ }
333
+ return errors;
334
+ },
335
+ };
336
+ export const VALIDATORS = [
337
+ bindingNameValidator,
338
+ bindingValueValidator,
339
+ domainValidator,
340
+ envValidator,
341
+ observerSourceValidator,
342
+ visibilityValidator,
343
+ nameValidator,
344
+ vectorIndexValidator,
345
+ duplicateModuleValidator,
346
+ ];
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.test.d.ts","sourceRoot":"","sources":["../../src/appify/validate.test.ts"],"names":[],"mappings":""}