modal 0.3.21 → 0.3.22

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/dist/index.cjs CHANGED
@@ -31,7 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AlreadyExistsError: () => AlreadyExistsError,
34
- App: () => App,
34
+ App: () => App2,
35
35
  CloudBucketMount: () => CloudBucketMount2,
36
36
  Cls: () => Cls,
37
37
  ClsInstance: () => ClsInstance,
@@ -41772,11 +41772,20 @@ var Image2 = class _Image {
41772
41772
  #imageId;
41773
41773
  #tag;
41774
41774
  #imageRegistryConfig;
41775
+ #layers;
41775
41776
  /** @ignore */
41776
- constructor(imageId, tag, imageRegistryConfig) {
41777
+ constructor(imageId, tag, imageRegistryConfig, layers) {
41777
41778
  this.#imageId = imageId;
41778
41779
  this.#tag = tag;
41779
41780
  this.#imageRegistryConfig = imageRegistryConfig;
41781
+ this.#layers = layers || [
41782
+ {
41783
+ commands: [],
41784
+ secrets: void 0,
41785
+ gpuConfig: void 0,
41786
+ forceBuild: false
41787
+ }
41788
+ ];
41780
41789
  }
41781
41790
  get imageId() {
41782
41791
  return this.#imageId;
@@ -41861,6 +41870,42 @@ var Image2 = class _Image {
41861
41870
  }
41862
41871
  return new _Image("", tag, imageRegistryConfig);
41863
41872
  }
41873
+ static validateDockerfileCommands(commands) {
41874
+ for (const command of commands) {
41875
+ const trimmed = command.trim().toUpperCase();
41876
+ if (trimmed.startsWith("COPY ") && !trimmed.startsWith("COPY --FROM=")) {
41877
+ throw new InvalidError(
41878
+ "COPY commands that copy from local context are not yet supported."
41879
+ );
41880
+ }
41881
+ }
41882
+ }
41883
+ /**
41884
+ * Extend an image with arbitrary Dockerfile-like commands.
41885
+ *
41886
+ * Each call creates a new Image layer that will be built sequentially.
41887
+ * The provided options apply only to this layer.
41888
+ *
41889
+ * @param commands - Array of Dockerfile commands as strings
41890
+ * @param options - Optional configuration for this layer's build
41891
+ * @returns A new Image instance
41892
+ */
41893
+ dockerfileCommands(commands, options) {
41894
+ if (commands.length === 0) {
41895
+ return this;
41896
+ }
41897
+ _Image.validateDockerfileCommands(commands);
41898
+ const newLayer = {
41899
+ commands: [...commands],
41900
+ secrets: options?.secrets,
41901
+ gpuConfig: options?.gpu ? parseGpuConfig(options.gpu) : void 0,
41902
+ forceBuild: options?.forceBuild
41903
+ };
41904
+ return new _Image("", this.#tag, this.#imageRegistryConfig, [
41905
+ ...this.#layers,
41906
+ newLayer
41907
+ ]);
41908
+ }
41864
41909
  /**
41865
41910
  * Eagerly builds an Image on Modal.
41866
41911
  *
@@ -41870,58 +41915,75 @@ var Image2 = class _Image {
41870
41915
  if (this.imageId !== "") {
41871
41916
  return this;
41872
41917
  }
41873
- const resp = await client.imageGetOrCreate({
41874
- appId: app.appId,
41875
- image: {
41876
- dockerfileCommands: [`FROM ${this.#tag}`],
41877
- imageRegistryConfig: this.#imageRegistryConfig
41878
- },
41879
- builderVersion: imageBuilderVersion()
41880
- });
41881
- let result;
41882
- let metadata = void 0;
41883
- if (resp.result?.status) {
41884
- result = resp.result;
41885
- metadata = resp.metadata;
41886
- } else {
41887
- let lastEntryId = "";
41888
- let resultJoined = void 0;
41889
- while (!resultJoined) {
41890
- for await (const item of client.imageJoinStreaming({
41891
- imageId: resp.imageId,
41892
- timeout: 55,
41893
- lastEntryId
41894
- })) {
41895
- if (item.entryId) lastEntryId = item.entryId;
41896
- if (item.result?.status) {
41897
- resultJoined = item.result;
41898
- metadata = item.metadata;
41899
- break;
41918
+ let baseImageId;
41919
+ for (let i = 0; i < this.#layers.length; i++) {
41920
+ const layer = this.#layers[i];
41921
+ const secretIds = layer.secrets?.map((secret) => secret.secretId) || [];
41922
+ const gpuConfig = layer.gpuConfig;
41923
+ let dockerfileCommands;
41924
+ let baseImages;
41925
+ if (i === 0) {
41926
+ dockerfileCommands = [`FROM ${this.#tag}`, ...layer.commands];
41927
+ baseImages = [];
41928
+ } else {
41929
+ dockerfileCommands = ["FROM base", ...layer.commands];
41930
+ baseImages = [{ dockerTag: "base", imageId: baseImageId }];
41931
+ }
41932
+ const resp = await client.imageGetOrCreate({
41933
+ appId: app.appId,
41934
+ image: Image.create({
41935
+ dockerfileCommands,
41936
+ imageRegistryConfig: this.#imageRegistryConfig,
41937
+ secretIds,
41938
+ gpuConfig,
41939
+ contextFiles: [],
41940
+ baseImages
41941
+ }),
41942
+ builderVersion: imageBuilderVersion(),
41943
+ forceBuild: layer.forceBuild || false
41944
+ });
41945
+ let result;
41946
+ if (resp.result?.status) {
41947
+ result = resp.result;
41948
+ } else {
41949
+ let lastEntryId = "";
41950
+ let resultJoined = void 0;
41951
+ while (!resultJoined) {
41952
+ for await (const item of client.imageJoinStreaming({
41953
+ imageId: resp.imageId,
41954
+ timeout: 55,
41955
+ lastEntryId
41956
+ })) {
41957
+ if (item.entryId) lastEntryId = item.entryId;
41958
+ if (item.result?.status) {
41959
+ resultJoined = item.result;
41960
+ break;
41961
+ }
41900
41962
  }
41901
41963
  }
41964
+ result = resultJoined;
41902
41965
  }
41903
- result = resultJoined;
41904
- }
41905
- void metadata;
41906
- if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41907
- throw new Error(
41908
- `Image build for ${resp.imageId} failed with the exception:
41966
+ if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41967
+ throw new Error(
41968
+ `Image build for ${resp.imageId} failed with the exception:
41909
41969
  ${result.exception}`
41910
- );
41911
- } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41912
- throw new Error(
41913
- `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41914
- );
41915
- } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41916
- throw new Error(
41917
- `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41918
- );
41919
- } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41920
- throw new Error(
41921
- `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41922
- );
41970
+ );
41971
+ } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41972
+ throw new Error(
41973
+ `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41974
+ );
41975
+ } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41976
+ throw new Error(
41977
+ `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41978
+ );
41979
+ } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41980
+ throw new Error(
41981
+ `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41982
+ );
41983
+ }
41984
+ baseImageId = resp.imageId;
41923
41985
  }
41924
- this.#imageId = resp.imageId;
41986
+ this.#imageId = baseImageId;
41925
41987
  return this;
41926
41988
  }
41927
41989
  /** Delete an Image by ID. Warning: This removes an *entire Image*, and cannot be undone. */
@@ -42673,7 +42735,7 @@ function parseGpuConfig(gpu) {
42673
42735
  gpuType: gpuType.toUpperCase()
42674
42736
  };
42675
42737
  }
42676
- var App = class _App {
42738
+ var App2 = class _App {
42677
42739
  appId;
42678
42740
  name;
42679
42741
  /** @ignore */
package/dist/index.d.cts CHANGED
@@ -1,5 +1,22 @@
1
1
  import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
2
2
 
3
+ declare enum GPUType {
4
+ /**
5
+ * GPU_TYPE_UNSPECIFIED - Note: this enum is no longer used by current clients - don't add new types
6
+ * Old clients still send it, so we use it server-side for compatibility
7
+ */
8
+ GPU_TYPE_UNSPECIFIED = 0,
9
+ GPU_TYPE_T4 = 1,
10
+ GPU_TYPE_A100 = 2,
11
+ GPU_TYPE_A10G = 3,
12
+ GPU_TYPE_ANY = 4,
13
+ GPU_TYPE_A100_80GB = 8,
14
+ GPU_TYPE_L4 = 9,
15
+ GPU_TYPE_H100 = 10,
16
+ GPU_TYPE_L40S = 11,
17
+ GPU_TYPE_H200 = 12,
18
+ UNRECOGNIZED = -1
19
+ }
3
20
  declare enum ParameterType {
4
21
  PARAM_TYPE_UNSPECIFIED = 0,
5
22
  PARAM_TYPE_STRING = 1,
@@ -40,6 +57,13 @@ interface ClassParameterSpec {
40
57
  fullType: GenericPayloadType | undefined;
41
58
  }
42
59
  declare const ClassParameterSpec: MessageFns<ClassParameterSpec>;
60
+ interface GPUConfig {
61
+ /** Deprecated, at some point */
62
+ type: GPUType;
63
+ count: number;
64
+ gpuType: string;
65
+ }
66
+ declare const GPUConfig: MessageFns<GPUConfig>;
43
67
  interface GenericPayloadType {
44
68
  baseType: ParameterType;
45
69
  /** sub-type for generic types like lists */
@@ -85,11 +109,27 @@ declare class Secret {
85
109
 
86
110
  /** Options for deleting an Image. */
87
111
  type ImageDeleteOptions = Record<never, never>;
112
+ /** Options for Image.dockerfileCommands(). */
113
+ type ImageDockerfileCommandsOptions = {
114
+ /** Secrets that will be made available to this layer's build environment. */
115
+ secrets?: Secret[];
116
+ /** GPU reservation for this layer's build environment (e.g. "A100", "T4:2", "A100-80GB:4"). */
117
+ gpu?: string;
118
+ /** Ignore cached builds for this layer, similar to 'docker build --no-cache'. */
119
+ forceBuild?: boolean;
120
+ };
121
+ /** Represents a single image layer with its build configuration. */
122
+ type Layer = {
123
+ commands: string[];
124
+ secrets?: Secret[];
125
+ gpuConfig?: GPUConfig;
126
+ forceBuild?: boolean;
127
+ };
88
128
  /** A container image, used for starting Sandboxes. */
89
129
  declare class Image {
90
130
  #private;
91
131
  /** @ignore */
92
- constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig);
132
+ constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig, layers?: Layer[]);
93
133
  get imageId(): string;
94
134
  /**
95
135
  * Creates an Image from an Image ID
@@ -118,6 +158,18 @@ declare class Image {
118
158
  * @param secret - A Secret containing credentials for registry authentication.
119
159
  */
120
160
  static fromGcpArtifactRegistry(tag: string, secret: Secret): Image;
161
+ private static validateDockerfileCommands;
162
+ /**
163
+ * Extend an image with arbitrary Dockerfile-like commands.
164
+ *
165
+ * Each call creates a new Image layer that will be built sequentially.
166
+ * The provided options apply only to this layer.
167
+ *
168
+ * @param commands - Array of Dockerfile commands as strings
169
+ * @param options - Optional configuration for this layer's build
170
+ * @returns A new Image instance
171
+ */
172
+ dockerfileCommands(commands: string[], options?: ImageDockerfileCommandsOptions): Image;
121
173
  /**
122
174
  * Eagerly builds an Image on Modal.
123
175
  *
@@ -747,4 +799,4 @@ declare class Queue {
747
799
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
748
800
  }
749
801
 
750
- export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
802
+ export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, type ImageDockerfileCommandsOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,22 @@
1
1
  import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
2
2
 
3
+ declare enum GPUType {
4
+ /**
5
+ * GPU_TYPE_UNSPECIFIED - Note: this enum is no longer used by current clients - don't add new types
6
+ * Old clients still send it, so we use it server-side for compatibility
7
+ */
8
+ GPU_TYPE_UNSPECIFIED = 0,
9
+ GPU_TYPE_T4 = 1,
10
+ GPU_TYPE_A100 = 2,
11
+ GPU_TYPE_A10G = 3,
12
+ GPU_TYPE_ANY = 4,
13
+ GPU_TYPE_A100_80GB = 8,
14
+ GPU_TYPE_L4 = 9,
15
+ GPU_TYPE_H100 = 10,
16
+ GPU_TYPE_L40S = 11,
17
+ GPU_TYPE_H200 = 12,
18
+ UNRECOGNIZED = -1
19
+ }
3
20
  declare enum ParameterType {
4
21
  PARAM_TYPE_UNSPECIFIED = 0,
5
22
  PARAM_TYPE_STRING = 1,
@@ -40,6 +57,13 @@ interface ClassParameterSpec {
40
57
  fullType: GenericPayloadType | undefined;
41
58
  }
42
59
  declare const ClassParameterSpec: MessageFns<ClassParameterSpec>;
60
+ interface GPUConfig {
61
+ /** Deprecated, at some point */
62
+ type: GPUType;
63
+ count: number;
64
+ gpuType: string;
65
+ }
66
+ declare const GPUConfig: MessageFns<GPUConfig>;
43
67
  interface GenericPayloadType {
44
68
  baseType: ParameterType;
45
69
  /** sub-type for generic types like lists */
@@ -85,11 +109,27 @@ declare class Secret {
85
109
 
86
110
  /** Options for deleting an Image. */
87
111
  type ImageDeleteOptions = Record<never, never>;
112
+ /** Options for Image.dockerfileCommands(). */
113
+ type ImageDockerfileCommandsOptions = {
114
+ /** Secrets that will be made available to this layer's build environment. */
115
+ secrets?: Secret[];
116
+ /** GPU reservation for this layer's build environment (e.g. "A100", "T4:2", "A100-80GB:4"). */
117
+ gpu?: string;
118
+ /** Ignore cached builds for this layer, similar to 'docker build --no-cache'. */
119
+ forceBuild?: boolean;
120
+ };
121
+ /** Represents a single image layer with its build configuration. */
122
+ type Layer = {
123
+ commands: string[];
124
+ secrets?: Secret[];
125
+ gpuConfig?: GPUConfig;
126
+ forceBuild?: boolean;
127
+ };
88
128
  /** A container image, used for starting Sandboxes. */
89
129
  declare class Image {
90
130
  #private;
91
131
  /** @ignore */
92
- constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig);
132
+ constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig, layers?: Layer[]);
93
133
  get imageId(): string;
94
134
  /**
95
135
  * Creates an Image from an Image ID
@@ -118,6 +158,18 @@ declare class Image {
118
158
  * @param secret - A Secret containing credentials for registry authentication.
119
159
  */
120
160
  static fromGcpArtifactRegistry(tag: string, secret: Secret): Image;
161
+ private static validateDockerfileCommands;
162
+ /**
163
+ * Extend an image with arbitrary Dockerfile-like commands.
164
+ *
165
+ * Each call creates a new Image layer that will be built sequentially.
166
+ * The provided options apply only to this layer.
167
+ *
168
+ * @param commands - Array of Dockerfile commands as strings
169
+ * @param options - Optional configuration for this layer's build
170
+ * @returns A new Image instance
171
+ */
172
+ dockerfileCommands(commands: string[], options?: ImageDockerfileCommandsOptions): Image;
121
173
  /**
122
174
  * Eagerly builds an Image on Modal.
123
175
  *
@@ -747,4 +799,4 @@ declare class Queue {
747
799
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
748
800
  }
749
801
 
750
- export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
802
+ export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, type ImageDockerfileCommandsOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
package/dist/index.js CHANGED
@@ -41718,11 +41718,20 @@ var Image2 = class _Image {
41718
41718
  #imageId;
41719
41719
  #tag;
41720
41720
  #imageRegistryConfig;
41721
+ #layers;
41721
41722
  /** @ignore */
41722
- constructor(imageId, tag, imageRegistryConfig) {
41723
+ constructor(imageId, tag, imageRegistryConfig, layers) {
41723
41724
  this.#imageId = imageId;
41724
41725
  this.#tag = tag;
41725
41726
  this.#imageRegistryConfig = imageRegistryConfig;
41727
+ this.#layers = layers || [
41728
+ {
41729
+ commands: [],
41730
+ secrets: void 0,
41731
+ gpuConfig: void 0,
41732
+ forceBuild: false
41733
+ }
41734
+ ];
41726
41735
  }
41727
41736
  get imageId() {
41728
41737
  return this.#imageId;
@@ -41807,6 +41816,42 @@ var Image2 = class _Image {
41807
41816
  }
41808
41817
  return new _Image("", tag, imageRegistryConfig);
41809
41818
  }
41819
+ static validateDockerfileCommands(commands) {
41820
+ for (const command of commands) {
41821
+ const trimmed = command.trim().toUpperCase();
41822
+ if (trimmed.startsWith("COPY ") && !trimmed.startsWith("COPY --FROM=")) {
41823
+ throw new InvalidError(
41824
+ "COPY commands that copy from local context are not yet supported."
41825
+ );
41826
+ }
41827
+ }
41828
+ }
41829
+ /**
41830
+ * Extend an image with arbitrary Dockerfile-like commands.
41831
+ *
41832
+ * Each call creates a new Image layer that will be built sequentially.
41833
+ * The provided options apply only to this layer.
41834
+ *
41835
+ * @param commands - Array of Dockerfile commands as strings
41836
+ * @param options - Optional configuration for this layer's build
41837
+ * @returns A new Image instance
41838
+ */
41839
+ dockerfileCommands(commands, options) {
41840
+ if (commands.length === 0) {
41841
+ return this;
41842
+ }
41843
+ _Image.validateDockerfileCommands(commands);
41844
+ const newLayer = {
41845
+ commands: [...commands],
41846
+ secrets: options?.secrets,
41847
+ gpuConfig: options?.gpu ? parseGpuConfig(options.gpu) : void 0,
41848
+ forceBuild: options?.forceBuild
41849
+ };
41850
+ return new _Image("", this.#tag, this.#imageRegistryConfig, [
41851
+ ...this.#layers,
41852
+ newLayer
41853
+ ]);
41854
+ }
41810
41855
  /**
41811
41856
  * Eagerly builds an Image on Modal.
41812
41857
  *
@@ -41816,58 +41861,75 @@ var Image2 = class _Image {
41816
41861
  if (this.imageId !== "") {
41817
41862
  return this;
41818
41863
  }
41819
- const resp = await client.imageGetOrCreate({
41820
- appId: app.appId,
41821
- image: {
41822
- dockerfileCommands: [`FROM ${this.#tag}`],
41823
- imageRegistryConfig: this.#imageRegistryConfig
41824
- },
41825
- builderVersion: imageBuilderVersion()
41826
- });
41827
- let result;
41828
- let metadata = void 0;
41829
- if (resp.result?.status) {
41830
- result = resp.result;
41831
- metadata = resp.metadata;
41832
- } else {
41833
- let lastEntryId = "";
41834
- let resultJoined = void 0;
41835
- while (!resultJoined) {
41836
- for await (const item of client.imageJoinStreaming({
41837
- imageId: resp.imageId,
41838
- timeout: 55,
41839
- lastEntryId
41840
- })) {
41841
- if (item.entryId) lastEntryId = item.entryId;
41842
- if (item.result?.status) {
41843
- resultJoined = item.result;
41844
- metadata = item.metadata;
41845
- break;
41864
+ let baseImageId;
41865
+ for (let i = 0; i < this.#layers.length; i++) {
41866
+ const layer = this.#layers[i];
41867
+ const secretIds = layer.secrets?.map((secret) => secret.secretId) || [];
41868
+ const gpuConfig = layer.gpuConfig;
41869
+ let dockerfileCommands;
41870
+ let baseImages;
41871
+ if (i === 0) {
41872
+ dockerfileCommands = [`FROM ${this.#tag}`, ...layer.commands];
41873
+ baseImages = [];
41874
+ } else {
41875
+ dockerfileCommands = ["FROM base", ...layer.commands];
41876
+ baseImages = [{ dockerTag: "base", imageId: baseImageId }];
41877
+ }
41878
+ const resp = await client.imageGetOrCreate({
41879
+ appId: app.appId,
41880
+ image: Image.create({
41881
+ dockerfileCommands,
41882
+ imageRegistryConfig: this.#imageRegistryConfig,
41883
+ secretIds,
41884
+ gpuConfig,
41885
+ contextFiles: [],
41886
+ baseImages
41887
+ }),
41888
+ builderVersion: imageBuilderVersion(),
41889
+ forceBuild: layer.forceBuild || false
41890
+ });
41891
+ let result;
41892
+ if (resp.result?.status) {
41893
+ result = resp.result;
41894
+ } else {
41895
+ let lastEntryId = "";
41896
+ let resultJoined = void 0;
41897
+ while (!resultJoined) {
41898
+ for await (const item of client.imageJoinStreaming({
41899
+ imageId: resp.imageId,
41900
+ timeout: 55,
41901
+ lastEntryId
41902
+ })) {
41903
+ if (item.entryId) lastEntryId = item.entryId;
41904
+ if (item.result?.status) {
41905
+ resultJoined = item.result;
41906
+ break;
41907
+ }
41846
41908
  }
41847
41909
  }
41910
+ result = resultJoined;
41848
41911
  }
41849
- result = resultJoined;
41850
- }
41851
- void metadata;
41852
- if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41853
- throw new Error(
41854
- `Image build for ${resp.imageId} failed with the exception:
41912
+ if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41913
+ throw new Error(
41914
+ `Image build for ${resp.imageId} failed with the exception:
41855
41915
  ${result.exception}`
41856
- );
41857
- } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41858
- throw new Error(
41859
- `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41860
- );
41861
- } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41862
- throw new Error(
41863
- `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41864
- );
41865
- } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41866
- throw new Error(
41867
- `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41868
- );
41916
+ );
41917
+ } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41918
+ throw new Error(
41919
+ `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41920
+ );
41921
+ } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41922
+ throw new Error(
41923
+ `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41924
+ );
41925
+ } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41926
+ throw new Error(
41927
+ `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41928
+ );
41929
+ }
41930
+ baseImageId = resp.imageId;
41869
41931
  }
41870
- this.#imageId = resp.imageId;
41932
+ this.#imageId = baseImageId;
41871
41933
  return this;
41872
41934
  }
41873
41935
  /** Delete an Image by ID. Warning: This removes an *entire Image*, and cannot be undone. */
@@ -42619,7 +42681,7 @@ function parseGpuConfig(gpu) {
42619
42681
  gpuType: gpuType.toUpperCase()
42620
42682
  };
42621
42683
  }
42622
- var App = class _App {
42684
+ var App2 = class _App {
42623
42685
  appId;
42624
42686
  name;
42625
42687
  /** @ignore */
@@ -44167,7 +44229,7 @@ var Proxy2 = class _Proxy {
44167
44229
  };
44168
44230
  export {
44169
44231
  AlreadyExistsError,
44170
- App,
44232
+ App2 as App,
44171
44233
  CloudBucketMount2 as CloudBucketMount,
44172
44234
  Cls,
44173
44235
  ClsInstance,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modal",
3
- "version": "0.3.21",
3
+ "version": "0.3.22",
4
4
  "description": "Modal client library for JavaScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://modal.com/docs",