freestyle-sandboxes 0.1.26 → 0.1.28

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/index.cjs CHANGED
@@ -5689,6 +5689,10 @@ function composeCreateVmOptions(arr) {
5689
5689
  if (options.snapshotId !== void 0)
5690
5690
  result.snapshotId = options.snapshotId;
5691
5691
  if (options.recreate !== void 0) result.recreate = options.recreate;
5692
+ if (options.aptDeps !== void 0) result.aptDeps = options.aptDeps;
5693
+ if (options.domains !== void 0) result.domains = options.domains;
5694
+ if (options.activityThresholdBytes !== void 0)
5695
+ result.activityThresholdBytes = options.activityThresholdBytes;
5692
5696
  if (options.persistence !== void 0)
5693
5697
  result.persistence = options.persistence;
5694
5698
  if (options.ports !== void 0) {
@@ -6324,8 +6328,14 @@ class VmSpec {
6324
6328
  this.with = withBuilders ?? {};
6325
6329
  }
6326
6330
  }
6331
+ function isVmSpecLike(value) {
6332
+ return !!value && typeof value === "object" && "raw" in value && "with" in value;
6333
+ }
6334
+ function isVmTemplateLike(value) {
6335
+ return !!value && typeof value === "object" && "raw" in value && "with" in value;
6336
+ }
6327
6337
  async function convertSpecSnapshotsToTemplates(spec, processBuilders = true) {
6328
- if (!spec.raw.snapshot) {
6338
+ if (!isVmSpecLike(spec.raw.snapshot)) {
6329
6339
  return void 0;
6330
6340
  }
6331
6341
  let innerSpec = spec.raw.snapshot;
@@ -6334,7 +6344,7 @@ async function convertSpecSnapshotsToTemplates(spec, processBuilders = true) {
6334
6344
  }
6335
6345
  const { snapshot: nestedSnapshot, ...innerRaw } = innerSpec.raw;
6336
6346
  let nestedTemplate;
6337
- if (nestedSnapshot instanceof VmSpec) {
6347
+ if (isVmSpecLike(nestedSnapshot)) {
6338
6348
  nestedTemplate = await convertSpecSnapshotsToTemplates(innerSpec, false);
6339
6349
  }
6340
6350
  return new VmTemplate({
@@ -6352,7 +6362,7 @@ async function processOuterSpecBuilders(spec) {
6352
6362
  }
6353
6363
  if (builder.configureSnapshotSpec) {
6354
6364
  let snapshotSpec;
6355
- if (spec.raw.snapshot instanceof VmSpec) {
6365
+ if (isVmSpecLike(spec.raw.snapshot)) {
6356
6366
  snapshotSpec = spec.raw.snapshot;
6357
6367
  } else {
6358
6368
  snapshotSpec = new VmSpec({});
@@ -6373,7 +6383,7 @@ async function processSpecTree(spec) {
6373
6383
  }
6374
6384
  if (builder.configureSnapshotSpec) {
6375
6385
  let snapshotSpec;
6376
- if (spec.raw.snapshot instanceof VmSpec) {
6386
+ if (isVmSpecLike(spec.raw.snapshot)) {
6377
6387
  snapshotSpec = spec.raw.snapshot;
6378
6388
  } else {
6379
6389
  snapshotSpec = new VmSpec({});
@@ -6383,14 +6393,14 @@ async function processSpecTree(spec) {
6383
6393
  }
6384
6394
  }
6385
6395
  }
6386
- if (spec.raw.snapshot instanceof VmSpec) {
6396
+ if (isVmSpecLike(spec.raw.snapshot)) {
6387
6397
  spec.raw.snapshot = await processSpecTree(spec.raw.snapshot);
6388
6398
  }
6389
6399
  return spec;
6390
6400
  }
6391
6401
  function collectSpecBuilders(spec) {
6392
6402
  let builders = {};
6393
- if (spec.raw.snapshot instanceof VmSpec) {
6403
+ if (isVmSpecLike(spec.raw.snapshot)) {
6394
6404
  builders = collectSpecBuilders(spec.raw.snapshot);
6395
6405
  }
6396
6406
  if (spec.with) {
@@ -6426,8 +6436,8 @@ class VmsNamespace {
6426
6436
  * @returns A VM instance representing the created VM
6427
6437
  */
6428
6438
  async create(options = {}) {
6429
- if (options.snapshot instanceof VmSpec) {
6430
- if (options.spec instanceof VmSpec) {
6439
+ if (isVmSpecLike(options.snapshot)) {
6440
+ if (isVmSpecLike(options.spec)) {
6431
6441
  if (!options.spec.raw.snapshot) {
6432
6442
  options.spec.raw.snapshot = options.snapshot;
6433
6443
  }
@@ -6477,10 +6487,10 @@ class VmsNamespace {
6477
6487
  const { snapshot: _snapshot, ...rest } = options;
6478
6488
  options = rest;
6479
6489
  }
6480
- if (options.spec instanceof VmSpec && options.with) {
6490
+ if (isVmSpecLike(options.spec) && options.with) {
6481
6491
  options.spec = mergeWithBuildersIntoSpec(options.spec, options.with);
6482
6492
  }
6483
- if (options.spec instanceof VmSpec) {
6493
+ if (isVmSpecLike(options.spec)) {
6484
6494
  let spec = options.spec;
6485
6495
  spec = await processOuterSpecBuilders(spec);
6486
6496
  const { snapshot, ...outerSpecOptions } = spec.raw;
@@ -6500,19 +6510,19 @@ class VmsNamespace {
6500
6510
  options.with = spec.with;
6501
6511
  }
6502
6512
  }
6503
- const specBuilders = options.spec instanceof VmSpec ? collectSpecBuilders(options.spec) : void 0;
6504
- const templateBuilders = options.template instanceof VmTemplate ? options.template.with : void 0;
6513
+ const specBuilders = isVmSpecLike(options.spec) ? collectSpecBuilders(options.spec) : void 0;
6514
+ const templateBuilders = isVmTemplateLike(options.template) ? options.template.with : void 0;
6505
6515
  const builders = {
6506
6516
  ...templateBuilders || {},
6507
6517
  ...specBuilders || {},
6508
6518
  ...options.with || {}
6509
6519
  };
6510
- const { with: _, ...baseConfig } = options;
6520
+ const { with: _, spec: _spec, ...baseConfig } = options;
6511
6521
  let config = baseConfig;
6512
- if (config.template instanceof VmTemplate) {
6522
+ if (isVmTemplateLike(config.template)) {
6513
6523
  config.template = await processTemplateTree(config.template);
6514
6524
  }
6515
- if (config.template instanceof VmTemplate) {
6525
+ if (isVmTemplateLike(config.template)) {
6516
6526
  config.template = await ensureNestedTemplates(
6517
6527
  config.template,
6518
6528
  this.snapshots
@@ -6525,10 +6535,47 @@ class VmsNamespace {
6525
6535
  config = await builder.configure(config);
6526
6536
  }
6527
6537
  }
6538
+ if (config.systemd?.services) {
6539
+ const normalizedServices = config.systemd.services.map((service) => ({
6540
+ ...service,
6541
+ after: service.after?.map(
6542
+ (s) => s.includes(".") ? s : `${s}.service`
6543
+ ),
6544
+ requires: service.requires?.map(
6545
+ (s) => s.includes(".") ? s : `${s}.service`
6546
+ ),
6547
+ wantedBy: service.wantedBy?.map(
6548
+ (s) => s.includes(".") ? s : `${s}.service`
6549
+ )
6550
+ }));
6551
+ const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6552
+ normalizedServices,
6553
+ config.additionalFiles ?? {}
6554
+ );
6555
+ config.systemd.services = processedServices;
6556
+ config.additionalFiles = bashFiles;
6557
+ }
6558
+ if (config.systemd?.patchedServices) {
6559
+ config.systemd.patchedServices = config.systemd.patchedServices.map(
6560
+ (service) => {
6561
+ service.after = service.after?.map(
6562
+ (s) => s.includes(".") ? s : `${s}.service`
6563
+ );
6564
+ service.requires = service.requires?.map(
6565
+ (s) => s.includes(".") ? s : `${s}.service`
6566
+ );
6567
+ service.wantedBy = service.wantedBy?.map(
6568
+ (s) => s.includes(".") ? s : `${s}.service`
6569
+ );
6570
+ return service;
6571
+ }
6572
+ );
6573
+ }
6574
+ config.git = normalizeGitOptions(config.git);
6528
6575
  const response = await this.freestyle._apiClient.post("/v1/vms", {
6529
6576
  body: {
6530
6577
  ...config,
6531
- template: config.template instanceof VmTemplate ? config.template["raw"] : config.template,
6578
+ template: isVmTemplateLike(config.template) ? config.template["raw"] : config.template,
6532
6579
  // Cast systemd since we've processed SystemdServiceInput[] to RawSystemdService[]
6533
6580
  systemd: config.systemd,
6534
6581
  // Normalize git options - default config to {}
@@ -6629,8 +6676,8 @@ class VmSnapshotsNamespace {
6629
6676
  }
6630
6677
  async ensure(options) {
6631
6678
  let requestOptions = options;
6632
- if (requestOptions.snapshot instanceof VmSpec) {
6633
- if (requestOptions.spec instanceof VmSpec) {
6679
+ if (isVmSpecLike(requestOptions.snapshot)) {
6680
+ if (isVmSpecLike(requestOptions.spec)) {
6634
6681
  if (!requestOptions.spec.raw.snapshot) {
6635
6682
  requestOptions.spec.raw.snapshot = requestOptions.snapshot;
6636
6683
  }
@@ -6642,7 +6689,7 @@ class VmSnapshotsNamespace {
6642
6689
  const { snapshot: _snapshot, ...rest } = requestOptions;
6643
6690
  requestOptions = rest;
6644
6691
  }
6645
- if (requestOptions.spec instanceof VmSpec) {
6692
+ if (isVmSpecLike(requestOptions.spec)) {
6646
6693
  let spec = requestOptions.spec;
6647
6694
  spec = await processSpecTree(spec);
6648
6695
  const { snapshot: _snapshotSpec, ...outerSpecOptions } = spec.raw;
@@ -6657,12 +6704,12 @@ class VmSnapshotsNamespace {
6657
6704
  const { spec: _spec, ...rest } = requestOptions;
6658
6705
  requestOptions = rest;
6659
6706
  }
6660
- if (requestOptions.template instanceof VmTemplate) {
6707
+ if (isVmTemplateLike(requestOptions.template)) {
6661
6708
  const processedTemplate = await processTemplateTree(
6662
6709
  requestOptions.template
6663
6710
  );
6664
6711
  requestOptions.template = processedTemplate;
6665
- if (processedTemplate.raw.template instanceof VmTemplate) {
6712
+ if (isVmTemplateLike(processedTemplate.raw.template)) {
6666
6713
  requestOptions.template = await ensureNestedTemplates(
6667
6714
  processedTemplate,
6668
6715
  this
@@ -6677,7 +6724,7 @@ class VmSnapshotsNamespace {
6677
6724
  return this.apiClient.post("/v1/vms/snapshots", {
6678
6725
  body: {
6679
6726
  ...requestOptions,
6680
- template: requestOptions.template instanceof VmTemplate ? requestOptions.template.raw : requestOptions.template
6727
+ template: isVmTemplateLike(requestOptions.template) ? requestOptions.template.raw : requestOptions.template
6681
6728
  }
6682
6729
  }).catch((e) => {
6683
6730
  enhanceError(e);
@@ -6719,7 +6766,7 @@ async function processTemplateTree(template) {
6719
6766
  }
6720
6767
  if (builder.configureNestedTemplate) {
6721
6768
  let nestedTemplate;
6722
- if (template.raw.template instanceof VmTemplate) {
6769
+ if (isVmTemplateLike(template.raw.template)) {
6723
6770
  nestedTemplate = template.raw.template;
6724
6771
  } else {
6725
6772
  nestedTemplate = new VmTemplate({});
@@ -6731,7 +6778,46 @@ async function processTemplateTree(template) {
6731
6778
  }
6732
6779
  }
6733
6780
  }
6734
- if (template.raw.template instanceof VmTemplate) {
6781
+ if (template.raw.git) {
6782
+ template.raw.git = normalizeGitOptions(
6783
+ template.raw.git
6784
+ );
6785
+ }
6786
+ if (template.raw.systemd?.services) {
6787
+ const normalizedServices = template.raw.systemd.services.map((service) => ({
6788
+ ...service,
6789
+ after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
6790
+ requires: service.requires?.map(
6791
+ (s) => s.includes(".") ? s : `${s}.service`
6792
+ ),
6793
+ wantedBy: service.wantedBy?.map(
6794
+ (s) => s.includes(".") ? s : `${s}.service`
6795
+ )
6796
+ }));
6797
+ const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6798
+ normalizedServices,
6799
+ template.raw.additionalFiles ?? {}
6800
+ );
6801
+ template.raw.systemd.services = processedServices;
6802
+ template.raw.additionalFiles = bashFiles;
6803
+ }
6804
+ if (template.raw.systemd?.patchedServices) {
6805
+ template.raw.systemd.patchedServices = template.raw.systemd.patchedServices.map(
6806
+ (service) => ({
6807
+ ...service,
6808
+ after: service.after?.map(
6809
+ (s) => s.includes(".") ? s : `${s}.service`
6810
+ ),
6811
+ requires: service.requires?.map(
6812
+ (s) => s.includes(".") ? s : `${s}.service`
6813
+ ),
6814
+ wantedBy: service.wantedBy?.map(
6815
+ (s) => s.includes(".") ? s : `${s}.service`
6816
+ )
6817
+ })
6818
+ );
6819
+ }
6820
+ if (isVmTemplateLike(template.raw.template)) {
6735
6821
  template.raw.template = await processTemplateTree(
6736
6822
  template.raw.template
6737
6823
  );
@@ -6740,7 +6826,7 @@ async function processTemplateTree(template) {
6740
6826
  }
6741
6827
  async function ensureNestedTemplates(template, snapshots) {
6742
6828
  const templates = [template];
6743
- while (templates.at(-1)?.raw.template instanceof VmTemplate) {
6829
+ while (isVmTemplateLike(templates.at(-1)?.raw.template)) {
6744
6830
  const innerTemplate = templates.at(-1).raw.template;
6745
6831
  templates.at(-1).raw.template = void 0;
6746
6832
  templates.push(innerTemplate);
@@ -6927,6 +7013,51 @@ async function readFiles(directory) {
6927
7013
  return files;
6928
7014
  }
6929
7015
 
7016
+ async function debugCreateRequests(freestyle, options = {}) {
7017
+ const requests = [];
7018
+ let snapshotCounter = 0;
7019
+ let vmCounter = 0;
7020
+ const apiClient = freestyle._apiClient;
7021
+ const originalPost = apiClient.post.bind(freestyle._apiClient);
7022
+ const mockPost = (async (path, ...args) => {
7023
+ const rawBody = args[0] && "body" in args[0] ? args[0].body : void 0;
7024
+ const clonedBody = JSON.parse(JSON.stringify(rawBody ?? null));
7025
+ if (path === "/v1/vms/snapshots") {
7026
+ requests.push({
7027
+ path: "/v1/vms/snapshots",
7028
+ body: clonedBody
7029
+ });
7030
+ snapshotCounter += 1;
7031
+ return {
7032
+ snapshotId: `__debug_snapshot_${snapshotCounter}__`
7033
+ };
7034
+ }
7035
+ if (path === "/v1/vms") {
7036
+ requests.push({
7037
+ path: "/v1/vms",
7038
+ body: clonedBody
7039
+ });
7040
+ vmCounter += 1;
7041
+ return {
7042
+ id: `__debug_vm_${vmCounter}__`,
7043
+ domains: [],
7044
+ consoleUrl: "https://debug.local/console"
7045
+ };
7046
+ }
7047
+ return originalPost(
7048
+ path,
7049
+ ...args
7050
+ );
7051
+ });
7052
+ apiClient.post = mockPost;
7053
+ try {
7054
+ await freestyle.vms.create(options);
7055
+ } finally {
7056
+ apiClient.post = originalPost;
7057
+ }
7058
+ return requests;
7059
+ }
7060
+
6930
7061
  class Freestyle {
6931
7062
  /**
6932
7063
  * @internal
@@ -7012,5 +7143,6 @@ exports.VmSpec = VmSpec;
7012
7143
  exports.VmTemplate = VmTemplate;
7013
7144
  exports.VmWith = VmWith;
7014
7145
  exports.VmWithInstance = VmWithInstance;
7146
+ exports.debugCreateRequests = debugCreateRequests;
7015
7147
  exports.freestyle = freestyle;
7016
7148
  exports.readFiles = readFiles;
package/index.d.cts CHANGED
@@ -13835,6 +13835,21 @@ declare function readFiles(directory: string): Promise<{
13835
13835
  encoding: string;
13836
13836
  }[]>;
13837
13837
 
13838
+ type DebugCreateApiRequest = {
13839
+ path: "/v1/vms/snapshots" | "/v1/vms";
13840
+ body: unknown;
13841
+ };
13842
+ /**
13843
+ * Simulate what the SDK would POST during `freestyle.vms.create(...)` and
13844
+ * return all request bodies in execution order without network calls.
13845
+ */
13846
+ declare function debugCreateRequests<T extends Record<string, VmWith>>(freestyle: Freestyle, options?: CreateVmOptions & {
13847
+ with?: T;
13848
+ template?: VmTemplate<T>;
13849
+ spec?: VmSpec<T>;
13850
+ snapshot?: VmSpec<T>;
13851
+ }): Promise<DebugCreateApiRequest[]>;
13852
+
13838
13853
  type FreestyleOptions = ApiClientConfig;
13839
13854
  declare class Freestyle {
13840
13855
  readonly domains: DomainsNamespace;
@@ -13856,5 +13871,5 @@ declare class Freestyle {
13856
13871
  */
13857
13872
  declare const freestyle: Freestyle;
13858
13873
 
13859
- export { CronNamespace, Deployment, errors as Errors, FileSystem, Freestyle, GitRepo, Identity, requests as Requests, responses as Responses, Systemd, SystemdService, Vm, VmBuilder, VmService, VmSpec, VmTemplate, VmWith, VmWithInstance, freestyle, readFiles };
13874
+ export { CronNamespace, Deployment, errors as Errors, FileSystem, Freestyle, GitRepo, Identity, requests as Requests, responses as Responses, Systemd, SystemdService, Vm, VmBuilder, VmService, VmSpec, VmTemplate, VmWith, VmWithInstance, debugCreateRequests, freestyle, readFiles };
13860
13875
  export type { CreateVmOptions, CronSchedule, FreestyleOptions, SystemdServiceInput };
package/index.d.mts CHANGED
@@ -13835,6 +13835,21 @@ declare function readFiles(directory: string): Promise<{
13835
13835
  encoding: string;
13836
13836
  }[]>;
13837
13837
 
13838
+ type DebugCreateApiRequest = {
13839
+ path: "/v1/vms/snapshots" | "/v1/vms";
13840
+ body: unknown;
13841
+ };
13842
+ /**
13843
+ * Simulate what the SDK would POST during `freestyle.vms.create(...)` and
13844
+ * return all request bodies in execution order without network calls.
13845
+ */
13846
+ declare function debugCreateRequests<T extends Record<string, VmWith>>(freestyle: Freestyle, options?: CreateVmOptions & {
13847
+ with?: T;
13848
+ template?: VmTemplate<T>;
13849
+ spec?: VmSpec<T>;
13850
+ snapshot?: VmSpec<T>;
13851
+ }): Promise<DebugCreateApiRequest[]>;
13852
+
13838
13853
  type FreestyleOptions = ApiClientConfig;
13839
13854
  declare class Freestyle {
13840
13855
  readonly domains: DomainsNamespace;
@@ -13856,5 +13871,5 @@ declare class Freestyle {
13856
13871
  */
13857
13872
  declare const freestyle: Freestyle;
13858
13873
 
13859
- export { CronNamespace, Deployment, errors as Errors, FileSystem, Freestyle, GitRepo, Identity, requests as Requests, responses as Responses, Systemd, SystemdService, Vm, VmBuilder, VmService, VmSpec, VmTemplate, VmWith, VmWithInstance, freestyle, readFiles };
13874
+ export { CronNamespace, Deployment, errors as Errors, FileSystem, Freestyle, GitRepo, Identity, requests as Requests, responses as Responses, Systemd, SystemdService, Vm, VmBuilder, VmService, VmSpec, VmTemplate, VmWith, VmWithInstance, debugCreateRequests, freestyle, readFiles };
13860
13875
  export type { CreateVmOptions, CronSchedule, FreestyleOptions, SystemdServiceInput };
package/index.mjs CHANGED
@@ -5687,6 +5687,10 @@ function composeCreateVmOptions(arr) {
5687
5687
  if (options.snapshotId !== void 0)
5688
5688
  result.snapshotId = options.snapshotId;
5689
5689
  if (options.recreate !== void 0) result.recreate = options.recreate;
5690
+ if (options.aptDeps !== void 0) result.aptDeps = options.aptDeps;
5691
+ if (options.domains !== void 0) result.domains = options.domains;
5692
+ if (options.activityThresholdBytes !== void 0)
5693
+ result.activityThresholdBytes = options.activityThresholdBytes;
5690
5694
  if (options.persistence !== void 0)
5691
5695
  result.persistence = options.persistence;
5692
5696
  if (options.ports !== void 0) {
@@ -6322,8 +6326,14 @@ class VmSpec {
6322
6326
  this.with = withBuilders ?? {};
6323
6327
  }
6324
6328
  }
6329
+ function isVmSpecLike(value) {
6330
+ return !!value && typeof value === "object" && "raw" in value && "with" in value;
6331
+ }
6332
+ function isVmTemplateLike(value) {
6333
+ return !!value && typeof value === "object" && "raw" in value && "with" in value;
6334
+ }
6325
6335
  async function convertSpecSnapshotsToTemplates(spec, processBuilders = true) {
6326
- if (!spec.raw.snapshot) {
6336
+ if (!isVmSpecLike(spec.raw.snapshot)) {
6327
6337
  return void 0;
6328
6338
  }
6329
6339
  let innerSpec = spec.raw.snapshot;
@@ -6332,7 +6342,7 @@ async function convertSpecSnapshotsToTemplates(spec, processBuilders = true) {
6332
6342
  }
6333
6343
  const { snapshot: nestedSnapshot, ...innerRaw } = innerSpec.raw;
6334
6344
  let nestedTemplate;
6335
- if (nestedSnapshot instanceof VmSpec) {
6345
+ if (isVmSpecLike(nestedSnapshot)) {
6336
6346
  nestedTemplate = await convertSpecSnapshotsToTemplates(innerSpec, false);
6337
6347
  }
6338
6348
  return new VmTemplate({
@@ -6350,7 +6360,7 @@ async function processOuterSpecBuilders(spec) {
6350
6360
  }
6351
6361
  if (builder.configureSnapshotSpec) {
6352
6362
  let snapshotSpec;
6353
- if (spec.raw.snapshot instanceof VmSpec) {
6363
+ if (isVmSpecLike(spec.raw.snapshot)) {
6354
6364
  snapshotSpec = spec.raw.snapshot;
6355
6365
  } else {
6356
6366
  snapshotSpec = new VmSpec({});
@@ -6371,7 +6381,7 @@ async function processSpecTree(spec) {
6371
6381
  }
6372
6382
  if (builder.configureSnapshotSpec) {
6373
6383
  let snapshotSpec;
6374
- if (spec.raw.snapshot instanceof VmSpec) {
6384
+ if (isVmSpecLike(spec.raw.snapshot)) {
6375
6385
  snapshotSpec = spec.raw.snapshot;
6376
6386
  } else {
6377
6387
  snapshotSpec = new VmSpec({});
@@ -6381,14 +6391,14 @@ async function processSpecTree(spec) {
6381
6391
  }
6382
6392
  }
6383
6393
  }
6384
- if (spec.raw.snapshot instanceof VmSpec) {
6394
+ if (isVmSpecLike(spec.raw.snapshot)) {
6385
6395
  spec.raw.snapshot = await processSpecTree(spec.raw.snapshot);
6386
6396
  }
6387
6397
  return spec;
6388
6398
  }
6389
6399
  function collectSpecBuilders(spec) {
6390
6400
  let builders = {};
6391
- if (spec.raw.snapshot instanceof VmSpec) {
6401
+ if (isVmSpecLike(spec.raw.snapshot)) {
6392
6402
  builders = collectSpecBuilders(spec.raw.snapshot);
6393
6403
  }
6394
6404
  if (spec.with) {
@@ -6424,8 +6434,8 @@ class VmsNamespace {
6424
6434
  * @returns A VM instance representing the created VM
6425
6435
  */
6426
6436
  async create(options = {}) {
6427
- if (options.snapshot instanceof VmSpec) {
6428
- if (options.spec instanceof VmSpec) {
6437
+ if (isVmSpecLike(options.snapshot)) {
6438
+ if (isVmSpecLike(options.spec)) {
6429
6439
  if (!options.spec.raw.snapshot) {
6430
6440
  options.spec.raw.snapshot = options.snapshot;
6431
6441
  }
@@ -6475,10 +6485,10 @@ class VmsNamespace {
6475
6485
  const { snapshot: _snapshot, ...rest } = options;
6476
6486
  options = rest;
6477
6487
  }
6478
- if (options.spec instanceof VmSpec && options.with) {
6488
+ if (isVmSpecLike(options.spec) && options.with) {
6479
6489
  options.spec = mergeWithBuildersIntoSpec(options.spec, options.with);
6480
6490
  }
6481
- if (options.spec instanceof VmSpec) {
6491
+ if (isVmSpecLike(options.spec)) {
6482
6492
  let spec = options.spec;
6483
6493
  spec = await processOuterSpecBuilders(spec);
6484
6494
  const { snapshot, ...outerSpecOptions } = spec.raw;
@@ -6498,19 +6508,19 @@ class VmsNamespace {
6498
6508
  options.with = spec.with;
6499
6509
  }
6500
6510
  }
6501
- const specBuilders = options.spec instanceof VmSpec ? collectSpecBuilders(options.spec) : void 0;
6502
- const templateBuilders = options.template instanceof VmTemplate ? options.template.with : void 0;
6511
+ const specBuilders = isVmSpecLike(options.spec) ? collectSpecBuilders(options.spec) : void 0;
6512
+ const templateBuilders = isVmTemplateLike(options.template) ? options.template.with : void 0;
6503
6513
  const builders = {
6504
6514
  ...templateBuilders || {},
6505
6515
  ...specBuilders || {},
6506
6516
  ...options.with || {}
6507
6517
  };
6508
- const { with: _, ...baseConfig } = options;
6518
+ const { with: _, spec: _spec, ...baseConfig } = options;
6509
6519
  let config = baseConfig;
6510
- if (config.template instanceof VmTemplate) {
6520
+ if (isVmTemplateLike(config.template)) {
6511
6521
  config.template = await processTemplateTree(config.template);
6512
6522
  }
6513
- if (config.template instanceof VmTemplate) {
6523
+ if (isVmTemplateLike(config.template)) {
6514
6524
  config.template = await ensureNestedTemplates(
6515
6525
  config.template,
6516
6526
  this.snapshots
@@ -6523,10 +6533,47 @@ class VmsNamespace {
6523
6533
  config = await builder.configure(config);
6524
6534
  }
6525
6535
  }
6536
+ if (config.systemd?.services) {
6537
+ const normalizedServices = config.systemd.services.map((service) => ({
6538
+ ...service,
6539
+ after: service.after?.map(
6540
+ (s) => s.includes(".") ? s : `${s}.service`
6541
+ ),
6542
+ requires: service.requires?.map(
6543
+ (s) => s.includes(".") ? s : `${s}.service`
6544
+ ),
6545
+ wantedBy: service.wantedBy?.map(
6546
+ (s) => s.includes(".") ? s : `${s}.service`
6547
+ )
6548
+ }));
6549
+ const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6550
+ normalizedServices,
6551
+ config.additionalFiles ?? {}
6552
+ );
6553
+ config.systemd.services = processedServices;
6554
+ config.additionalFiles = bashFiles;
6555
+ }
6556
+ if (config.systemd?.patchedServices) {
6557
+ config.systemd.patchedServices = config.systemd.patchedServices.map(
6558
+ (service) => {
6559
+ service.after = service.after?.map(
6560
+ (s) => s.includes(".") ? s : `${s}.service`
6561
+ );
6562
+ service.requires = service.requires?.map(
6563
+ (s) => s.includes(".") ? s : `${s}.service`
6564
+ );
6565
+ service.wantedBy = service.wantedBy?.map(
6566
+ (s) => s.includes(".") ? s : `${s}.service`
6567
+ );
6568
+ return service;
6569
+ }
6570
+ );
6571
+ }
6572
+ config.git = normalizeGitOptions(config.git);
6526
6573
  const response = await this.freestyle._apiClient.post("/v1/vms", {
6527
6574
  body: {
6528
6575
  ...config,
6529
- template: config.template instanceof VmTemplate ? config.template["raw"] : config.template,
6576
+ template: isVmTemplateLike(config.template) ? config.template["raw"] : config.template,
6530
6577
  // Cast systemd since we've processed SystemdServiceInput[] to RawSystemdService[]
6531
6578
  systemd: config.systemd,
6532
6579
  // Normalize git options - default config to {}
@@ -6627,8 +6674,8 @@ class VmSnapshotsNamespace {
6627
6674
  }
6628
6675
  async ensure(options) {
6629
6676
  let requestOptions = options;
6630
- if (requestOptions.snapshot instanceof VmSpec) {
6631
- if (requestOptions.spec instanceof VmSpec) {
6677
+ if (isVmSpecLike(requestOptions.snapshot)) {
6678
+ if (isVmSpecLike(requestOptions.spec)) {
6632
6679
  if (!requestOptions.spec.raw.snapshot) {
6633
6680
  requestOptions.spec.raw.snapshot = requestOptions.snapshot;
6634
6681
  }
@@ -6640,7 +6687,7 @@ class VmSnapshotsNamespace {
6640
6687
  const { snapshot: _snapshot, ...rest } = requestOptions;
6641
6688
  requestOptions = rest;
6642
6689
  }
6643
- if (requestOptions.spec instanceof VmSpec) {
6690
+ if (isVmSpecLike(requestOptions.spec)) {
6644
6691
  let spec = requestOptions.spec;
6645
6692
  spec = await processSpecTree(spec);
6646
6693
  const { snapshot: _snapshotSpec, ...outerSpecOptions } = spec.raw;
@@ -6655,12 +6702,12 @@ class VmSnapshotsNamespace {
6655
6702
  const { spec: _spec, ...rest } = requestOptions;
6656
6703
  requestOptions = rest;
6657
6704
  }
6658
- if (requestOptions.template instanceof VmTemplate) {
6705
+ if (isVmTemplateLike(requestOptions.template)) {
6659
6706
  const processedTemplate = await processTemplateTree(
6660
6707
  requestOptions.template
6661
6708
  );
6662
6709
  requestOptions.template = processedTemplate;
6663
- if (processedTemplate.raw.template instanceof VmTemplate) {
6710
+ if (isVmTemplateLike(processedTemplate.raw.template)) {
6664
6711
  requestOptions.template = await ensureNestedTemplates(
6665
6712
  processedTemplate,
6666
6713
  this
@@ -6675,7 +6722,7 @@ class VmSnapshotsNamespace {
6675
6722
  return this.apiClient.post("/v1/vms/snapshots", {
6676
6723
  body: {
6677
6724
  ...requestOptions,
6678
- template: requestOptions.template instanceof VmTemplate ? requestOptions.template.raw : requestOptions.template
6725
+ template: isVmTemplateLike(requestOptions.template) ? requestOptions.template.raw : requestOptions.template
6679
6726
  }
6680
6727
  }).catch((e) => {
6681
6728
  enhanceError(e);
@@ -6717,7 +6764,7 @@ async function processTemplateTree(template) {
6717
6764
  }
6718
6765
  if (builder.configureNestedTemplate) {
6719
6766
  let nestedTemplate;
6720
- if (template.raw.template instanceof VmTemplate) {
6767
+ if (isVmTemplateLike(template.raw.template)) {
6721
6768
  nestedTemplate = template.raw.template;
6722
6769
  } else {
6723
6770
  nestedTemplate = new VmTemplate({});
@@ -6729,7 +6776,46 @@ async function processTemplateTree(template) {
6729
6776
  }
6730
6777
  }
6731
6778
  }
6732
- if (template.raw.template instanceof VmTemplate) {
6779
+ if (template.raw.git) {
6780
+ template.raw.git = normalizeGitOptions(
6781
+ template.raw.git
6782
+ );
6783
+ }
6784
+ if (template.raw.systemd?.services) {
6785
+ const normalizedServices = template.raw.systemd.services.map((service) => ({
6786
+ ...service,
6787
+ after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
6788
+ requires: service.requires?.map(
6789
+ (s) => s.includes(".") ? s : `${s}.service`
6790
+ ),
6791
+ wantedBy: service.wantedBy?.map(
6792
+ (s) => s.includes(".") ? s : `${s}.service`
6793
+ )
6794
+ }));
6795
+ const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6796
+ normalizedServices,
6797
+ template.raw.additionalFiles ?? {}
6798
+ );
6799
+ template.raw.systemd.services = processedServices;
6800
+ template.raw.additionalFiles = bashFiles;
6801
+ }
6802
+ if (template.raw.systemd?.patchedServices) {
6803
+ template.raw.systemd.patchedServices = template.raw.systemd.patchedServices.map(
6804
+ (service) => ({
6805
+ ...service,
6806
+ after: service.after?.map(
6807
+ (s) => s.includes(".") ? s : `${s}.service`
6808
+ ),
6809
+ requires: service.requires?.map(
6810
+ (s) => s.includes(".") ? s : `${s}.service`
6811
+ ),
6812
+ wantedBy: service.wantedBy?.map(
6813
+ (s) => s.includes(".") ? s : `${s}.service`
6814
+ )
6815
+ })
6816
+ );
6817
+ }
6818
+ if (isVmTemplateLike(template.raw.template)) {
6733
6819
  template.raw.template = await processTemplateTree(
6734
6820
  template.raw.template
6735
6821
  );
@@ -6738,7 +6824,7 @@ async function processTemplateTree(template) {
6738
6824
  }
6739
6825
  async function ensureNestedTemplates(template, snapshots) {
6740
6826
  const templates = [template];
6741
- while (templates.at(-1)?.raw.template instanceof VmTemplate) {
6827
+ while (isVmTemplateLike(templates.at(-1)?.raw.template)) {
6742
6828
  const innerTemplate = templates.at(-1).raw.template;
6743
6829
  templates.at(-1).raw.template = void 0;
6744
6830
  templates.push(innerTemplate);
@@ -6925,6 +7011,51 @@ async function readFiles(directory) {
6925
7011
  return files;
6926
7012
  }
6927
7013
 
7014
+ async function debugCreateRequests(freestyle, options = {}) {
7015
+ const requests = [];
7016
+ let snapshotCounter = 0;
7017
+ let vmCounter = 0;
7018
+ const apiClient = freestyle._apiClient;
7019
+ const originalPost = apiClient.post.bind(freestyle._apiClient);
7020
+ const mockPost = (async (path, ...args) => {
7021
+ const rawBody = args[0] && "body" in args[0] ? args[0].body : void 0;
7022
+ const clonedBody = JSON.parse(JSON.stringify(rawBody ?? null));
7023
+ if (path === "/v1/vms/snapshots") {
7024
+ requests.push({
7025
+ path: "/v1/vms/snapshots",
7026
+ body: clonedBody
7027
+ });
7028
+ snapshotCounter += 1;
7029
+ return {
7030
+ snapshotId: `__debug_snapshot_${snapshotCounter}__`
7031
+ };
7032
+ }
7033
+ if (path === "/v1/vms") {
7034
+ requests.push({
7035
+ path: "/v1/vms",
7036
+ body: clonedBody
7037
+ });
7038
+ vmCounter += 1;
7039
+ return {
7040
+ id: `__debug_vm_${vmCounter}__`,
7041
+ domains: [],
7042
+ consoleUrl: "https://debug.local/console"
7043
+ };
7044
+ }
7045
+ return originalPost(
7046
+ path,
7047
+ ...args
7048
+ );
7049
+ });
7050
+ apiClient.post = mockPost;
7051
+ try {
7052
+ await freestyle.vms.create(options);
7053
+ } finally {
7054
+ apiClient.post = originalPost;
7055
+ }
7056
+ return requests;
7057
+ }
7058
+
6928
7059
  class Freestyle {
6929
7060
  /**
6930
7061
  * @internal
@@ -6994,4 +7125,4 @@ function createLazyFreestyle(options = {}) {
6994
7125
  }
6995
7126
  const freestyle = createLazyFreestyle();
6996
7127
 
6997
- export { CronNamespace, Deployment, errors as Errors, FileSystem, Freestyle, GitRepo, Identity, Systemd, SystemdService, Vm, VmBuilder, VmService, VmSpec, VmTemplate, VmWith, VmWithInstance, freestyle, readFiles };
7128
+ export { CronNamespace, Deployment, errors as Errors, FileSystem, Freestyle, GitRepo, Identity, Systemd, SystemdService, Vm, VmBuilder, VmService, VmSpec, VmTemplate, VmWith, VmWithInstance, debugCreateRequests, freestyle, readFiles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freestyle-sandboxes",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "require": {