@positronic/cli 0.0.2 → 0.0.4

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 (83) hide show
  1. package/dist/src/cli.js +16 -1
  2. package/dist/src/commands/helpers.js +11 -25
  3. package/dist/types/cli.d.ts.map +1 -1
  4. package/dist/types/commands/helpers.d.ts.map +1 -1
  5. package/package.json +11 -4
  6. package/dist/src/commands/brain.test.js +0 -2936
  7. package/dist/src/commands/helpers.test.js +0 -832
  8. package/dist/src/commands/project.test.js +0 -1201
  9. package/dist/src/commands/resources.test.js +0 -2511
  10. package/dist/src/commands/schedule.test.js +0 -1235
  11. package/dist/src/commands/secret.test.d.js +0 -1
  12. package/dist/src/commands/secret.test.js +0 -761
  13. package/dist/src/commands/server.test.js +0 -1237
  14. package/dist/src/commands/test-utils.js +0 -737
  15. package/dist/src/components/secret-sync.js +0 -303
  16. package/dist/src/test/mock-api-client.js +0 -371
  17. package/dist/src/test/test-dev-server.js +0 -1376
  18. package/dist/types/commands/test-utils.d.ts +0 -45
  19. package/dist/types/commands/test-utils.d.ts.map +0 -1
  20. package/dist/types/components/secret-sync.d.ts +0 -9
  21. package/dist/types/components/secret-sync.d.ts.map +0 -1
  22. package/dist/types/test/mock-api-client.d.ts +0 -25
  23. package/dist/types/test/mock-api-client.d.ts.map +0 -1
  24. package/dist/types/test/test-dev-server.d.ts +0 -129
  25. package/dist/types/test/test-dev-server.d.ts.map +0 -1
  26. package/src/cli.ts +0 -981
  27. package/src/commands/backend.ts +0 -63
  28. package/src/commands/brain.test.ts +0 -1004
  29. package/src/commands/brain.ts +0 -215
  30. package/src/commands/helpers.test.ts +0 -487
  31. package/src/commands/helpers.ts +0 -870
  32. package/src/commands/project-config-manager.ts +0 -152
  33. package/src/commands/project.test.ts +0 -502
  34. package/src/commands/project.ts +0 -109
  35. package/src/commands/resources.test.ts +0 -1052
  36. package/src/commands/resources.ts +0 -97
  37. package/src/commands/schedule.test.ts +0 -481
  38. package/src/commands/schedule.ts +0 -65
  39. package/src/commands/secret.test.ts +0 -210
  40. package/src/commands/secret.ts +0 -50
  41. package/src/commands/server.test.ts +0 -493
  42. package/src/commands/server.ts +0 -353
  43. package/src/commands/test-utils.ts +0 -324
  44. package/src/components/brain-history.tsx +0 -198
  45. package/src/components/brain-list.tsx +0 -105
  46. package/src/components/brain-rerun.tsx +0 -111
  47. package/src/components/brain-show.tsx +0 -92
  48. package/src/components/error.tsx +0 -24
  49. package/src/components/project-add.tsx +0 -59
  50. package/src/components/project-create.tsx +0 -83
  51. package/src/components/project-list.tsx +0 -83
  52. package/src/components/project-remove.tsx +0 -55
  53. package/src/components/project-select.tsx +0 -200
  54. package/src/components/project-show.tsx +0 -58
  55. package/src/components/resource-clear.tsx +0 -127
  56. package/src/components/resource-delete.tsx +0 -160
  57. package/src/components/resource-list.tsx +0 -177
  58. package/src/components/resource-sync.tsx +0 -170
  59. package/src/components/resource-types.tsx +0 -55
  60. package/src/components/resource-upload.tsx +0 -182
  61. package/src/components/schedule-create.tsx +0 -90
  62. package/src/components/schedule-delete.tsx +0 -116
  63. package/src/components/schedule-list.tsx +0 -186
  64. package/src/components/schedule-runs.tsx +0 -151
  65. package/src/components/secret-bulk.tsx +0 -79
  66. package/src/components/secret-create.tsx +0 -49
  67. package/src/components/secret-delete.tsx +0 -41
  68. package/src/components/secret-list.tsx +0 -41
  69. package/src/components/watch.tsx +0 -155
  70. package/src/hooks/useApi.ts +0 -183
  71. package/src/positronic.ts +0 -40
  72. package/src/test/data/resources/config.json +0 -1
  73. package/src/test/data/resources/data/config.json +0 -1
  74. package/src/test/data/resources/data/logo.png +0 -2
  75. package/src/test/data/resources/docs/api.md +0 -3
  76. package/src/test/data/resources/docs/readme.md +0 -3
  77. package/src/test/data/resources/example.md +0 -3
  78. package/src/test/data/resources/file with spaces.txt +0 -1
  79. package/src/test/data/resources/readme.md +0 -3
  80. package/src/test/data/resources/test.txt +0 -1
  81. package/src/test/mock-api-client.ts +0 -145
  82. package/src/test/test-dev-server.ts +0 -1003
  83. package/tsconfig.json +0 -11
@@ -1,832 +0,0 @@
1
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
2
- try {
3
- var info = gen[key](arg);
4
- var value = info.value;
5
- } catch (error) {
6
- reject(error);
7
- return;
8
- }
9
- if (info.done) {
10
- resolve(value);
11
- } else {
12
- Promise.resolve(value).then(_next, _throw);
13
- }
14
- }
15
- function _async_to_generator(fn) {
16
- return function() {
17
- var self = this, args = arguments;
18
- return new Promise(function(resolve, reject) {
19
- var gen = fn.apply(self, args);
20
- function _next(value) {
21
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
22
- }
23
- function _throw(err) {
24
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
25
- }
26
- _next(undefined);
27
- });
28
- };
29
- }
30
- function _instanceof(left, right) {
31
- if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
32
- return !!right[Symbol.hasInstance](left);
33
- } else {
34
- return left instanceof right;
35
- }
36
- }
37
- function _ts_generator(thisArg, body) {
38
- var f, y, t, _ = {
39
- label: 0,
40
- sent: function() {
41
- if (t[0] & 1) throw t[1];
42
- return t[1];
43
- },
44
- trys: [],
45
- ops: []
46
- }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
47
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
48
- return this;
49
- }), g;
50
- function verb(n) {
51
- return function(v) {
52
- return step([
53
- n,
54
- v
55
- ]);
56
- };
57
- }
58
- function step(op) {
59
- if (f) throw new TypeError("Generator is already executing.");
60
- while(g && (g = 0, op[0] && (_ = 0)), _)try {
61
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
62
- if (y = 0, t) op = [
63
- op[0] & 2,
64
- t.value
65
- ];
66
- switch(op[0]){
67
- case 0:
68
- case 1:
69
- t = op;
70
- break;
71
- case 4:
72
- _.label++;
73
- return {
74
- value: op[1],
75
- done: false
76
- };
77
- case 5:
78
- _.label++;
79
- y = op[1];
80
- op = [
81
- 0
82
- ];
83
- continue;
84
- case 7:
85
- op = _.ops.pop();
86
- _.trys.pop();
87
- continue;
88
- default:
89
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
90
- _ = 0;
91
- continue;
92
- }
93
- if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
94
- _.label = op[1];
95
- break;
96
- }
97
- if (op[0] === 6 && _.label < t[1]) {
98
- _.label = t[1];
99
- t = op;
100
- break;
101
- }
102
- if (t && _.label < t[2]) {
103
- _.label = t[2];
104
- _.ops.push(op);
105
- break;
106
- }
107
- if (t[2]) _.ops.pop();
108
- _.trys.pop();
109
- continue;
110
- }
111
- op = body.call(thisArg, _);
112
- } catch (e) {
113
- op = [
114
- 6,
115
- e
116
- ];
117
- y = 0;
118
- } finally{
119
- f = t = 0;
120
- }
121
- if (op[0] & 5) throw op[1];
122
- return {
123
- value: op[0] ? op[1] : void 0,
124
- done: true
125
- };
126
- }
127
- }
128
- import * as fs from 'fs';
129
- import * as path from 'path';
130
- import * as os from 'os';
131
- import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
132
- import { Response } from 'node-fetch';
133
- import { syncResources, generateTypes } from './helpers.js';
134
- import { createMockApiClient } from '../test/mock-api-client.js';
135
- describe('Helper Functions Unit Tests', function() {
136
- var tempDir;
137
- var projectPath;
138
- var mockClient;
139
- beforeEach(function() {
140
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'positronic-unit-test-'));
141
- projectPath = path.join(tempDir, 'test-project');
142
- fs.mkdirSync(projectPath, {
143
- recursive: true
144
- });
145
- mockClient = createMockApiClient();
146
- });
147
- afterEach(function() {
148
- fs.rmSync(tempDir, {
149
- recursive: true,
150
- force: true
151
- });
152
- mockClient.reset();
153
- });
154
- describe('syncResources', function() {
155
- it('should create resources directory if it does not exist', function() {
156
- return _async_to_generator(function() {
157
- var resourcesDir;
158
- return _ts_generator(this, function(_state) {
159
- switch(_state.label){
160
- case 0:
161
- resourcesDir = path.join(projectPath, 'resources');
162
- expect(fs.existsSync(resourcesDir)).toBe(false);
163
- return [
164
- 4,
165
- syncResources(projectPath, mockClient)
166
- ];
167
- case 1:
168
- _state.sent();
169
- expect(fs.existsSync(resourcesDir)).toBe(true);
170
- return [
171
- 2
172
- ];
173
- }
174
- });
175
- })();
176
- });
177
- it('should return zero counts for empty resources directory', function() {
178
- return _async_to_generator(function() {
179
- var resourcesDir, result;
180
- return _ts_generator(this, function(_state) {
181
- switch(_state.label){
182
- case 0:
183
- resourcesDir = path.join(projectPath, 'resources');
184
- fs.mkdirSync(resourcesDir, {
185
- recursive: true
186
- });
187
- return [
188
- 4,
189
- syncResources(projectPath, mockClient)
190
- ];
191
- case 1:
192
- result = _state.sent();
193
- expect(result).toEqual({
194
- uploadCount: 0,
195
- skipCount: 0,
196
- errorCount: 0,
197
- totalCount: 0,
198
- deleteCount: 0,
199
- errors: []
200
- });
201
- // Should not make any API calls for empty directory
202
- expect(mockClient.calls.length).toBe(1); // Only GET /resources call
203
- return [
204
- 2
205
- ];
206
- }
207
- });
208
- })();
209
- });
210
- it('should upload new resources', function() {
211
- return _async_to_generator(function() {
212
- var _mockClient_calls__options, resourcesDir, result, resources;
213
- return _ts_generator(this, function(_state) {
214
- switch(_state.label){
215
- case 0:
216
- resourcesDir = path.join(projectPath, 'resources');
217
- fs.mkdirSync(resourcesDir, {
218
- recursive: true
219
- });
220
- // Create test files
221
- fs.writeFileSync(path.join(resourcesDir, 'test.txt'), 'Hello World');
222
- fs.writeFileSync(path.join(resourcesDir, 'data.json'), '{"key": "value"}');
223
- return [
224
- 4,
225
- syncResources(projectPath, mockClient)
226
- ];
227
- case 1:
228
- result = _state.sent();
229
- expect(result.uploadCount).toBe(2);
230
- expect(result.skipCount).toBe(0);
231
- expect(result.errorCount).toBe(0);
232
- expect(result.totalCount).toBe(2);
233
- // Verify API calls
234
- expect(mockClient.calls).toHaveLength(3); // 1 GET + 2 POSTs
235
- expect(mockClient.calls[0].path).toBe('/resources');
236
- expect(mockClient.calls[1].path).toBe('/resources');
237
- expect((_mockClient_calls__options = mockClient.calls[1].options) === null || _mockClient_calls__options === void 0 ? void 0 : _mockClient_calls__options.method).toBe('POST');
238
- // Verify uploaded resources
239
- resources = mockClient.getResources();
240
- expect(resources).toHaveLength(2);
241
- expect(resources.find(function(r) {
242
- return r.key === 'test.txt';
243
- })).toBeDefined();
244
- expect(resources.find(function(r) {
245
- return r.key === 'data.json';
246
- })).toBeDefined();
247
- return [
248
- 2
249
- ];
250
- }
251
- });
252
- })();
253
- });
254
- it('should skip unchanged resources', function() {
255
- return _async_to_generator(function() {
256
- var resourcesDir, result;
257
- return _ts_generator(this, function(_state) {
258
- switch(_state.label){
259
- case 0:
260
- resourcesDir = path.join(projectPath, 'resources');
261
- fs.mkdirSync(resourcesDir, {
262
- recursive: true
263
- });
264
- // Add existing resource to mock with future lastModified to ensure it's newer than the file
265
- mockClient.addResource({
266
- key: 'existing.txt',
267
- type: 'text',
268
- size: 8,
269
- lastModified: new Date(Date.now() + 10000).toISOString(),
270
- local: false
271
- });
272
- // Create matching file
273
- fs.writeFileSync(path.join(resourcesDir, 'existing.txt'), '12345678'); // 8 bytes
274
- return [
275
- 4,
276
- syncResources(projectPath, mockClient)
277
- ];
278
- case 1:
279
- result = _state.sent();
280
- expect(result.uploadCount).toBe(0);
281
- expect(result.skipCount).toBe(1);
282
- expect(result.errorCount).toBe(0);
283
- // Should only make GET call
284
- expect(mockClient.calls).toHaveLength(1);
285
- expect(mockClient.calls[0].path).toBe('/resources');
286
- return [
287
- 2
288
- ];
289
- }
290
- });
291
- })();
292
- });
293
- it('should upload modified resources based on size', function() {
294
- return _async_to_generator(function() {
295
- var resourcesDir, result;
296
- return _ts_generator(this, function(_state) {
297
- switch(_state.label){
298
- case 0:
299
- resourcesDir = path.join(projectPath, 'resources');
300
- fs.mkdirSync(resourcesDir, {
301
- recursive: true
302
- });
303
- // Add existing resource to mock with different size
304
- mockClient.addResource({
305
- key: 'modified.txt',
306
- type: 'text',
307
- size: 5,
308
- lastModified: new Date(Date.now() - 10000).toISOString(),
309
- local: false
310
- });
311
- // Create file with different content
312
- fs.writeFileSync(path.join(resourcesDir, 'modified.txt'), 'New content');
313
- return [
314
- 4,
315
- syncResources(projectPath, mockClient)
316
- ];
317
- case 1:
318
- result = _state.sent();
319
- expect(result.uploadCount).toBe(1);
320
- expect(result.skipCount).toBe(0);
321
- return [
322
- 2
323
- ];
324
- }
325
- });
326
- })();
327
- });
328
- it('should upload resources based on modification time when size matches', function() {
329
- return _async_to_generator(function() {
330
- var resourcesDir, result;
331
- return _ts_generator(this, function(_state) {
332
- switch(_state.label){
333
- case 0:
334
- resourcesDir = path.join(projectPath, 'resources');
335
- fs.mkdirSync(resourcesDir, {
336
- recursive: true
337
- });
338
- // Add existing resource to mock with same size but older timestamp
339
- mockClient.addResource({
340
- key: 'updated.txt',
341
- type: 'text',
342
- size: 11,
343
- lastModified: new Date(Date.now() - 10000).toISOString(),
344
- local: false
345
- });
346
- // Create file with same size but it will have a newer modification time
347
- fs.writeFileSync(path.join(resourcesDir, 'updated.txt'), 'Same content'); // 11 bytes
348
- return [
349
- 4,
350
- syncResources(projectPath, mockClient)
351
- ];
352
- case 1:
353
- result = _state.sent();
354
- expect(result.uploadCount).toBe(1);
355
- expect(result.skipCount).toBe(0);
356
- return [
357
- 2
358
- ];
359
- }
360
- });
361
- })();
362
- });
363
- it('should handle nested directories', function() {
364
- return _async_to_generator(function() {
365
- var resourcesDir, docsDir, result, resources;
366
- return _ts_generator(this, function(_state) {
367
- switch(_state.label){
368
- case 0:
369
- resourcesDir = path.join(projectPath, 'resources');
370
- docsDir = path.join(resourcesDir, 'docs');
371
- fs.mkdirSync(docsDir, {
372
- recursive: true
373
- });
374
- fs.writeFileSync(path.join(docsDir, 'readme.md'), '# README');
375
- return [
376
- 4,
377
- syncResources(projectPath, mockClient)
378
- ];
379
- case 1:
380
- result = _state.sent();
381
- expect(result.uploadCount).toBe(1);
382
- resources = mockClient.getResources();
383
- expect(resources[0].key).toBe('docs/readme.md');
384
- return [
385
- 2
386
- ];
387
- }
388
- });
389
- })();
390
- });
391
- it('should handle upload errors gracefully', function() {
392
- return _async_to_generator(function() {
393
- var resourcesDir, badClient, originalFetch, result;
394
- return _ts_generator(this, function(_state) {
395
- switch(_state.label){
396
- case 0:
397
- resourcesDir = path.join(projectPath, 'resources');
398
- fs.mkdirSync(resourcesDir, {
399
- recursive: true
400
- });
401
- // Create a file that will trigger an error in our mock
402
- // We'll modify the mock to fail on specific file names
403
- badClient = createMockApiClient();
404
- originalFetch = badClient.fetch;
405
- badClient.fetch = function(path, options) {
406
- return _async_to_generator(function() {
407
- return _ts_generator(this, function(_state) {
408
- if ((options === null || options === void 0 ? void 0 : options.method) === 'POST' && _instanceof(options.body, FormData) && options.body.get('key') === 'error.txt') {
409
- throw new Error('Upload failed');
410
- }
411
- return [
412
- 2,
413
- originalFetch.call(badClient, path, options)
414
- ];
415
- });
416
- })();
417
- };
418
- fs.writeFileSync(path.join(resourcesDir, 'error.txt'), 'This will fail');
419
- fs.writeFileSync(path.join(resourcesDir, 'good.txt'), 'This will succeed');
420
- return [
421
- 4,
422
- syncResources(projectPath, badClient)
423
- ];
424
- case 1:
425
- result = _state.sent();
426
- expect(result.uploadCount).toBe(1);
427
- expect(result.errorCount).toBe(1);
428
- expect(result.errors).toHaveLength(1);
429
- expect(result.errors[0]).toEqual({
430
- file: 'error.txt',
431
- message: 'Upload failed'
432
- });
433
- return [
434
- 2
435
- ];
436
- }
437
- });
438
- })();
439
- });
440
- it('should delete server resources with local=true when files are removed', function() {
441
- return _async_to_generator(function() {
442
- var resourcesDir, result, remainingResources, deleteCalls;
443
- return _ts_generator(this, function(_state) {
444
- switch(_state.label){
445
- case 0:
446
- resourcesDir = path.join(projectPath, 'resources');
447
- fs.mkdirSync(resourcesDir, {
448
- recursive: true
449
- });
450
- // Create the file that should still exist first
451
- fs.writeFileSync(path.join(resourcesDir, 'still-exists.txt'), 'I still exist');
452
- // Add existing resources to mock - some synced (local=true), some manual (local=false)
453
- mockClient.addResource({
454
- key: 'deleted-file.txt',
455
- type: 'text',
456
- size: 100,
457
- lastModified: new Date().toISOString(),
458
- local: true
459
- });
460
- mockClient.addResource({
461
- key: 'still-exists.txt',
462
- type: 'text',
463
- size: 13,
464
- lastModified: new Date(Date.now() + 10000).toISOString(),
465
- local: true
466
- });
467
- mockClient.addResource({
468
- key: 'docs/removed-doc.md',
469
- type: 'text',
470
- size: 200,
471
- lastModified: new Date().toISOString(),
472
- local: true
473
- });
474
- return [
475
- 4,
476
- syncResources(projectPath, mockClient)
477
- ];
478
- case 1:
479
- result = _state.sent();
480
- expect(result.deleteCount).toBe(2);
481
- expect(result.uploadCount).toBe(0); // No new uploads
482
- expect(result.skipCount).toBe(1); // still-exists.txt is up to date
483
- // Verify the correct resources were deleted
484
- remainingResources = mockClient.getResources();
485
- expect(remainingResources).toHaveLength(1);
486
- expect(remainingResources[0].key).toBe('still-exists.txt');
487
- // Verify DELETE API calls were made
488
- deleteCalls = mockClient.calls.filter(function(call) {
489
- var _call_options;
490
- return ((_call_options = call.options) === null || _call_options === void 0 ? void 0 : _call_options.method) === 'DELETE';
491
- });
492
- expect(deleteCalls).toHaveLength(2);
493
- expect(deleteCalls[0].path).toBe('/resources/deleted-file.txt');
494
- expect(deleteCalls[1].path).toBe('/resources/docs%2Fremoved-doc.md'); // URL encoded
495
- return [
496
- 2
497
- ];
498
- }
499
- });
500
- })();
501
- });
502
- it('should preserve manually uploaded resources (local=false)', function() {
503
- return _async_to_generator(function() {
504
- var resourcesDir, result, remainingResources, deleteCalls;
505
- return _ts_generator(this, function(_state) {
506
- switch(_state.label){
507
- case 0:
508
- resourcesDir = path.join(projectPath, 'resources');
509
- fs.mkdirSync(resourcesDir, {
510
- recursive: true
511
- });
512
- // Add existing resources to mock
513
- mockClient.addResource({
514
- key: 'manual-upload.txt',
515
- type: 'text',
516
- size: 100,
517
- lastModified: new Date().toISOString(),
518
- local: false
519
- });
520
- mockClient.addResource({
521
- key: 'another-manual.png',
522
- type: 'binary',
523
- size: 5000,
524
- lastModified: new Date().toISOString(),
525
- local: false
526
- });
527
- mockClient.addResource({
528
- key: 'synced-file.txt',
529
- type: 'text',
530
- size: 75,
531
- lastModified: new Date().toISOString(),
532
- local: true
533
- });
534
- return [
535
- 4,
536
- syncResources(projectPath, mockClient)
537
- ];
538
- case 1:
539
- result = _state.sent();
540
- expect(result.deleteCount).toBe(1); // Only the synced file
541
- expect(result.uploadCount).toBe(0);
542
- expect(result.skipCount).toBe(0);
543
- // Verify manual uploads are still there
544
- remainingResources = mockClient.getResources();
545
- expect(remainingResources).toHaveLength(2);
546
- expect(remainingResources.find(function(r) {
547
- return r.key === 'manual-upload.txt';
548
- })).toBeDefined();
549
- expect(remainingResources.find(function(r) {
550
- return r.key === 'another-manual.png';
551
- })).toBeDefined();
552
- expect(remainingResources.find(function(r) {
553
- return r.key === 'synced-file.txt';
554
- })).toBeUndefined();
555
- // Verify only one DELETE call was made
556
- deleteCalls = mockClient.calls.filter(function(call) {
557
- var _call_options;
558
- return ((_call_options = call.options) === null || _call_options === void 0 ? void 0 : _call_options.method) === 'DELETE';
559
- });
560
- expect(deleteCalls).toHaveLength(1);
561
- expect(deleteCalls[0].path).toBe('/resources/synced-file.txt');
562
- return [
563
- 2
564
- ];
565
- }
566
- });
567
- })();
568
- });
569
- it('should handle delete errors gracefully', function() {
570
- return _async_to_generator(function() {
571
- var resourcesDir, badClient, originalFetch, result;
572
- return _ts_generator(this, function(_state) {
573
- switch(_state.label){
574
- case 0:
575
- resourcesDir = path.join(projectPath, 'resources');
576
- fs.mkdirSync(resourcesDir, {
577
- recursive: true
578
- });
579
- // Create a client that fails on DELETE requests
580
- badClient = createMockApiClient();
581
- badClient.addResource({
582
- key: 'will-fail-delete.txt',
583
- type: 'text',
584
- size: 100,
585
- lastModified: new Date().toISOString(),
586
- local: true
587
- });
588
- originalFetch = badClient.fetch;
589
- badClient.fetch = function(path, options) {
590
- return _async_to_generator(function() {
591
- return _ts_generator(this, function(_state) {
592
- if ((options === null || options === void 0 ? void 0 : options.method) === 'DELETE') {
593
- return [
594
- 2,
595
- new Response('Internal Server Error', {
596
- status: 500
597
- })
598
- ];
599
- }
600
- return [
601
- 2,
602
- originalFetch.call(badClient, path, options)
603
- ];
604
- });
605
- })();
606
- };
607
- return [
608
- 4,
609
- syncResources(projectPath, badClient)
610
- ];
611
- case 1:
612
- result = _state.sent();
613
- expect(result.deleteCount).toBe(0); // Failed to delete
614
- expect(result.errorCount).toBe(1);
615
- expect(result.errors).toHaveLength(1);
616
- expect(result.errors[0]).toEqual({
617
- file: 'will-fail-delete.txt',
618
- message: 'Failed to delete: Delete failed: 500 Internal Server Error'
619
- });
620
- return [
621
- 2
622
- ];
623
- }
624
- });
625
- })();
626
- });
627
- });
628
- describe('generateTypes', function() {
629
- it('should generate types file for empty resources', function() {
630
- return _async_to_generator(function() {
631
- var result, typesPath, content;
632
- return _ts_generator(this, function(_state) {
633
- switch(_state.label){
634
- case 0:
635
- return [
636
- 4,
637
- generateTypes(projectPath, mockClient)
638
- ];
639
- case 1:
640
- result = _state.sent();
641
- typesPath = path.join(projectPath, 'resources.d.ts');
642
- expect(fs.existsSync(typesPath)).toBe(true);
643
- content = fs.readFileSync(typesPath, 'utf-8');
644
- expect(content).toContain("declare module '@positronic/core'");
645
- expect(content).toContain('interface Resources');
646
- expect(content).toContain('loadText(path: string): Promise<string>');
647
- expect(content).toContain('loadBinary(path: string): Promise<Buffer>');
648
- return [
649
- 2
650
- ];
651
- }
652
- });
653
- })();
654
- });
655
- it('should generate types for text and binary resources', function() {
656
- return _async_to_generator(function() {
657
- var typesPath, content;
658
- return _ts_generator(this, function(_state) {
659
- switch(_state.label){
660
- case 0:
661
- // Add mock resources
662
- mockClient.addResource({
663
- key: 'readme.md',
664
- type: 'text',
665
- size: 100,
666
- lastModified: new Date().toISOString(),
667
- local: false
668
- });
669
- mockClient.addResource({
670
- key: 'logo.png',
671
- type: 'binary',
672
- size: 1000,
673
- lastModified: new Date().toISOString(),
674
- local: false
675
- });
676
- mockClient.addResource({
677
- key: 'data.json',
678
- type: 'text',
679
- size: 50,
680
- lastModified: new Date().toISOString(),
681
- local: false
682
- });
683
- return [
684
- 4,
685
- generateTypes(projectPath, mockClient)
686
- ];
687
- case 1:
688
- _state.sent();
689
- typesPath = path.join(projectPath, 'resources.d.ts');
690
- content = fs.readFileSync(typesPath, 'utf-8');
691
- // Check resource declarations
692
- expect(content).toContain('readme: TextResource;');
693
- expect(content).toContain('logo: BinaryResource;');
694
- expect(content).toContain('data: TextResource;');
695
- return [
696
- 2
697
- ];
698
- }
699
- });
700
- })();
701
- });
702
- it('should handle nested resources', function() {
703
- return _async_to_generator(function() {
704
- var typesPath, content;
705
- return _ts_generator(this, function(_state) {
706
- switch(_state.label){
707
- case 0:
708
- mockClient.addResource({
709
- key: 'docs/api.md',
710
- type: 'text',
711
- size: 100,
712
- lastModified: new Date().toISOString(),
713
- local: false
714
- });
715
- mockClient.addResource({
716
- key: 'docs/images/diagram.png',
717
- type: 'binary',
718
- size: 500,
719
- lastModified: new Date().toISOString(),
720
- local: false
721
- });
722
- mockClient.addResource({
723
- key: 'config/settings.json',
724
- type: 'text',
725
- size: 200,
726
- lastModified: new Date().toISOString(),
727
- local: false
728
- });
729
- return [
730
- 4,
731
- generateTypes(projectPath, mockClient)
732
- ];
733
- case 1:
734
- _state.sent();
735
- typesPath = path.join(projectPath, 'resources.d.ts');
736
- content = fs.readFileSync(typesPath, 'utf-8');
737
- // Check nested structure
738
- expect(content).toContain('docs: {');
739
- expect(content).toContain('api: TextResource;');
740
- expect(content).toContain('images: {');
741
- expect(content).toContain('diagram: BinaryResource;');
742
- expect(content).toContain('config: {');
743
- expect(content).toContain('settings: TextResource;');
744
- return [
745
- 2
746
- ];
747
- }
748
- });
749
- })();
750
- });
751
- it('should exclude invalid JavaScript identifiers', function() {
752
- return _async_to_generator(function() {
753
- var typesPath, content;
754
- return _ts_generator(this, function(_state) {
755
- switch(_state.label){
756
- case 0:
757
- mockClient.addResource({
758
- key: '123invalid.txt',
759
- type: 'text',
760
- size: 100,
761
- lastModified: new Date().toISOString(),
762
- local: false
763
- });
764
- mockClient.addResource({
765
- key: 'file-with-dash.txt',
766
- type: 'text',
767
- size: 100,
768
- lastModified: new Date().toISOString(),
769
- local: false
770
- });
771
- mockClient.addResource({
772
- key: 'valid_file.txt',
773
- type: 'text',
774
- size: 100,
775
- lastModified: new Date().toISOString(),
776
- local: false
777
- });
778
- return [
779
- 4,
780
- generateTypes(projectPath, mockClient)
781
- ];
782
- case 1:
783
- _state.sent();
784
- typesPath = path.join(projectPath, 'resources.d.ts');
785
- content = fs.readFileSync(typesPath, 'utf-8');
786
- // Should include valid identifier
787
- expect(content).toContain('valid_file: TextResource;');
788
- // Should not include invalid identifiers
789
- expect(content).not.toContain('123invalid');
790
- expect(content).not.toContain('file-with-dash');
791
- return [
792
- 2
793
- ];
794
- }
795
- });
796
- })();
797
- });
798
- it('should handle API errors gracefully', function() {
799
- return _async_to_generator(function() {
800
- var errorClient;
801
- return _ts_generator(this, function(_state) {
802
- switch(_state.label){
803
- case 0:
804
- // Create a client that returns an error
805
- errorClient = createMockApiClient();
806
- errorClient.fetch = function(path, options) {
807
- return _async_to_generator(function() {
808
- return _ts_generator(this, function(_state) {
809
- return [
810
- 2,
811
- new Response('Internal Server Error', {
812
- status: 500
813
- })
814
- ];
815
- });
816
- })();
817
- };
818
- return [
819
- 4,
820
- expect(generateTypes(projectPath, errorClient)).rejects.toThrow('Failed to fetch resources: 500 Internal Server Error')
821
- ];
822
- case 1:
823
- _state.sent();
824
- return [
825
- 2
826
- ];
827
- }
828
- });
829
- })();
830
- });
831
- });
832
- });