veryfront 0.1.278 → 0.1.279

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/esm/deno.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
3
  "version": "0.1.277",
4
- "version": "0.1.278",
4
+ "version": "0.1.279",
5
5
  "license": "Apache-2.0",
6
6
  "nodeModulesDir": "auto",
7
7
  "workspace": [
@@ -23,6 +23,7 @@ export declare class LazySandbox {
23
23
  private heartbeatPromise;
24
24
  private heartbeatTimer;
25
25
  private lastHeartbeatAt;
26
+ private readonly activeCommandJobEndpoints;
26
27
  constructor(options?: LazySandboxOptions);
27
28
  ensure(): Promise<void>;
28
29
  executeCommand(command: string, options?: ExecOptions): Promise<ExecResult>;
@@ -51,6 +52,9 @@ export declare class LazySandbox {
51
52
  private requireEndpoint;
52
53
  private resolveProjectId;
53
54
  private resetSessionState;
55
+ private resolveExecOptions;
56
+ private resolveCommandJobEndpoint;
57
+ private updateTrackedCommandJob;
54
58
  private authHeaders;
55
59
  private jsonHeaders;
56
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-sandbox.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/lazy-sandbox.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,gBAAgB,EAErB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,eAAe,EAGpB,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,YAAY,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAaD,4EAA4E;AAC5E,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,eAAe,CAAK;gBAEhB,OAAO,GAAE,kBAAuB;IAUtC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBvB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAsB1E,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,eAAe,CAAC;IAwCvF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAcvC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1E,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAkB5E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgBjD,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuB7D,eAAe,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAkBxC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAiBpD,SAAS,CAAC,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC5B,IAAI,EAAE,IAAI,MAAM,GAAG,IAAI,CAEtB;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;YAEa,gBAAgB;YAiChB,oBAAoB;YA2BpB,YAAY;IAc1B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,iBAAiB;YAMX,aAAa;IAO3B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,WAAW;CAMpB"}
1
+ {"version":3,"file":"lazy-sandbox.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/lazy-sandbox.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@ export class LazySandbox {
22
22
  heartbeatPromise = null;
23
23
  heartbeatTimer = null;
24
24
  lastHeartbeatAt = 0;
25
+ activeCommandJobEndpoints = new Map();
25
26
  constructor(options = {}) {
26
27
  this.apiUrl = resolveSandboxApiUrl(options);
27
28
  this.authToken = resolveSandboxAuthToken(options);
@@ -73,7 +74,7 @@ export class LazySandbox {
73
74
  const res = await fetch(`${this.requireEndpoint()}/exec`, {
74
75
  method: "POST",
75
76
  headers: this.jsonHeaders(),
76
- body: JSON.stringify({ command, ...options }),
77
+ body: JSON.stringify({ command, ...this.resolveExecOptions(options) }),
77
78
  });
78
79
  if (!res.ok) {
79
80
  throw REQUEST_ERROR.create({ detail: `Exec failed: ${res.status} ${await res.text()}` });
@@ -126,21 +127,24 @@ export class LazySandbox {
126
127
  }
127
128
  async startCommandJob(command, options) {
128
129
  await this.touchSession();
129
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs`, {
130
+ const endpoint = this.requireEndpoint();
131
+ const res = await fetch(`${endpoint}/exec/jobs`, {
130
132
  method: "POST",
131
133
  headers: this.jsonHeaders(),
132
- body: JSON.stringify({ command, ...options }),
134
+ body: JSON.stringify({ command, ...this.resolveExecOptions(options) }),
133
135
  });
134
136
  if (!res.ok) {
135
137
  throw REQUEST_ERROR.create({
136
138
  detail: `Start command job failed: ${res.status} ${await res.text()}`,
137
139
  });
138
140
  }
139
- return mapCommandJob(await res.json());
141
+ const job = mapCommandJob(await res.json());
142
+ this.updateTrackedCommandJob(job, endpoint);
143
+ return job;
140
144
  }
141
145
  async getCommandJob(jobId) {
142
- await this.ensure();
143
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs/${jobId}`, {
146
+ const endpoint = await this.resolveCommandJobEndpoint(jobId);
147
+ const res = await fetch(`${endpoint}/exec/jobs/${jobId}`, {
144
148
  headers: this.authHeaders(),
145
149
  });
146
150
  if (!res.ok) {
@@ -148,11 +152,13 @@ export class LazySandbox {
148
152
  detail: `Get command job failed: ${res.status} ${await res.text()}`,
149
153
  });
150
154
  }
151
- return mapCommandJob(await res.json());
155
+ const job = mapCommandJob(await res.json());
156
+ this.updateTrackedCommandJob(job, endpoint);
157
+ return job;
152
158
  }
153
159
  async getCommandJobOutput(jobId) {
154
- await this.ensure();
155
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs/${jobId}/output`, {
160
+ const endpoint = await this.resolveCommandJobEndpoint(jobId);
161
+ const res = await fetch(`${endpoint}/exec/jobs/${jobId}/output`, {
156
162
  headers: this.authHeaders(),
157
163
  });
158
164
  if (!res.ok) {
@@ -161,13 +167,15 @@ export class LazySandbox {
161
167
  });
162
168
  }
163
169
  const json = await res.json();
164
- return {
170
+ const output = {
165
171
  ...mapCommandJob(json),
166
172
  stdout: json.stdout,
167
173
  stderr: json.stderr,
168
174
  stdoutTruncated: json.stdout_truncated,
169
175
  stderrTruncated: json.stderr_truncated,
170
176
  };
177
+ this.updateTrackedCommandJob(output, endpoint);
178
+ return output;
171
179
  }
172
180
  async listCommandJobs() {
173
181
  await this.ensure();
@@ -184,8 +192,8 @@ export class LazySandbox {
184
192
  return jobs.map((job) => mapCommandJob(job));
185
193
  }
186
194
  async cancelCommandJob(jobId) {
187
- await this.ensure();
188
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs/${jobId}/cancel`, {
195
+ const endpoint = await this.resolveCommandJobEndpoint(jobId);
196
+ const res = await fetch(`${endpoint}/exec/jobs/${jobId}/cancel`, {
189
197
  method: "POST",
190
198
  headers: this.authHeaders(),
191
199
  });
@@ -194,7 +202,9 @@ export class LazySandbox {
194
202
  detail: `Cancel command job failed: ${res.status} ${await res.text()}`,
195
203
  });
196
204
  }
197
- return mapCommandJob(await res.json());
205
+ const job = mapCommandJob(await res.json());
206
+ this.updateTrackedCommandJob(job, endpoint);
207
+ return job;
198
208
  }
199
209
  async heartbeat(force = false) {
200
210
  const currentSessionId = this.sessionId;
@@ -215,8 +225,10 @@ export class LazySandbox {
215
225
  });
216
226
  if (!res.ok) {
217
227
  if (this.sessionId === currentSessionId) {
218
- await this.deleteSession(currentSessionId);
219
- this.resetSessionState(currentSessionId);
228
+ if (this.activeCommandJobEndpoints.size === 0) {
229
+ await this.deleteSession(currentSessionId);
230
+ this.resetSessionState(currentSessionId);
231
+ }
220
232
  }
221
233
  throw new Error(`Sandbox heartbeat failed: ${res.status} ${await res.text()}`);
222
234
  }
@@ -338,7 +350,7 @@ export class LazySandbox {
338
350
  await this.heartbeat();
339
351
  }
340
352
  startHeartbeatLoop() {
341
- if (!this.sessionId || this.heartbeatTimer)
353
+ if (!this.sessionId || this.heartbeatTimer || this.activeCommandJobEndpoints.size > 0)
342
354
  return;
343
355
  this.heartbeatTimer = dntShim.setInterval(() => {
344
356
  void this.heartbeat().catch(() => {
@@ -370,6 +382,7 @@ export class LazySandbox {
370
382
  resetSessionState(sessionId) {
371
383
  if (!sessionId || this.sessionId === sessionId) {
372
384
  this.stopHeartbeatLoop();
385
+ this.activeCommandJobEndpoints.clear();
373
386
  this.endpoint = null;
374
387
  this.sessionId = null;
375
388
  this.sessionProjectId = null;
@@ -377,6 +390,31 @@ export class LazySandbox {
377
390
  this.lastHeartbeatAt = 0;
378
391
  }
379
392
  }
393
+ resolveExecOptions(options) {
394
+ const projectReference = options?.projectReference ?? this.resolveProjectId() ?? undefined;
395
+ return projectReference ? { ...options, projectReference } : options;
396
+ }
397
+ async resolveCommandJobEndpoint(jobId) {
398
+ const trackedEndpoint = this.activeCommandJobEndpoints.get(jobId);
399
+ if (trackedEndpoint) {
400
+ return trackedEndpoint;
401
+ }
402
+ await this.ensure();
403
+ return this.requireEndpoint();
404
+ }
405
+ updateTrackedCommandJob(job, endpoint) {
406
+ if (job.status === "running") {
407
+ this.activeCommandJobEndpoints.set(job.id, endpoint);
408
+ this.stopHeartbeatLoop();
409
+ return;
410
+ }
411
+ if (!this.activeCommandJobEndpoints.delete(job.id)) {
412
+ return;
413
+ }
414
+ if (this.activeCommandJobEndpoints.size === 0 && this.endpoint) {
415
+ this.startHeartbeatLoop();
416
+ }
417
+ }
380
418
  authHeaders() {
381
419
  return { Authorization: `Bearer ${this.authToken}` };
382
420
  }
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.278";
1
+ export declare const VERSION = "0.1.279";
2
2
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.278";
3
+ export const VERSION = "0.1.279";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.278",
3
+ "version": "0.1.279",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
package/src/deno.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
3
  "version": "0.1.277",
4
- "version": "0.1.278",
4
+ "version": "0.1.279",
5
5
  "license": "Apache-2.0",
6
6
  "nodeModulesDir": "auto",
7
7
  "workspace": [
@@ -50,6 +50,7 @@ export class LazySandbox {
50
50
  private heartbeatPromise: Promise<void> | null = null;
51
51
  private heartbeatTimer: ReturnType<typeof dntShim.setInterval> | null = null;
52
52
  private lastHeartbeatAt = 0;
53
+ private readonly activeCommandJobEndpoints = new Map<string, string>();
53
54
 
54
55
  constructor(options: LazySandboxOptions = {}) {
55
56
  this.apiUrl = resolveSandboxApiUrl(options);
@@ -108,7 +109,7 @@ export class LazySandbox {
108
109
  const res = await fetch(`${this.requireEndpoint()}/exec`, {
109
110
  method: "POST",
110
111
  headers: this.jsonHeaders(),
111
- body: JSON.stringify({ command, ...options }),
112
+ body: JSON.stringify({ command, ...this.resolveExecOptions(options) }),
112
113
  });
113
114
 
114
115
  if (!res.ok) {
@@ -174,11 +175,12 @@ export class LazySandbox {
174
175
 
175
176
  async startCommandJob(command: string, options?: ExecOptions): Promise<CommandJob> {
176
177
  await this.touchSession();
178
+ const endpoint = this.requireEndpoint();
177
179
 
178
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs`, {
180
+ const res = await fetch(`${endpoint}/exec/jobs`, {
179
181
  method: "POST",
180
182
  headers: this.jsonHeaders(),
181
- body: JSON.stringify({ command, ...options }),
183
+ body: JSON.stringify({ command, ...this.resolveExecOptions(options) }),
182
184
  });
183
185
 
184
186
  if (!res.ok) {
@@ -187,13 +189,15 @@ export class LazySandbox {
187
189
  });
188
190
  }
189
191
 
190
- return mapCommandJob(await res.json());
192
+ const job = mapCommandJob(await res.json());
193
+ this.updateTrackedCommandJob(job, endpoint);
194
+ return job;
191
195
  }
192
196
 
193
197
  async getCommandJob(jobId: string): Promise<CommandJob> {
194
- await this.ensure();
198
+ const endpoint = await this.resolveCommandJobEndpoint(jobId);
195
199
 
196
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs/${jobId}`, {
200
+ const res = await fetch(`${endpoint}/exec/jobs/${jobId}`, {
197
201
  headers: this.authHeaders(),
198
202
  });
199
203
 
@@ -203,13 +207,15 @@ export class LazySandbox {
203
207
  });
204
208
  }
205
209
 
206
- return mapCommandJob(await res.json());
210
+ const job = mapCommandJob(await res.json());
211
+ this.updateTrackedCommandJob(job, endpoint);
212
+ return job;
207
213
  }
208
214
 
209
215
  async getCommandJobOutput(jobId: string): Promise<CommandJobOutput> {
210
- await this.ensure();
216
+ const endpoint = await this.resolveCommandJobEndpoint(jobId);
211
217
 
212
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs/${jobId}/output`, {
218
+ const res = await fetch(`${endpoint}/exec/jobs/${jobId}/output`, {
213
219
  headers: this.authHeaders(),
214
220
  });
215
221
 
@@ -220,13 +226,15 @@ export class LazySandbox {
220
226
  }
221
227
 
222
228
  const json = await res.json();
223
- return {
229
+ const output = {
224
230
  ...mapCommandJob(json),
225
231
  stdout: json.stdout,
226
232
  stderr: json.stderr,
227
233
  stdoutTruncated: json.stdout_truncated,
228
234
  stderrTruncated: json.stderr_truncated,
229
235
  };
236
+ this.updateTrackedCommandJob(output, endpoint);
237
+ return output;
230
238
  }
231
239
 
232
240
  async listCommandJobs(): Promise<CommandJob[]> {
@@ -248,9 +256,9 @@ export class LazySandbox {
248
256
  }
249
257
 
250
258
  async cancelCommandJob(jobId: string): Promise<CommandJob> {
251
- await this.ensure();
259
+ const endpoint = await this.resolveCommandJobEndpoint(jobId);
252
260
 
253
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs/${jobId}/cancel`, {
261
+ const res = await fetch(`${endpoint}/exec/jobs/${jobId}/cancel`, {
254
262
  method: "POST",
255
263
  headers: this.authHeaders(),
256
264
  });
@@ -261,7 +269,9 @@ export class LazySandbox {
261
269
  });
262
270
  }
263
271
 
264
- return mapCommandJob(await res.json());
272
+ const job = mapCommandJob(await res.json());
273
+ this.updateTrackedCommandJob(job, endpoint);
274
+ return job;
265
275
  }
266
276
 
267
277
  async heartbeat(force = false): Promise<void> {
@@ -288,8 +298,10 @@ export class LazySandbox {
288
298
 
289
299
  if (!res.ok) {
290
300
  if (this.sessionId === currentSessionId) {
291
- await this.deleteSession(currentSessionId);
292
- this.resetSessionState(currentSessionId);
301
+ if (this.activeCommandJobEndpoints.size === 0) {
302
+ await this.deleteSession(currentSessionId);
303
+ this.resetSessionState(currentSessionId);
304
+ }
293
305
  }
294
306
 
295
307
  throw new Error(`Sandbox heartbeat failed: ${res.status} ${await res.text()}`);
@@ -432,7 +444,7 @@ export class LazySandbox {
432
444
  }
433
445
 
434
446
  private startHeartbeatLoop(): void {
435
- if (!this.sessionId || this.heartbeatTimer) return;
447
+ if (!this.sessionId || this.heartbeatTimer || this.activeCommandJobEndpoints.size > 0) return;
436
448
 
437
449
  this.heartbeatTimer = dntShim.setInterval(() => {
438
450
  void this.heartbeat().catch(() => {
@@ -468,6 +480,7 @@ export class LazySandbox {
468
480
  private resetSessionState(sessionId?: string): void {
469
481
  if (!sessionId || this.sessionId === sessionId) {
470
482
  this.stopHeartbeatLoop();
483
+ this.activeCommandJobEndpoints.clear();
471
484
  this.endpoint = null;
472
485
  this.sessionId = null;
473
486
  this.sessionProjectId = null;
@@ -476,6 +489,37 @@ export class LazySandbox {
476
489
  }
477
490
  }
478
491
 
492
+ private resolveExecOptions(options?: ExecOptions): ExecOptions | undefined {
493
+ const projectReference = options?.projectReference ?? this.resolveProjectId() ?? undefined;
494
+ return projectReference ? { ...options, projectReference } : options;
495
+ }
496
+
497
+ private async resolveCommandJobEndpoint(jobId: string): Promise<string> {
498
+ const trackedEndpoint = this.activeCommandJobEndpoints.get(jobId);
499
+ if (trackedEndpoint) {
500
+ return trackedEndpoint;
501
+ }
502
+
503
+ await this.ensure();
504
+ return this.requireEndpoint();
505
+ }
506
+
507
+ private updateTrackedCommandJob(job: Pick<CommandJob, "id" | "status">, endpoint: string): void {
508
+ if (job.status === "running") {
509
+ this.activeCommandJobEndpoints.set(job.id, endpoint);
510
+ this.stopHeartbeatLoop();
511
+ return;
512
+ }
513
+
514
+ if (!this.activeCommandJobEndpoints.delete(job.id)) {
515
+ return;
516
+ }
517
+
518
+ if (this.activeCommandJobEndpoints.size === 0 && this.endpoint) {
519
+ this.startHeartbeatLoop();
520
+ }
521
+ }
522
+
479
523
  private authHeaders(): HeadersInit {
480
524
  return { Authorization: `Bearer ${this.authToken}` };
481
525
  }
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.278";
3
+ export const VERSION = "0.1.279";