@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.
- package/.changeset/README.md +4 -0
- package/.changeset/config.json +11 -0
- package/.changeset/odd-plums-dress.md +7 -0
- package/.turbo/turbo-build.log +6 -0
- package/dist/appify/build.d.ts +130 -0
- package/dist/appify/build.d.ts.map +1 -0
- package/dist/appify/build.js +703 -0
- package/dist/appify/build.test.d.ts +2 -0
- package/dist/appify/build.test.d.ts.map +1 -0
- package/dist/appify/build.test.js +111 -0
- package/dist/appify/index.d.ts +8 -0
- package/dist/appify/index.d.ts.map +1 -0
- package/dist/appify/index.js +28 -0
- package/dist/appify/index.test.d.ts +2 -0
- package/dist/appify/index.test.d.ts.map +1 -0
- package/dist/appify/index.test.js +40 -0
- package/dist/appify/parse.d.ts +151 -0
- package/dist/appify/parse.d.ts.map +1 -0
- package/dist/appify/parse.js +579 -0
- package/dist/appify/parse.test.d.ts +2 -0
- package/dist/appify/parse.test.d.ts.map +1 -0
- package/dist/appify/parse.test.js +319 -0
- package/dist/appify/validate.d.ts +22 -0
- package/dist/appify/validate.d.ts.map +1 -0
- package/dist/appify/validate.js +346 -0
- package/dist/appify/validate.test.d.ts +2 -0
- package/dist/appify/validate.test.d.ts.map +1 -0
- package/dist/appify/validate.test.js +327 -0
- package/dist/codestore.d.ts +34 -0
- package/dist/codestore.d.ts.map +1 -0
- package/dist/codestore.js +54 -0
- package/dist/codestore.test.d.ts +2 -0
- package/dist/codestore.test.d.ts.map +1 -0
- package/dist/codestore.test.js +84 -0
- package/dist/liquidmetal/v1alpha1/catalog_connect.d.ts +147 -0
- package/dist/liquidmetal/v1alpha1/catalog_connect.d.ts.map +1 -0
- package/dist/liquidmetal/v1alpha1/catalog_connect.js +150 -0
- package/dist/liquidmetal/v1alpha1/catalog_pb.d.ts +965 -0
- package/dist/liquidmetal/v1alpha1/catalog_pb.d.ts.map +1 -0
- package/dist/liquidmetal/v1alpha1/catalog_pb.js +1486 -0
- package/dist/liquidmetal/v1alpha1/rainbow_auth_connect.d.ts +49 -0
- package/dist/liquidmetal/v1alpha1/rainbow_auth_connect.d.ts.map +1 -0
- package/dist/liquidmetal/v1alpha1/rainbow_auth_connect.js +52 -0
- package/dist/liquidmetal/v1alpha1/rainbow_auth_pb.d.ts +271 -0
- package/dist/liquidmetal/v1alpha1/rainbow_auth_pb.d.ts.map +1 -0
- package/dist/liquidmetal/v1alpha1/rainbow_auth_pb.js +381 -0
- package/dist/liquidmetal/v1alpha1/raindrop_pb.d.ts +38 -0
- package/dist/liquidmetal/v1alpha1/raindrop_pb.d.ts.map +1 -0
- package/dist/liquidmetal/v1alpha1/raindrop_pb.js +52 -0
- package/dist/unsafe/codestore.d.ts +10 -0
- package/dist/unsafe/codestore.d.ts.map +1 -0
- package/dist/unsafe/codestore.js +23 -0
- package/dist/unsafe/codestore.test.d.ts +2 -0
- package/dist/unsafe/codestore.test.d.ts.map +1 -0
- package/dist/unsafe/codestore.test.js +27 -0
- package/eslint.config.mjs +4 -0
- package/package.json +45 -0
- package/src/appify/build.test.ts +116 -0
- package/src/appify/build.ts +783 -0
- package/src/appify/index.test.ts +43 -0
- package/src/appify/index.ts +33 -0
- package/src/appify/parse.test.ts +337 -0
- package/src/appify/parse.ts +744 -0
- package/src/appify/validate.test.ts +341 -0
- package/src/appify/validate.ts +435 -0
- package/src/codestore.test.ts +98 -0
- package/src/codestore.ts +93 -0
- package/src/liquidmetal/v1alpha1/catalog_connect.ts +153 -0
- package/src/liquidmetal/v1alpha1/catalog_pb.ts +1827 -0
- package/src/liquidmetal/v1alpha1/rainbow_auth_connect.ts +55 -0
- package/src/liquidmetal/v1alpha1/rainbow_auth_pb.ts +476 -0
- package/src/liquidmetal/v1alpha1/raindrop_pb.ts +63 -0
- package/src/unsafe/codestore.test.ts +34 -0
- package/src/unsafe/codestore.ts +29 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { buildManifest } from './build.js';
|
|
3
|
+
import { Parser, Tokenizer } from './parse.js';
|
|
4
|
+
import { validate, VALIDATORS } from './validate.js';
|
|
5
|
+
test('validate nothing', async () => {
|
|
6
|
+
const manifest = `
|
|
7
|
+
application "my-app" {
|
|
8
|
+
service "my-service" {}
|
|
9
|
+
actor "my-actor" {}
|
|
10
|
+
observer "my-observer" {}
|
|
11
|
+
bucket "my-bucket" {}
|
|
12
|
+
queue "my-queue" {}
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
const tokenizer = new Tokenizer(manifest);
|
|
16
|
+
const parser = new Parser(tokenizer);
|
|
17
|
+
const parsedManifest = parser.parse();
|
|
18
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
19
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
20
|
+
expect(validateErrors).toEqual([]);
|
|
21
|
+
});
|
|
22
|
+
test('validate bad binds', async () => {
|
|
23
|
+
const manifest = `
|
|
24
|
+
application "my-app" {
|
|
25
|
+
service "my-service" {
|
|
26
|
+
binding {
|
|
27
|
+
bad_bind = "my-missing-service"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
const tokenizer = new Tokenizer(manifest);
|
|
32
|
+
const parser = new Parser(tokenizer);
|
|
33
|
+
const parsedManifest = parser.parse();
|
|
34
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
35
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
36
|
+
expect(validateErrors).toMatchObject([
|
|
37
|
+
{
|
|
38
|
+
message: 'binding names should be UPPER_SNAKE_CASE',
|
|
39
|
+
line: 5,
|
|
40
|
+
column: 7,
|
|
41
|
+
start: 69,
|
|
42
|
+
end: 77,
|
|
43
|
+
severity: 'warning',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
message: 'binding "my-missing-service" not found',
|
|
47
|
+
line: 5,
|
|
48
|
+
column: 18,
|
|
49
|
+
start: 80,
|
|
50
|
+
end: 100,
|
|
51
|
+
severity: 'error',
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
54
|
+
});
|
|
55
|
+
test('domainValidator', async () => {
|
|
56
|
+
const manifest = `
|
|
57
|
+
application "my-app" {
|
|
58
|
+
service "my-service" {
|
|
59
|
+
domain {
|
|
60
|
+
fqdn = "not-valid.com_foo"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
const tokenizer = new Tokenizer(manifest);
|
|
66
|
+
const parser = new Parser(tokenizer);
|
|
67
|
+
const parsedManifest = parser.parse();
|
|
68
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
69
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
70
|
+
expect(validateErrors).toMatchObject([
|
|
71
|
+
{
|
|
72
|
+
message: 'domain "not-valid.com_foo" is an invalid domain name',
|
|
73
|
+
line: 5,
|
|
74
|
+
column: 14,
|
|
75
|
+
start: 75,
|
|
76
|
+
end: 94,
|
|
77
|
+
severity: 'warning',
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
test('visibilityValidator', async () => {
|
|
82
|
+
const manifest = `
|
|
83
|
+
application "my-app" {
|
|
84
|
+
service "my-service" {
|
|
85
|
+
visibility = 'not-a-visibility'
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
const tokenizer = new Tokenizer(manifest);
|
|
90
|
+
const parser = new Parser(tokenizer);
|
|
91
|
+
const parsedManifest = parser.parse();
|
|
92
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
93
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
94
|
+
expect(validateErrors).toMatchObject([
|
|
95
|
+
{
|
|
96
|
+
message: 'visibility must be one of none, public, private, protected, application, suite, tenant',
|
|
97
|
+
line: 4,
|
|
98
|
+
column: 18,
|
|
99
|
+
start: 66,
|
|
100
|
+
end: 84,
|
|
101
|
+
severity: 'error',
|
|
102
|
+
},
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
test('nameValidator', async () => {
|
|
106
|
+
const manifest = `
|
|
107
|
+
application "MyApp" {
|
|
108
|
+
service "my-service" {}
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
const tokenizer = new Tokenizer(manifest);
|
|
112
|
+
const parser = new Parser(tokenizer);
|
|
113
|
+
const parsedManifest = parser.parse();
|
|
114
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
115
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
116
|
+
expect(validateErrors).toMatchObject([
|
|
117
|
+
{
|
|
118
|
+
message: 'name must be lowercase and dash-separated',
|
|
119
|
+
line: 2,
|
|
120
|
+
column: 13,
|
|
121
|
+
start: 13,
|
|
122
|
+
end: 20,
|
|
123
|
+
severity: 'error',
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
});
|
|
127
|
+
test('duplicateModuleValidator', async () => {
|
|
128
|
+
const manifest = `
|
|
129
|
+
application "my-app" {
|
|
130
|
+
service "my-service" {}
|
|
131
|
+
|
|
132
|
+
service "my-service" {}
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
const tokenizer = new Tokenizer(manifest);
|
|
136
|
+
const parser = new Parser(tokenizer);
|
|
137
|
+
const parsedManifest = parser.parse();
|
|
138
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
139
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
140
|
+
expect(validateErrors).toMatchObject([
|
|
141
|
+
{
|
|
142
|
+
message: 'module my-service has already been defined',
|
|
143
|
+
line: 5,
|
|
144
|
+
column: 11,
|
|
145
|
+
start: 61,
|
|
146
|
+
end: 73,
|
|
147
|
+
severity: 'error',
|
|
148
|
+
},
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
test('validate env var format', async () => {
|
|
152
|
+
const manifest = `
|
|
153
|
+
application "my-app" {
|
|
154
|
+
service "my-service" {
|
|
155
|
+
env "MY_ENV_VAR" {}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
`;
|
|
159
|
+
const tokenizer = new Tokenizer(manifest);
|
|
160
|
+
const parser = new Parser(tokenizer);
|
|
161
|
+
const parsedManifest = parser.parse();
|
|
162
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
163
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
164
|
+
expect(validateErrors).toEqual([]);
|
|
165
|
+
});
|
|
166
|
+
test('validate vector missing parameters', async () => {
|
|
167
|
+
const manifest = `
|
|
168
|
+
application "my-app" {
|
|
169
|
+
service "my-service" {}
|
|
170
|
+
vector_index "my-index" {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
const tokenizer = new Tokenizer(manifest);
|
|
175
|
+
const parser = new Parser(tokenizer);
|
|
176
|
+
const parsedManifest = parser.parse();
|
|
177
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
178
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
179
|
+
expect(validateErrors).toMatchObject([
|
|
180
|
+
{
|
|
181
|
+
message: 'vector index must have a dimension',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
message: 'vector index metric must be either cosine or euclidean',
|
|
185
|
+
},
|
|
186
|
+
]);
|
|
187
|
+
});
|
|
188
|
+
test('validate vector index parameters', async () => {
|
|
189
|
+
const manifest = `
|
|
190
|
+
application "my-app" {
|
|
191
|
+
service "my-service" {}
|
|
192
|
+
vector_index "my-index" {
|
|
193
|
+
dimension = 128.9
|
|
194
|
+
metric = "cosine"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
const tokenizer = new Tokenizer(manifest);
|
|
199
|
+
const parser = new Parser(tokenizer);
|
|
200
|
+
const parsedManifest = parser.parse();
|
|
201
|
+
const [builtApps] = buildManifest(parsedManifest);
|
|
202
|
+
const validateErrors = await validate(builtApps, VALIDATORS);
|
|
203
|
+
expect(validateErrors).toMatchObject([
|
|
204
|
+
{
|
|
205
|
+
message: 'vector index dimension must be an integer',
|
|
206
|
+
},
|
|
207
|
+
]);
|
|
208
|
+
});
|
|
209
|
+
test('observer source validator', async () => {
|
|
210
|
+
let manifest = `
|
|
211
|
+
application "my-app" {
|
|
212
|
+
observer "my-observer" {
|
|
213
|
+
source {
|
|
214
|
+
service = "my-service"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
`;
|
|
219
|
+
let tokenizer = new Tokenizer(manifest);
|
|
220
|
+
let parser = new Parser(tokenizer);
|
|
221
|
+
let parsedManifest = parser.parse();
|
|
222
|
+
let [builtApps] = buildManifest(parsedManifest);
|
|
223
|
+
let validateErrors = await validate(builtApps, VALIDATORS);
|
|
224
|
+
expect(validateErrors).toMatchObject([
|
|
225
|
+
{
|
|
226
|
+
message: 'observer source must have either a bucket or a queue',
|
|
227
|
+
},
|
|
228
|
+
]);
|
|
229
|
+
manifest = `
|
|
230
|
+
application "my-app" {
|
|
231
|
+
observer "my-observer" {
|
|
232
|
+
source {
|
|
233
|
+
bucket = "my-bucket"
|
|
234
|
+
queue = "my-queue"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
`;
|
|
239
|
+
tokenizer = new Tokenizer(manifest);
|
|
240
|
+
parser = new Parser(tokenizer);
|
|
241
|
+
parsedManifest = parser.parse();
|
|
242
|
+
[builtApps] = buildManifest(parsedManifest);
|
|
243
|
+
validateErrors = await validate(builtApps, VALIDATORS);
|
|
244
|
+
expect(validateErrors).toMatchObject([
|
|
245
|
+
{
|
|
246
|
+
message: 'observer source can only have one of bucket or queue',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
message: 'bucket "my-bucket" not found',
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
message: 'queue "my-queue" not found',
|
|
253
|
+
},
|
|
254
|
+
]);
|
|
255
|
+
manifest = `
|
|
256
|
+
application "my-app" {
|
|
257
|
+
observer "my-observer" {
|
|
258
|
+
source {
|
|
259
|
+
bucket = "my-bucket"
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
`;
|
|
264
|
+
tokenizer = new Tokenizer(manifest);
|
|
265
|
+
parser = new Parser(tokenizer);
|
|
266
|
+
parsedManifest = parser.parse();
|
|
267
|
+
[builtApps] = buildManifest(parsedManifest);
|
|
268
|
+
validateErrors = await validate(builtApps, VALIDATORS);
|
|
269
|
+
expect(validateErrors).toMatchObject([
|
|
270
|
+
{
|
|
271
|
+
message: 'bucket "my-bucket" not found',
|
|
272
|
+
},
|
|
273
|
+
]);
|
|
274
|
+
manifest = `
|
|
275
|
+
application "my-app" {
|
|
276
|
+
observer "my-observer" {
|
|
277
|
+
source {
|
|
278
|
+
bucket = "my-bucket"
|
|
279
|
+
}
|
|
280
|
+
source {
|
|
281
|
+
bucket = "my-bucket"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
`;
|
|
286
|
+
tokenizer = new Tokenizer(manifest);
|
|
287
|
+
parser = new Parser(tokenizer);
|
|
288
|
+
parsedManifest = parser.parse();
|
|
289
|
+
[builtApps] = buildManifest(parsedManifest);
|
|
290
|
+
validateErrors = await validate(builtApps, VALIDATORS);
|
|
291
|
+
expect(validateErrors).toMatchObject([
|
|
292
|
+
{
|
|
293
|
+
message: 'bucket "my-bucket" not found',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
message: 'bucket "my-bucket" not found',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
message: 'source my-bucket is duplicated',
|
|
300
|
+
},
|
|
301
|
+
]);
|
|
302
|
+
manifest = `
|
|
303
|
+
application "my-app" {
|
|
304
|
+
queue "my-queue" {}
|
|
305
|
+
observer "my-observer" {
|
|
306
|
+
source {
|
|
307
|
+
queue = "my-queue"
|
|
308
|
+
rule {
|
|
309
|
+
actions = ['PutObject']
|
|
310
|
+
prefix = ''
|
|
311
|
+
suffix = ''
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
`;
|
|
317
|
+
tokenizer = new Tokenizer(manifest);
|
|
318
|
+
parser = new Parser(tokenizer);
|
|
319
|
+
parsedManifest = parser.parse();
|
|
320
|
+
[builtApps] = buildManifest(parsedManifest);
|
|
321
|
+
validateErrors = await validate(builtApps, VALIDATORS);
|
|
322
|
+
expect(validateErrors).toMatchObject([
|
|
323
|
+
{
|
|
324
|
+
message: 'queue sources do not have bucket notification rules',
|
|
325
|
+
},
|
|
326
|
+
]);
|
|
327
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface CodeStore {
|
|
2
|
+
[key: string]: Bundle;
|
|
3
|
+
}
|
|
4
|
+
export interface Stat {
|
|
5
|
+
sizeBytes: number;
|
|
6
|
+
sha256Hex: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ReadableBundle {
|
|
9
|
+
list(): Promise<string[]>;
|
|
10
|
+
read(name: string): Promise<Buffer>;
|
|
11
|
+
stat(name: string): Promise<Stat>;
|
|
12
|
+
[Symbol.asyncIterator](): AsyncGenerator<File>;
|
|
13
|
+
hash(): Promise<string>;
|
|
14
|
+
}
|
|
15
|
+
export interface WritableBundle {
|
|
16
|
+
write(name: string, content: Buffer): Promise<void>;
|
|
17
|
+
delete(name: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export type Bundle = ReadableBundle & WritableBundle;
|
|
20
|
+
export interface File {
|
|
21
|
+
name: string;
|
|
22
|
+
read(): Promise<Buffer>;
|
|
23
|
+
stat(): Promise<Stat>;
|
|
24
|
+
}
|
|
25
|
+
export declare abstract class BundleBase implements ReadableBundle {
|
|
26
|
+
abstract list(): Promise<string[]>;
|
|
27
|
+
abstract read(name: string): Promise<Buffer>;
|
|
28
|
+
stat(name: string): Promise<Stat>;
|
|
29
|
+
[Symbol.asyncIterator](): AsyncGenerator<File>;
|
|
30
|
+
hash(): Promise<string>;
|
|
31
|
+
}
|
|
32
|
+
export declare function archive(bundle: ReadableBundle): Promise<ArrayBuffer>;
|
|
33
|
+
export declare function unarchive(zipBuffer: ArrayBuffer, bundle: WritableBundle): Promise<void>;
|
|
34
|
+
//# sourceMappingURL=codestore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codestore.d.ts","sourceRoot":"","sources":["../src/codestore.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,IAAI;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,MAAM,GAAG,cAAc,GAAG,cAAc,CAAC;AAErD,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,8BAAsB,UAAW,YAAW,cAAc;IACxD,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAEtC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBhC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC;IAW/C,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAO9B;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAM1E;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7F"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import JSZip from 'jszip';
|
|
3
|
+
export class BundleBase {
|
|
4
|
+
async stat(name) {
|
|
5
|
+
const content = await this.read(name);
|
|
6
|
+
if (content === undefined) {
|
|
7
|
+
throw new Error(`File not found: ${name}`);
|
|
8
|
+
}
|
|
9
|
+
const hash = crypto.createHash('sha256');
|
|
10
|
+
hash.update(content);
|
|
11
|
+
const sha256Hex = hash.digest('hex');
|
|
12
|
+
return {
|
|
13
|
+
sizeBytes: content.length,
|
|
14
|
+
sha256Hex,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
// This is an AsyncInterator generator function that returns "File"
|
|
18
|
+
// objects that can be read or stat'd. Use via:
|
|
19
|
+
// for await (const file of bundle) { ... }
|
|
20
|
+
async *[Symbol.asyncIterator]() {
|
|
21
|
+
const list = await this.list();
|
|
22
|
+
for (const name of list) {
|
|
23
|
+
yield {
|
|
24
|
+
name,
|
|
25
|
+
read: async () => await this.read(name),
|
|
26
|
+
stat: async () => await this.stat(name),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async hash() {
|
|
31
|
+
const hash = crypto.createHash('sha256', { encoding: 'hex' });
|
|
32
|
+
for await (const file of this) {
|
|
33
|
+
hash.update(await file.read());
|
|
34
|
+
}
|
|
35
|
+
return hash.digest('hex');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function archive(bundle) {
|
|
39
|
+
const zip = new JSZip();
|
|
40
|
+
for await (const file of bundle) {
|
|
41
|
+
zip.file(file.name, await file.read());
|
|
42
|
+
}
|
|
43
|
+
return await zip.generateAsync({ type: 'arraybuffer' });
|
|
44
|
+
}
|
|
45
|
+
export async function unarchive(zipBuffer, bundle) {
|
|
46
|
+
const zip = await JSZip.loadAsync(zipBuffer);
|
|
47
|
+
for (const [, file] of Object.entries(zip.files)) {
|
|
48
|
+
if (file.dir) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const content = await file.async('arraybuffer');
|
|
52
|
+
await bundle.write(file.name, Buffer.from(content));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codestore.test.d.ts","sourceRoot":"","sources":["../src/codestore.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { BundleBase, archive, unarchive } from './codestore.js';
|
|
3
|
+
class MemoryBundle extends BundleBase {
|
|
4
|
+
files = new Map();
|
|
5
|
+
async list() {
|
|
6
|
+
return Array.from(this.files.keys());
|
|
7
|
+
}
|
|
8
|
+
async read(name) {
|
|
9
|
+
const buff = this.files.get(name);
|
|
10
|
+
if (buff === undefined) {
|
|
11
|
+
throw new Error(`File not found: ${name}`);
|
|
12
|
+
}
|
|
13
|
+
return buff;
|
|
14
|
+
}
|
|
15
|
+
async write(name, content) {
|
|
16
|
+
this.files.set(name, content);
|
|
17
|
+
}
|
|
18
|
+
async delete(name) {
|
|
19
|
+
this.files.delete(name);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
test('archive and unarchive', async () => {
|
|
23
|
+
const bundle = new MemoryBundle();
|
|
24
|
+
await bundle.write('file1.txt', Buffer.from('hello'));
|
|
25
|
+
await bundle.write('file2.txt', Buffer.from('world'));
|
|
26
|
+
const hash1 = await bundle.hash();
|
|
27
|
+
const archiveBuffer = await archive(bundle);
|
|
28
|
+
const bundle2 = new MemoryBundle();
|
|
29
|
+
await unarchive(archiveBuffer, bundle2);
|
|
30
|
+
const hash2 = await bundle2.hash();
|
|
31
|
+
expect(hash1).toBe(hash2);
|
|
32
|
+
expect(await bundle2.list()).toEqual(['file1.txt', 'file2.txt']);
|
|
33
|
+
expect((await bundle2.read('file1.txt')).toString()).toBe('hello');
|
|
34
|
+
expect((await bundle2.read('file2.txt')).toString()).toBe('world');
|
|
35
|
+
});
|
|
36
|
+
test('list bundle with files', async () => {
|
|
37
|
+
const bundle = new MemoryBundle();
|
|
38
|
+
await bundle.write('file1.txt', Buffer.from('hello'));
|
|
39
|
+
await bundle.write('file2.txt', Buffer.from('world'));
|
|
40
|
+
expect(await bundle.list()).toEqual(['file1.txt', 'file2.txt']);
|
|
41
|
+
});
|
|
42
|
+
test('read file from bundle', async () => {
|
|
43
|
+
const bundle = new MemoryBundle();
|
|
44
|
+
await bundle.write('file1.txt', Buffer.from('hello'));
|
|
45
|
+
expect((await bundle.read('file1.txt')).toString()).toBe('hello');
|
|
46
|
+
});
|
|
47
|
+
test('read missing file from bundle', async () => {
|
|
48
|
+
const bundle = new MemoryBundle();
|
|
49
|
+
expect(bundle.read('file1.txt')).rejects.toThrowError('File not found: file1.txt');
|
|
50
|
+
});
|
|
51
|
+
test('delete file from bundle', async () => {
|
|
52
|
+
const bundle = new MemoryBundle();
|
|
53
|
+
await bundle.write('file1.txt', Buffer.from('hello'));
|
|
54
|
+
await bundle.delete('file1.txt');
|
|
55
|
+
expect(bundle.read('file1.txt')).rejects.toThrowError('File not found: file1.txt');
|
|
56
|
+
});
|
|
57
|
+
test('for await of bundle', async () => {
|
|
58
|
+
const bundle = new MemoryBundle();
|
|
59
|
+
await bundle.write('file1.txt', Buffer.from('hello'));
|
|
60
|
+
await bundle.write('file2.txt', Buffer.from('world'));
|
|
61
|
+
const files = [];
|
|
62
|
+
for await (const file of bundle) {
|
|
63
|
+
files.push(file.name);
|
|
64
|
+
}
|
|
65
|
+
expect(files).toEqual(['file1.txt', 'file2.txt']);
|
|
66
|
+
});
|
|
67
|
+
test('stat file from bundle', async () => {
|
|
68
|
+
const bundle = new MemoryBundle();
|
|
69
|
+
await bundle.write('file1.txt', Buffer.from('hello'));
|
|
70
|
+
const stat = await bundle.stat('file1.txt');
|
|
71
|
+
expect(stat.sizeBytes).toBe(5);
|
|
72
|
+
expect(stat.sha256Hex).toBe('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824');
|
|
73
|
+
});
|
|
74
|
+
test('stat missing file from bundle', async () => {
|
|
75
|
+
const bundle = new MemoryBundle();
|
|
76
|
+
expect(bundle.stat('file1.txt')).rejects.toThrowError('File not found: file1.txt');
|
|
77
|
+
});
|
|
78
|
+
test('hash bundle', async () => {
|
|
79
|
+
const bundle = new MemoryBundle();
|
|
80
|
+
await bundle.write('file1.txt', Buffer.from('hello'));
|
|
81
|
+
await bundle.write('file2.txt', Buffer.from('world'));
|
|
82
|
+
const hash = await bundle.hash();
|
|
83
|
+
expect(hash).toBe('936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af');
|
|
84
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ApplicationsRequest, ApplicationsResponse, BootstrapRequest, BootstrapResponse, CreateApplicationsRequest, CreateApplicationsResponse, CreateVersionRequest, CreateVersionResponse, DeleteApplicationsRequest, DeleteApplicationsResponse, GetEnvRequest, GetEnvResponse, QueryResourcesRequest, QueryResourcesResponse, SetApplicationActiveStatesRequest, SetApplicationActiveStatesResponse, SetEnvRequest, SetEnvResponse, StatBundleRequest, StatBundleResponse, UploadBundleRequest, UploadBundleResponse, VersionsRequest, VersionsResponse } from "./catalog_pb.js";
|
|
2
|
+
import { MethodKind } from "@bufbuild/protobuf";
|
|
3
|
+
/**
|
|
4
|
+
* CatalogService defines data interactions for the catalog portion of the
|
|
5
|
+
* rainbow UI.
|
|
6
|
+
*
|
|
7
|
+
* As an authorization style choice, the RPCs are focused on providing user
|
|
8
|
+
* context data explicitly (don't assume the userId or orgId from the
|
|
9
|
+
* authentication header.) It is expected that an authorization layer will
|
|
10
|
+
* enforce the ability for a request to act as a specified user or org matching
|
|
11
|
+
* against the authentication header. This makes services easier to mock without
|
|
12
|
+
* needing to think about injecting mock authentication contexts and allows for
|
|
13
|
+
* use cases where certain roles of users may wish to act as other users.
|
|
14
|
+
*
|
|
15
|
+
* @generated from service liquidmetal.v1alpha1.CatalogService
|
|
16
|
+
*/
|
|
17
|
+
export declare const CatalogService: {
|
|
18
|
+
readonly typeName: "liquidmetal.v1alpha1.CatalogService";
|
|
19
|
+
readonly methods: {
|
|
20
|
+
/**
|
|
21
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.Versions
|
|
22
|
+
*/
|
|
23
|
+
readonly versions: {
|
|
24
|
+
readonly name: "Versions";
|
|
25
|
+
readonly I: typeof VersionsRequest;
|
|
26
|
+
readonly O: typeof VersionsResponse;
|
|
27
|
+
readonly kind: MethodKind.Unary;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.CreateVersion
|
|
31
|
+
*/
|
|
32
|
+
readonly createVersion: {
|
|
33
|
+
readonly name: "CreateVersion";
|
|
34
|
+
readonly I: typeof CreateVersionRequest;
|
|
35
|
+
readonly O: typeof CreateVersionResponse;
|
|
36
|
+
readonly kind: MethodKind.Unary;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Applications fetches a list of applications for an organization.
|
|
40
|
+
* This list follows best practices for pagination.
|
|
41
|
+
* https://protobuf.dev/programming-guides/api/#define-pagination-api
|
|
42
|
+
*
|
|
43
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.Applications
|
|
44
|
+
*/
|
|
45
|
+
readonly applications: {
|
|
46
|
+
readonly name: "Applications";
|
|
47
|
+
readonly I: typeof ApplicationsRequest;
|
|
48
|
+
readonly O: typeof ApplicationsResponse;
|
|
49
|
+
readonly kind: MethodKind.Unary;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.CreateApplications
|
|
53
|
+
*/
|
|
54
|
+
readonly createApplications: {
|
|
55
|
+
readonly name: "CreateApplications";
|
|
56
|
+
readonly I: typeof CreateApplicationsRequest;
|
|
57
|
+
readonly O: typeof CreateApplicationsResponse;
|
|
58
|
+
readonly kind: MethodKind.Unary;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.SetApplicationActiveStates
|
|
62
|
+
*/
|
|
63
|
+
readonly setApplicationActiveStates: {
|
|
64
|
+
readonly name: "SetApplicationActiveStates";
|
|
65
|
+
readonly I: typeof SetApplicationActiveStatesRequest;
|
|
66
|
+
readonly O: typeof SetApplicationActiveStatesResponse;
|
|
67
|
+
readonly kind: MethodKind.Unary;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.DeleteApplications
|
|
71
|
+
*/
|
|
72
|
+
readonly deleteApplications: {
|
|
73
|
+
readonly name: "DeleteApplications";
|
|
74
|
+
readonly I: typeof DeleteApplicationsRequest;
|
|
75
|
+
readonly O: typeof DeleteApplicationsResponse;
|
|
76
|
+
readonly kind: MethodKind.Unary;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* UploadBundle uploads a bundle for a specific application version.
|
|
80
|
+
*
|
|
81
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.UploadBundle
|
|
82
|
+
*/
|
|
83
|
+
readonly uploadBundle: {
|
|
84
|
+
readonly name: "UploadBundle";
|
|
85
|
+
readonly I: typeof UploadBundleRequest;
|
|
86
|
+
readonly O: typeof UploadBundleResponse;
|
|
87
|
+
readonly kind: MethodKind.Unary;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* StatBundle returns the metadata for a bundle.
|
|
91
|
+
*
|
|
92
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.StatBundle
|
|
93
|
+
*/
|
|
94
|
+
readonly statBundle: {
|
|
95
|
+
readonly name: "StatBundle";
|
|
96
|
+
readonly I: typeof StatBundleRequest;
|
|
97
|
+
readonly O: typeof StatBundleResponse;
|
|
98
|
+
readonly kind: MethodKind.Unary;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* SetEnv sets an environment variable for an application/version.
|
|
102
|
+
*
|
|
103
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.SetEnv
|
|
104
|
+
*/
|
|
105
|
+
readonly setEnv: {
|
|
106
|
+
readonly name: "SetEnv";
|
|
107
|
+
readonly I: typeof SetEnvRequest;
|
|
108
|
+
readonly O: typeof SetEnvResponse;
|
|
109
|
+
readonly kind: MethodKind.Unary;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* GetEnv gets an environment variable for an application/version.
|
|
113
|
+
*
|
|
114
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.GetEnv
|
|
115
|
+
*/
|
|
116
|
+
readonly getEnv: {
|
|
117
|
+
readonly name: "GetEnv";
|
|
118
|
+
readonly I: typeof GetEnvRequest;
|
|
119
|
+
readonly O: typeof GetEnvResponse;
|
|
120
|
+
readonly kind: MethodKind.Unary;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* QueryResources returns the physical underlying resources for a given query
|
|
124
|
+
*
|
|
125
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.QueryResources
|
|
126
|
+
*/
|
|
127
|
+
readonly queryResources: {
|
|
128
|
+
readonly name: "QueryResources";
|
|
129
|
+
readonly I: typeof QueryResourcesRequest;
|
|
130
|
+
readonly O: typeof QueryResourcesResponse;
|
|
131
|
+
readonly kind: MethodKind.Unary;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Bootstrap is a special RPC that is used to bootstrap the system
|
|
135
|
+
* using a one-time token.
|
|
136
|
+
*
|
|
137
|
+
* @generated from rpc liquidmetal.v1alpha1.CatalogService.Bootstrap
|
|
138
|
+
*/
|
|
139
|
+
readonly bootstrap: {
|
|
140
|
+
readonly name: "Bootstrap";
|
|
141
|
+
readonly I: typeof BootstrapRequest;
|
|
142
|
+
readonly O: typeof BootstrapResponse;
|
|
143
|
+
readonly kind: MethodKind.Unary;
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=catalog_connect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog_connect.d.ts","sourceRoot":"","sources":["../../../src/liquidmetal/v1alpha1/catalog_connect.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,aAAa,EAAE,cAAc,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,iCAAiC,EAAE,kCAAkC,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrjB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc;;;QAGvB;;WAEG;;;;;;;QAOH;;WAEG;;;;;;;QAOH;;;;;;WAMG;;;;;;;QAOH;;WAEG;;;;;;;QAOH;;WAEG;;;;;;;QAOH;;WAEG;;;;;;;QAOH;;;;WAIG;;;;;;;QAOH;;;;WAIG;;;;;;;QAOH;;;;WAIG;;;;;;;QAOH;;;;WAIG;;;;;;;QAOH;;;;WAIG;;;;;;;QAOH;;;;;WAKG;;;;;;;;CAQG,CAAC"}
|