@molecule/api-code-sandbox-flyio 1.2.1 → 1.2.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAWV,eAAe,EAGhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAwBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAIV,WAAW,EAUZ,MAAM,YAAY,CAAA;AA0tEnB;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,YAAY,EACrB,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,GACzB,eAAe,CAEjB"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAWV,eAAe,EAGhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAwBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAIV,WAAW,EAUZ,MAAM,YAAY,CAAA;AAmxEnB;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,YAAY,EACrB,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,GACzB,eAAe,CAEjB"}
package/dist/provider.js CHANGED
@@ -358,6 +358,7 @@ class FlyioSandboxProvider {
358
358
  */
359
359
  async ensureApp(app, options = {}) {
360
360
  const existing = await this.client.request(`/apps/${app}`, { nullOn: [404] });
361
+ let createdHere = false;
361
362
  if (!existing) {
362
363
  const body = { name: app, org_slug: this.orgSlug() };
363
364
  const network = this.networkFor(app);
@@ -379,13 +380,47 @@ class FlyioSandboxProvider {
379
380
  throw error;
380
381
  }
381
382
  }
382
- if (created && this.config.assignSharedIpv4)
383
+ createdHere = created;
384
+ }
385
+ try {
386
+ if (createdHere && this.config.assignSharedIpv4)
383
387
  await this.assignSharedIpv4(app);
388
+ await this.applyEgressPolicy(app);
389
+ if (options.privateRoutes === false)
390
+ return { routes: {}, created: createdHere };
391
+ return {
392
+ routes: await this.ensurePrivateRoutes(app, Boolean(existing)),
393
+ created: createdHere,
394
+ };
395
+ }
396
+ catch (error) {
397
+ if (createdHere)
398
+ await this.discardCreatedApp(app, {});
399
+ throw error;
384
400
  }
385
- await this.applyEgressPolicy(app);
386
- if (options.privateRoutes === false)
387
- return {};
388
- return this.ensurePrivateRoutes(app, Boolean(existing));
401
+ }
402
+ /**
403
+ * Deletes an app THIS call created, after a later step of the same create
404
+ * failed. Without it every failed create leaves an app behind with its
405
+ * addresses and volume: on 2026-09-28, 34 empty `mol-prod-*` apps had piled
406
+ * up in production, one per deploy that hit the organization's Machine limit
407
+ * (`POST /machines` → 422) after its app was already made. An app that
408
+ * existed before the call is never touched — it may hold a working Machine.
409
+ * @param app - The app this call created.
410
+ * @param privateRoutes - Flycast addresses already allocated for it.
411
+ */
412
+ async discardCreatedApp(app, privateRoutes) {
413
+ try {
414
+ await this.client.request(`/apps/${app}`, { method: 'DELETE', nullOn: [404] });
415
+ logger.info('Deleted a Fly sandbox app whose create failed', { app });
416
+ }
417
+ catch (error) {
418
+ logger.error('Failed to delete a Fly sandbox app after its create failed — it leaks', {
419
+ app,
420
+ error,
421
+ });
422
+ }
423
+ await this.releasePrivateRoutes(privateRoutes);
389
424
  }
390
425
  /**
391
426
  * Allocates a Flycast private address on every declared service app, INTO this
@@ -880,7 +915,26 @@ class FlyioSandboxProvider {
880
915
  // leave in a Machine config.
881
916
  assertPrivateRoutesForEnv({ ...config.env, ...config.selfDeliveredEnv }, this.privateServices());
882
917
  const app = this.resolveApp(config.projectId);
883
- const privateRoutes = await this.ensureApp(app);
918
+ const { routes: privateRoutes, created } = await this.ensureApp(app);
919
+ try {
920
+ return await this.createInApp(app, privateRoutes, config, manifest);
921
+ }
922
+ catch (error) {
923
+ if (created && this.appPerProject())
924
+ await this.discardCreatedApp(app, privateRoutes);
925
+ throw error;
926
+ }
927
+ }
928
+ /**
929
+ * The part of {@link create} that runs once the app exists: clear stale
930
+ * Machines, provision the volume, create the Machine and wait for it.
931
+ * @param app - The app.
932
+ * @param privateRoutes - Flycast addresses allocated for the app.
933
+ * @param config - The sandbox config.
934
+ * @param manifest - The template manifest, when creating from a template.
935
+ * @returns The running sandbox.
936
+ */
937
+ async createInApp(app, privateRoutes, config, manifest) {
884
938
  // Clear any stale Machine from a prior failed/partial create before
885
939
  // provisioning — otherwise it still holds the app's single-attach volume and
886
940
  // `POST /machines` fails with `412 volume already claimed`. Makes create
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-code-sandbox-flyio",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Fly.io Machines (Firecracker microVM) code sandbox provider",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",