modal 0.3.11 → 0.3.13

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
@@ -48,6 +48,7 @@ __export(index_exports, {
48
48
  Sandbox: () => Sandbox2,
49
49
  SandboxFile: () => SandboxFile,
50
50
  Secret: () => Secret,
51
+ Volume: () => Volume,
51
52
  initializeClient: () => initializeClient
52
53
  });
53
54
  module.exports = __toCommonJS(index_exports);
@@ -37891,6 +37892,7 @@ function imageBuilderVersion(version) {
37891
37892
 
37892
37893
  // src/client.ts
37893
37894
  var defaultProfile = getProfile(process.env["MODAL_PROFILE"]);
37895
+ var modalAuthToken;
37894
37896
  function authMiddleware(profile) {
37895
37897
  return async function* authMiddleware2(call, options) {
37896
37898
  if (!profile.tokenId || !profile.tokenSecret) {
@@ -37907,6 +37909,25 @@ function authMiddleware(profile) {
37907
37909
  options.metadata.set("x-modal-client-version", "1.0.0");
37908
37910
  options.metadata.set("x-modal-token-id", tokenId);
37909
37911
  options.metadata.set("x-modal-token-secret", tokenSecret);
37912
+ if (modalAuthToken) {
37913
+ options.metadata.set("x-modal-auth-token", modalAuthToken);
37914
+ }
37915
+ const prevOnHeader = options.onHeader;
37916
+ options.onHeader = (header) => {
37917
+ const token = header.get("x-modal-auth-token");
37918
+ if (token) {
37919
+ modalAuthToken = token;
37920
+ }
37921
+ prevOnHeader?.(header);
37922
+ };
37923
+ const prevOnTrailer = options.onTrailer;
37924
+ options.onTrailer = (trailer) => {
37925
+ const token = trailer.get("x-modal-auth-token");
37926
+ if (token) {
37927
+ modalAuthToken = token;
37928
+ }
37929
+ prevOnTrailer?.(trailer);
37930
+ };
37910
37931
  return yield* call.next(call.request, options);
37911
37932
  };
37912
37933
  }
@@ -38012,6 +38033,17 @@ var retryMiddleware = async function* retryMiddleware2(call, options) {
38012
38033
  }
38013
38034
  }
38014
38035
  };
38036
+ var inputPlaneClients = {};
38037
+ var getOrCreateInputPlaneClient = (serverUrl) => {
38038
+ const client2 = inputPlaneClients[serverUrl];
38039
+ if (client2) {
38040
+ return client2;
38041
+ }
38042
+ const profile = { ...clientProfile, serverUrl };
38043
+ const newClient = createClient(profile);
38044
+ inputPlaneClients[serverUrl] = newClient;
38045
+ return newClient;
38046
+ };
38015
38047
  function createClient(profile) {
38016
38048
  const channel = (0, import_nice_grpc.createChannel)(profile.serverUrl, void 0, {
38017
38049
  "grpc.max_receive_message_length": 100 * 1024 * 1024,
@@ -38048,7 +38080,6 @@ async function fromRegistryInternal(appId, tag, imageRegistryConfig) {
38048
38080
  dockerfileCommands: [`FROM ${tag}`],
38049
38081
  imageRegistryConfig
38050
38082
  },
38051
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
38052
38083
  builderVersion: imageBuilderVersion()
38053
38084
  });
38054
38085
  let result;
@@ -38607,7 +38638,6 @@ var Secret = class _Secret {
38607
38638
  try {
38608
38639
  const resp = await client.secretGetOrCreate({
38609
38640
  deploymentName: name,
38610
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
38611
38641
  environmentName: environmentName(options?.environment),
38612
38642
  requiredKeys: options?.requiredKeys ?? []
38613
38643
  });
@@ -38650,6 +38680,12 @@ var App = class _App {
38650
38680
  `Timeout must be a multiple of 1000ms, got ${options.timeout}`
38651
38681
  );
38652
38682
  }
38683
+ const volumeMounts = options.volumes ? Object.entries(options.volumes).map(([mountPath, volume]) => ({
38684
+ volumeId: volume.volumeId,
38685
+ mountPath,
38686
+ allowBackgroundCommits: true,
38687
+ readOnly: false
38688
+ })) : [];
38653
38689
  const createResp = await client.sandboxCreate({
38654
38690
  appId: this.appId,
38655
38691
  definition: {
@@ -38664,13 +38700,26 @@ var App = class _App {
38664
38700
  // https://modal.com/docs/guide/resources
38665
38701
  milliCpu: Math.round(1e3 * (options.cpu ?? 0.125)),
38666
38702
  memoryMb: options.memory ?? 128
38667
- }
38703
+ },
38704
+ volumeMounts
38668
38705
  }
38669
38706
  });
38670
38707
  return new Sandbox2(createResp.sandboxId);
38671
38708
  }
38672
- async imageFromRegistry(tag) {
38673
- return await fromRegistryInternal(this.appId, tag);
38709
+ async imageFromRegistry(tag, secret) {
38710
+ let imageRegistryConfig;
38711
+ if (secret) {
38712
+ if (!(secret instanceof Secret)) {
38713
+ throw new TypeError(
38714
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38715
+ );
38716
+ }
38717
+ imageRegistryConfig = {
38718
+ registryAuthType: 4 /* REGISTRY_AUTH_TYPE_STATIC_CREDS */,
38719
+ secretId: secret.secretId
38720
+ };
38721
+ }
38722
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38674
38723
  }
38675
38724
  async imageFromAwsEcr(tag, secret) {
38676
38725
  if (!(secret instanceof Secret)) {
@@ -38684,6 +38733,18 @@ var App = class _App {
38684
38733
  };
38685
38734
  return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38686
38735
  }
38736
+ async imageFromGcpArtifactRegistry(tag, secret) {
38737
+ if (!(secret instanceof Secret)) {
38738
+ throw new TypeError(
38739
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38740
+ );
38741
+ }
38742
+ const imageRegistryConfig = {
38743
+ registryAuthType: 2 /* REGISTRY_AUTH_TYPE_GCP */,
38744
+ secretId: secret.secretId
38745
+ };
38746
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38747
+ }
38687
38748
  };
38688
38749
 
38689
38750
  // src/cls.ts
@@ -39074,7 +39135,22 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39074
39135
  return new _ControlPlaneInvocation(functionCallId);
39075
39136
  }
39076
39137
  async awaitOutput(timeout) {
39077
- return await pollFunctionOutput(this.functionCallId, timeout);
39138
+ return await pollFunctionOutput(
39139
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39140
+ timeout
39141
+ );
39142
+ }
39143
+ async #getOutput(timeoutMillis) {
39144
+ const response = await client.functionGetOutputs({
39145
+ functionCallId: this.functionCallId,
39146
+ maxValues: 1,
39147
+ timeout: timeoutMillis / 1e3,
39148
+ // Backend needs seconds
39149
+ lastEntryId: "0-0",
39150
+ clearOnSuccess: true,
39151
+ requestedAt: timeNowSeconds()
39152
+ });
39153
+ return response.outputs ? response.outputs[0] : void 0;
39078
39154
  }
39079
39155
  async retry(retryCount) {
39080
39156
  if (!this.input) {
@@ -39092,33 +39168,72 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39092
39168
  this.inputJwt = functionRetryResponse.inputJwts[0];
39093
39169
  }
39094
39170
  };
39171
+ var InputPlaneInvocation = class _InputPlaneInvocation {
39172
+ client;
39173
+ functionId;
39174
+ input;
39175
+ attemptToken;
39176
+ constructor(client2, functionId, input, attemptToken) {
39177
+ this.client = client2;
39178
+ this.functionId = functionId;
39179
+ this.input = input;
39180
+ this.attemptToken = attemptToken;
39181
+ }
39182
+ static async create(inputPlaneUrl, functionId, input) {
39183
+ const functionPutInputsItem = {
39184
+ idx: 0,
39185
+ input,
39186
+ r2Failed: false,
39187
+ r2LatencyMs: 0
39188
+ };
39189
+ const client2 = getOrCreateInputPlaneClient(inputPlaneUrl);
39190
+ const attemptStartResponse = await client2.attemptStart({
39191
+ functionId,
39192
+ input: functionPutInputsItem
39193
+ });
39194
+ return new _InputPlaneInvocation(
39195
+ client2,
39196
+ functionId,
39197
+ functionPutInputsItem,
39198
+ attemptStartResponse.attemptToken
39199
+ );
39200
+ }
39201
+ async awaitOutput(timeout) {
39202
+ return await pollFunctionOutput(
39203
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39204
+ timeout
39205
+ );
39206
+ }
39207
+ async #getOutput(timeoutMillis) {
39208
+ const response = await this.client.attemptAwait({
39209
+ attemptToken: this.attemptToken,
39210
+ requestedAt: timeNowSeconds(),
39211
+ timeoutSecs: timeoutMillis / 1e3
39212
+ });
39213
+ return response.output;
39214
+ }
39215
+ async retry(_retryCount) {
39216
+ const attemptRetryResponse = await this.client.attemptRetry({
39217
+ functionId: this.functionId,
39218
+ input: this.input,
39219
+ attemptToken: this.attemptToken
39220
+ });
39221
+ this.attemptToken = attemptRetryResponse.attemptToken;
39222
+ }
39223
+ };
39095
39224
  function timeNowSeconds() {
39096
39225
  return Date.now() / 1e3;
39097
39226
  }
39098
- async function pollFunctionOutput(functionCallId, timeout) {
39227
+ async function pollFunctionOutput(getOutput, timeout) {
39099
39228
  const startTime = Date.now();
39100
39229
  let pollTimeout = outputsTimeout;
39101
39230
  if (timeout !== void 0) {
39102
39231
  pollTimeout = Math.min(timeout, outputsTimeout);
39103
39232
  }
39104
39233
  while (true) {
39105
- let response;
39106
- try {
39107
- response = await client.functionGetOutputs({
39108
- functionCallId,
39109
- maxValues: 1,
39110
- timeout: pollTimeout / 1e3,
39111
- // Backend needs seconds
39112
- lastEntryId: "0-0",
39113
- clearOnSuccess: true,
39114
- requestedAt: timeNowSeconds()
39115
- });
39116
- } catch (err) {
39117
- throw new Error(`FunctionGetOutputs failed: ${err}`);
39118
- }
39119
- const outputs = response.outputs;
39120
- if (outputs.length > 0) {
39121
- return await processResult(outputs[0].result, outputs[0].dataFormat);
39234
+ const output = await getOutput(pollTimeout);
39235
+ if (output) {
39236
+ return await processResult(output.result, output.dataFormat);
39122
39237
  }
39123
39238
  if (timeout !== void 0) {
39124
39239
  const remainingTime = timeout - (Date.now() - startTime);
@@ -39212,20 +39327,25 @@ var maxSystemRetries = 8;
39212
39327
  var Function_ = class _Function_ {
39213
39328
  functionId;
39214
39329
  methodName;
39330
+ inputPlaneUrl;
39215
39331
  /** @ignore */
39216
- constructor(functionId, methodName) {
39332
+ constructor(functionId, methodName, inputPlaneUrl) {
39217
39333
  this.functionId = functionId;
39218
39334
  this.methodName = methodName;
39335
+ this.inputPlaneUrl = inputPlaneUrl;
39219
39336
  }
39220
39337
  static async lookup(appName, name, options = {}) {
39221
39338
  try {
39222
39339
  const resp = await client.functionGet({
39223
39340
  appName,
39224
39341
  objectTag: name,
39225
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39226
39342
  environmentName: environmentName(options.environment)
39227
39343
  });
39228
- return new _Function_(resp.functionId);
39344
+ return new _Function_(
39345
+ resp.functionId,
39346
+ void 0,
39347
+ resp.handleMetadata?.inputPlaneUrl
39348
+ );
39229
39349
  } catch (err) {
39230
39350
  if (err instanceof import_nice_grpc4.ClientError && err.code === import_nice_grpc4.Status.NOT_FOUND)
39231
39351
  throw new NotFoundError(`Function '${appName}/${name}' not found`);
@@ -39235,11 +39355,7 @@ var Function_ = class _Function_ {
39235
39355
  // Execute a single input into a remote Function.
39236
39356
  async remote(args = [], kwargs = {}) {
39237
39357
  const input = await this.#createInput(args, kwargs);
39238
- const invocation = await ControlPlaneInvocation.create(
39239
- this.functionId,
39240
- input,
39241
- 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39242
- );
39358
+ const invocation = await this.#createRemoteInvocation(input);
39243
39359
  let retryCount = 0;
39244
39360
  while (true) {
39245
39361
  try {
@@ -39254,6 +39370,20 @@ var Function_ = class _Function_ {
39254
39370
  }
39255
39371
  }
39256
39372
  }
39373
+ async #createRemoteInvocation(input) {
39374
+ if (this.inputPlaneUrl) {
39375
+ return await InputPlaneInvocation.create(
39376
+ this.inputPlaneUrl,
39377
+ this.functionId,
39378
+ input
39379
+ );
39380
+ }
39381
+ return await ControlPlaneInvocation.create(
39382
+ this.functionId,
39383
+ input,
39384
+ 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39385
+ );
39386
+ }
39257
39387
  // Spawn a single input into a remote function.
39258
39388
  async spawn(args = [], kwargs = {}) {
39259
39389
  const input = await this.#createInput(args, kwargs);
@@ -39315,11 +39445,13 @@ var Cls = class _Cls {
39315
39445
  #serviceFunctionId;
39316
39446
  #schema;
39317
39447
  #methodNames;
39448
+ #inputPlaneUrl;
39318
39449
  /** @ignore */
39319
- constructor(serviceFunctionId, schema, methodNames) {
39450
+ constructor(serviceFunctionId, schema, methodNames, inputPlaneUrl) {
39320
39451
  this.#serviceFunctionId = serviceFunctionId;
39321
39452
  this.#schema = schema;
39322
39453
  this.#methodNames = methodNames;
39454
+ this.#inputPlaneUrl = inputPlaneUrl;
39323
39455
  }
39324
39456
  static async lookup(appName, name, options = {}) {
39325
39457
  try {
@@ -39327,7 +39459,6 @@ var Cls = class _Cls {
39327
39459
  const serviceFunction = await client.functionGet({
39328
39460
  appName,
39329
39461
  objectTag: serviceFunctionName,
39330
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39331
39462
  environmentName: environmentName(options.environment)
39332
39463
  });
39333
39464
  const parameterInfo = serviceFunction.handleMetadata?.classParameterInfo;
@@ -39347,7 +39478,12 @@ var Cls = class _Cls {
39347
39478
  "Cls requires Modal deployments using client v0.67 or later."
39348
39479
  );
39349
39480
  }
39350
- return new _Cls(serviceFunction.functionId, schema, methodNames);
39481
+ return new _Cls(
39482
+ serviceFunction.functionId,
39483
+ schema,
39484
+ methodNames,
39485
+ serviceFunction.handleMetadata?.inputPlaneUrl
39486
+ );
39351
39487
  } catch (err) {
39352
39488
  if (err instanceof import_nice_grpc5.ClientError && err.code === import_nice_grpc5.Status.NOT_FOUND)
39353
39489
  throw new NotFoundError(`Class '${appName}/${name}' not found`);
@@ -39364,7 +39500,7 @@ var Cls = class _Cls {
39364
39500
  }
39365
39501
  const methods = /* @__PURE__ */ new Map();
39366
39502
  for (const name of this.#methodNames) {
39367
- methods.set(name, new Function_(functionId, name));
39503
+ methods.set(name, new Function_(functionId, name, this.#inputPlaneUrl));
39368
39504
  }
39369
39505
  return new ClsInstance(methods);
39370
39506
  }
@@ -39515,7 +39651,6 @@ var Queue = class _Queue {
39515
39651
  const resp = await client.queueGetOrCreate({
39516
39652
  deploymentName: name,
39517
39653
  objectCreationType: options.createIfMissing ? 1 /* OBJECT_CREATION_TYPE_CREATE_IF_MISSING */ : void 0,
39518
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39519
39654
  environmentName: environmentName(options.environment)
39520
39655
  });
39521
39656
  return new _Queue(resp.queueId);
@@ -39693,6 +39828,30 @@ var Queue = class _Queue {
39693
39828
  }
39694
39829
  }
39695
39830
  };
39831
+
39832
+ // src/volume.ts
39833
+ var import_nice_grpc7 = require("nice-grpc");
39834
+ var Volume = class _Volume {
39835
+ volumeId;
39836
+ /** @ignore */
39837
+ constructor(volumeId) {
39838
+ this.volumeId = volumeId;
39839
+ }
39840
+ static async fromName(name, options) {
39841
+ try {
39842
+ const resp = await client.volumeGetOrCreate({
39843
+ deploymentName: name,
39844
+ environmentName: environmentName(options?.environment),
39845
+ objectCreationType: options?.createIfMissing ? 1 /* OBJECT_CREATION_TYPE_CREATE_IF_MISSING */ : 0 /* OBJECT_CREATION_TYPE_UNSPECIFIED */
39846
+ });
39847
+ return new _Volume(resp.volumeId);
39848
+ } catch (err) {
39849
+ if (err instanceof import_nice_grpc7.ClientError && err.code === import_nice_grpc7.Status.NOT_FOUND)
39850
+ throw new NotFoundError(err.details);
39851
+ throw err;
39852
+ }
39853
+ }
39854
+ };
39696
39855
  // Annotate the CommonJS export names for ESM import in node:
39697
39856
  0 && (module.exports = {
39698
39857
  App,
@@ -39713,5 +39872,6 @@ var Queue = class _Queue {
39713
39872
  Sandbox,
39714
39873
  SandboxFile,
39715
39874
  Secret,
39875
+ Volume,
39716
39876
  initializeClient
39717
39877
  });
package/dist/index.d.cts CHANGED
@@ -200,6 +200,19 @@ declare class Secret {
200
200
  static fromName(name: string, options?: SecretFromNameOptions): Promise<Secret>;
201
201
  }
202
202
 
203
+ /** Options for `Volume.fromName()`. */
204
+ type VolumeFromNameOptions = {
205
+ environment?: string;
206
+ createIfMissing?: boolean;
207
+ };
208
+ /** Volumes provide persistent storage that can be mounted in Modal functions. */
209
+ declare class Volume {
210
+ readonly volumeId: string;
211
+ /** @ignore */
212
+ constructor(volumeId: string);
213
+ static fromName(name: string, options?: VolumeFromNameOptions): Promise<Volume>;
214
+ }
215
+
203
216
  /** Options for functions that find deployed Modal objects. */
204
217
  type LookupOptions = {
205
218
  environment?: string;
@@ -226,6 +239,8 @@ type SandboxCreateOptions = {
226
239
  * Default behavior is to sleep indefinitely until timeout or termination.
227
240
  */
228
241
  command?: string[];
242
+ /** Mount points for Modal Volumes. */
243
+ volumes?: Record<string, Volume>;
229
244
  };
230
245
  /** Represents a deployed Modal App. */
231
246
  declare class App {
@@ -235,8 +250,9 @@ declare class App {
235
250
  /** Lookup a deployed app by name, or create if it does not exist. */
236
251
  static lookup(name: string, options?: LookupOptions): Promise<App>;
237
252
  createSandbox(image: Image, options?: SandboxCreateOptions): Promise<Sandbox>;
238
- imageFromRegistry(tag: string): Promise<Image>;
253
+ imageFromRegistry(tag: string, secret?: Secret): Promise<Image>;
239
254
  imageFromAwsEcr(tag: string, secret: Secret): Promise<Image>;
255
+ imageFromGcpArtifactRegistry(tag: string, secret: Secret): Promise<Image>;
240
256
  }
241
257
 
242
258
  /** Options for initializing a client at runtime. */
@@ -283,8 +299,9 @@ declare class Function_ {
283
299
  #private;
284
300
  readonly functionId: string;
285
301
  readonly methodName: string | undefined;
302
+ readonly inputPlaneUrl: string | undefined;
286
303
  /** @ignore */
287
- constructor(functionId: string, methodName?: string);
304
+ constructor(functionId: string, methodName?: string, inputPlaneUrl?: string);
288
305
  static lookup(appName: string, name: string, options?: LookupOptions): Promise<Function_>;
289
306
  remote(args?: any[], kwargs?: Record<string, any>): Promise<any>;
290
307
  spawn(args?: any[], kwargs?: Record<string, any>): Promise<FunctionCall>;
@@ -294,7 +311,7 @@ declare class Function_ {
294
311
  declare class Cls {
295
312
  #private;
296
313
  /** @ignore */
297
- constructor(serviceFunctionId: string, schema: ClassParameterSpec[], methodNames: string[]);
314
+ constructor(serviceFunctionId: string, schema: ClassParameterSpec[], methodNames: string[], inputPlaneUrl?: string);
298
315
  static lookup(appName: string, name: string, options?: LookupOptions): Promise<Cls>;
299
316
  /** Create a new instance of the Cls with parameters. */
300
317
  instance(params?: Record<string, any>): Promise<ClsInstance>;
@@ -435,4 +452,4 @@ declare class Queue {
435
452
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
436
453
  }
437
454
 
438
- export { App, type ClientOptions, Cls, ClsInstance, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, FunctionTimeoutError, Function_, Image, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, initializeClient };
455
+ export { App, type ClientOptions, Cls, ClsInstance, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, FunctionTimeoutError, Function_, Image, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Volume, type VolumeFromNameOptions, initializeClient };
package/dist/index.d.ts CHANGED
@@ -200,6 +200,19 @@ declare class Secret {
200
200
  static fromName(name: string, options?: SecretFromNameOptions): Promise<Secret>;
201
201
  }
202
202
 
203
+ /** Options for `Volume.fromName()`. */
204
+ type VolumeFromNameOptions = {
205
+ environment?: string;
206
+ createIfMissing?: boolean;
207
+ };
208
+ /** Volumes provide persistent storage that can be mounted in Modal functions. */
209
+ declare class Volume {
210
+ readonly volumeId: string;
211
+ /** @ignore */
212
+ constructor(volumeId: string);
213
+ static fromName(name: string, options?: VolumeFromNameOptions): Promise<Volume>;
214
+ }
215
+
203
216
  /** Options for functions that find deployed Modal objects. */
204
217
  type LookupOptions = {
205
218
  environment?: string;
@@ -226,6 +239,8 @@ type SandboxCreateOptions = {
226
239
  * Default behavior is to sleep indefinitely until timeout or termination.
227
240
  */
228
241
  command?: string[];
242
+ /** Mount points for Modal Volumes. */
243
+ volumes?: Record<string, Volume>;
229
244
  };
230
245
  /** Represents a deployed Modal App. */
231
246
  declare class App {
@@ -235,8 +250,9 @@ declare class App {
235
250
  /** Lookup a deployed app by name, or create if it does not exist. */
236
251
  static lookup(name: string, options?: LookupOptions): Promise<App>;
237
252
  createSandbox(image: Image, options?: SandboxCreateOptions): Promise<Sandbox>;
238
- imageFromRegistry(tag: string): Promise<Image>;
253
+ imageFromRegistry(tag: string, secret?: Secret): Promise<Image>;
239
254
  imageFromAwsEcr(tag: string, secret: Secret): Promise<Image>;
255
+ imageFromGcpArtifactRegistry(tag: string, secret: Secret): Promise<Image>;
240
256
  }
241
257
 
242
258
  /** Options for initializing a client at runtime. */
@@ -283,8 +299,9 @@ declare class Function_ {
283
299
  #private;
284
300
  readonly functionId: string;
285
301
  readonly methodName: string | undefined;
302
+ readonly inputPlaneUrl: string | undefined;
286
303
  /** @ignore */
287
- constructor(functionId: string, methodName?: string);
304
+ constructor(functionId: string, methodName?: string, inputPlaneUrl?: string);
288
305
  static lookup(appName: string, name: string, options?: LookupOptions): Promise<Function_>;
289
306
  remote(args?: any[], kwargs?: Record<string, any>): Promise<any>;
290
307
  spawn(args?: any[], kwargs?: Record<string, any>): Promise<FunctionCall>;
@@ -294,7 +311,7 @@ declare class Function_ {
294
311
  declare class Cls {
295
312
  #private;
296
313
  /** @ignore */
297
- constructor(serviceFunctionId: string, schema: ClassParameterSpec[], methodNames: string[]);
314
+ constructor(serviceFunctionId: string, schema: ClassParameterSpec[], methodNames: string[], inputPlaneUrl?: string);
298
315
  static lookup(appName: string, name: string, options?: LookupOptions): Promise<Cls>;
299
316
  /** Create a new instance of the Cls with parameters. */
300
317
  instance(params?: Record<string, any>): Promise<ClsInstance>;
@@ -435,4 +452,4 @@ declare class Queue {
435
452
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
436
453
  }
437
454
 
438
- export { App, type ClientOptions, Cls, ClsInstance, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, FunctionTimeoutError, Function_, Image, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, initializeClient };
455
+ export { App, type ClientOptions, Cls, ClsInstance, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, FunctionTimeoutError, Function_, Image, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Volume, type VolumeFromNameOptions, initializeClient };
package/dist/index.js CHANGED
@@ -37843,6 +37843,7 @@ function imageBuilderVersion(version) {
37843
37843
 
37844
37844
  // src/client.ts
37845
37845
  var defaultProfile = getProfile(process.env["MODAL_PROFILE"]);
37846
+ var modalAuthToken;
37846
37847
  function authMiddleware(profile) {
37847
37848
  return async function* authMiddleware2(call, options) {
37848
37849
  if (!profile.tokenId || !profile.tokenSecret) {
@@ -37859,6 +37860,25 @@ function authMiddleware(profile) {
37859
37860
  options.metadata.set("x-modal-client-version", "1.0.0");
37860
37861
  options.metadata.set("x-modal-token-id", tokenId);
37861
37862
  options.metadata.set("x-modal-token-secret", tokenSecret);
37863
+ if (modalAuthToken) {
37864
+ options.metadata.set("x-modal-auth-token", modalAuthToken);
37865
+ }
37866
+ const prevOnHeader = options.onHeader;
37867
+ options.onHeader = (header) => {
37868
+ const token = header.get("x-modal-auth-token");
37869
+ if (token) {
37870
+ modalAuthToken = token;
37871
+ }
37872
+ prevOnHeader?.(header);
37873
+ };
37874
+ const prevOnTrailer = options.onTrailer;
37875
+ options.onTrailer = (trailer) => {
37876
+ const token = trailer.get("x-modal-auth-token");
37877
+ if (token) {
37878
+ modalAuthToken = token;
37879
+ }
37880
+ prevOnTrailer?.(trailer);
37881
+ };
37862
37882
  return yield* call.next(call.request, options);
37863
37883
  };
37864
37884
  }
@@ -37964,6 +37984,17 @@ var retryMiddleware = async function* retryMiddleware2(call, options) {
37964
37984
  }
37965
37985
  }
37966
37986
  };
37987
+ var inputPlaneClients = {};
37988
+ var getOrCreateInputPlaneClient = (serverUrl) => {
37989
+ const client2 = inputPlaneClients[serverUrl];
37990
+ if (client2) {
37991
+ return client2;
37992
+ }
37993
+ const profile = { ...clientProfile, serverUrl };
37994
+ const newClient = createClient(profile);
37995
+ inputPlaneClients[serverUrl] = newClient;
37996
+ return newClient;
37997
+ };
37967
37998
  function createClient(profile) {
37968
37999
  const channel = createChannel(profile.serverUrl, void 0, {
37969
38000
  "grpc.max_receive_message_length": 100 * 1024 * 1024,
@@ -38000,7 +38031,6 @@ async function fromRegistryInternal(appId, tag, imageRegistryConfig) {
38000
38031
  dockerfileCommands: [`FROM ${tag}`],
38001
38032
  imageRegistryConfig
38002
38033
  },
38003
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
38004
38034
  builderVersion: imageBuilderVersion()
38005
38035
  });
38006
38036
  let result;
@@ -38559,7 +38589,6 @@ var Secret = class _Secret {
38559
38589
  try {
38560
38590
  const resp = await client.secretGetOrCreate({
38561
38591
  deploymentName: name,
38562
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
38563
38592
  environmentName: environmentName(options?.environment),
38564
38593
  requiredKeys: options?.requiredKeys ?? []
38565
38594
  });
@@ -38602,6 +38631,12 @@ var App = class _App {
38602
38631
  `Timeout must be a multiple of 1000ms, got ${options.timeout}`
38603
38632
  );
38604
38633
  }
38634
+ const volumeMounts = options.volumes ? Object.entries(options.volumes).map(([mountPath, volume]) => ({
38635
+ volumeId: volume.volumeId,
38636
+ mountPath,
38637
+ allowBackgroundCommits: true,
38638
+ readOnly: false
38639
+ })) : [];
38605
38640
  const createResp = await client.sandboxCreate({
38606
38641
  appId: this.appId,
38607
38642
  definition: {
@@ -38616,13 +38651,26 @@ var App = class _App {
38616
38651
  // https://modal.com/docs/guide/resources
38617
38652
  milliCpu: Math.round(1e3 * (options.cpu ?? 0.125)),
38618
38653
  memoryMb: options.memory ?? 128
38619
- }
38654
+ },
38655
+ volumeMounts
38620
38656
  }
38621
38657
  });
38622
38658
  return new Sandbox2(createResp.sandboxId);
38623
38659
  }
38624
- async imageFromRegistry(tag) {
38625
- return await fromRegistryInternal(this.appId, tag);
38660
+ async imageFromRegistry(tag, secret) {
38661
+ let imageRegistryConfig;
38662
+ if (secret) {
38663
+ if (!(secret instanceof Secret)) {
38664
+ throw new TypeError(
38665
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38666
+ );
38667
+ }
38668
+ imageRegistryConfig = {
38669
+ registryAuthType: 4 /* REGISTRY_AUTH_TYPE_STATIC_CREDS */,
38670
+ secretId: secret.secretId
38671
+ };
38672
+ }
38673
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38626
38674
  }
38627
38675
  async imageFromAwsEcr(tag, secret) {
38628
38676
  if (!(secret instanceof Secret)) {
@@ -38636,6 +38684,18 @@ var App = class _App {
38636
38684
  };
38637
38685
  return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38638
38686
  }
38687
+ async imageFromGcpArtifactRegistry(tag, secret) {
38688
+ if (!(secret instanceof Secret)) {
38689
+ throw new TypeError(
38690
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38691
+ );
38692
+ }
38693
+ const imageRegistryConfig = {
38694
+ registryAuthType: 2 /* REGISTRY_AUTH_TYPE_GCP */,
38695
+ secretId: secret.secretId
38696
+ };
38697
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38698
+ }
38639
38699
  };
38640
38700
 
38641
38701
  // src/cls.ts
@@ -39026,7 +39086,22 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39026
39086
  return new _ControlPlaneInvocation(functionCallId);
39027
39087
  }
39028
39088
  async awaitOutput(timeout) {
39029
- return await pollFunctionOutput(this.functionCallId, timeout);
39089
+ return await pollFunctionOutput(
39090
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39091
+ timeout
39092
+ );
39093
+ }
39094
+ async #getOutput(timeoutMillis) {
39095
+ const response = await client.functionGetOutputs({
39096
+ functionCallId: this.functionCallId,
39097
+ maxValues: 1,
39098
+ timeout: timeoutMillis / 1e3,
39099
+ // Backend needs seconds
39100
+ lastEntryId: "0-0",
39101
+ clearOnSuccess: true,
39102
+ requestedAt: timeNowSeconds()
39103
+ });
39104
+ return response.outputs ? response.outputs[0] : void 0;
39030
39105
  }
39031
39106
  async retry(retryCount) {
39032
39107
  if (!this.input) {
@@ -39044,33 +39119,72 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39044
39119
  this.inputJwt = functionRetryResponse.inputJwts[0];
39045
39120
  }
39046
39121
  };
39122
+ var InputPlaneInvocation = class _InputPlaneInvocation {
39123
+ client;
39124
+ functionId;
39125
+ input;
39126
+ attemptToken;
39127
+ constructor(client2, functionId, input, attemptToken) {
39128
+ this.client = client2;
39129
+ this.functionId = functionId;
39130
+ this.input = input;
39131
+ this.attemptToken = attemptToken;
39132
+ }
39133
+ static async create(inputPlaneUrl, functionId, input) {
39134
+ const functionPutInputsItem = {
39135
+ idx: 0,
39136
+ input,
39137
+ r2Failed: false,
39138
+ r2LatencyMs: 0
39139
+ };
39140
+ const client2 = getOrCreateInputPlaneClient(inputPlaneUrl);
39141
+ const attemptStartResponse = await client2.attemptStart({
39142
+ functionId,
39143
+ input: functionPutInputsItem
39144
+ });
39145
+ return new _InputPlaneInvocation(
39146
+ client2,
39147
+ functionId,
39148
+ functionPutInputsItem,
39149
+ attemptStartResponse.attemptToken
39150
+ );
39151
+ }
39152
+ async awaitOutput(timeout) {
39153
+ return await pollFunctionOutput(
39154
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39155
+ timeout
39156
+ );
39157
+ }
39158
+ async #getOutput(timeoutMillis) {
39159
+ const response = await this.client.attemptAwait({
39160
+ attemptToken: this.attemptToken,
39161
+ requestedAt: timeNowSeconds(),
39162
+ timeoutSecs: timeoutMillis / 1e3
39163
+ });
39164
+ return response.output;
39165
+ }
39166
+ async retry(_retryCount) {
39167
+ const attemptRetryResponse = await this.client.attemptRetry({
39168
+ functionId: this.functionId,
39169
+ input: this.input,
39170
+ attemptToken: this.attemptToken
39171
+ });
39172
+ this.attemptToken = attemptRetryResponse.attemptToken;
39173
+ }
39174
+ };
39047
39175
  function timeNowSeconds() {
39048
39176
  return Date.now() / 1e3;
39049
39177
  }
39050
- async function pollFunctionOutput(functionCallId, timeout) {
39178
+ async function pollFunctionOutput(getOutput, timeout) {
39051
39179
  const startTime = Date.now();
39052
39180
  let pollTimeout = outputsTimeout;
39053
39181
  if (timeout !== void 0) {
39054
39182
  pollTimeout = Math.min(timeout, outputsTimeout);
39055
39183
  }
39056
39184
  while (true) {
39057
- let response;
39058
- try {
39059
- response = await client.functionGetOutputs({
39060
- functionCallId,
39061
- maxValues: 1,
39062
- timeout: pollTimeout / 1e3,
39063
- // Backend needs seconds
39064
- lastEntryId: "0-0",
39065
- clearOnSuccess: true,
39066
- requestedAt: timeNowSeconds()
39067
- });
39068
- } catch (err) {
39069
- throw new Error(`FunctionGetOutputs failed: ${err}`);
39070
- }
39071
- const outputs = response.outputs;
39072
- if (outputs.length > 0) {
39073
- return await processResult(outputs[0].result, outputs[0].dataFormat);
39185
+ const output = await getOutput(pollTimeout);
39186
+ if (output) {
39187
+ return await processResult(output.result, output.dataFormat);
39074
39188
  }
39075
39189
  if (timeout !== void 0) {
39076
39190
  const remainingTime = timeout - (Date.now() - startTime);
@@ -39164,20 +39278,25 @@ var maxSystemRetries = 8;
39164
39278
  var Function_ = class _Function_ {
39165
39279
  functionId;
39166
39280
  methodName;
39281
+ inputPlaneUrl;
39167
39282
  /** @ignore */
39168
- constructor(functionId, methodName) {
39283
+ constructor(functionId, methodName, inputPlaneUrl) {
39169
39284
  this.functionId = functionId;
39170
39285
  this.methodName = methodName;
39286
+ this.inputPlaneUrl = inputPlaneUrl;
39171
39287
  }
39172
39288
  static async lookup(appName, name, options = {}) {
39173
39289
  try {
39174
39290
  const resp = await client.functionGet({
39175
39291
  appName,
39176
39292
  objectTag: name,
39177
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39178
39293
  environmentName: environmentName(options.environment)
39179
39294
  });
39180
- return new _Function_(resp.functionId);
39295
+ return new _Function_(
39296
+ resp.functionId,
39297
+ void 0,
39298
+ resp.handleMetadata?.inputPlaneUrl
39299
+ );
39181
39300
  } catch (err) {
39182
39301
  if (err instanceof ClientError4 && err.code === Status4.NOT_FOUND)
39183
39302
  throw new NotFoundError(`Function '${appName}/${name}' not found`);
@@ -39187,11 +39306,7 @@ var Function_ = class _Function_ {
39187
39306
  // Execute a single input into a remote Function.
39188
39307
  async remote(args = [], kwargs = {}) {
39189
39308
  const input = await this.#createInput(args, kwargs);
39190
- const invocation = await ControlPlaneInvocation.create(
39191
- this.functionId,
39192
- input,
39193
- 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39194
- );
39309
+ const invocation = await this.#createRemoteInvocation(input);
39195
39310
  let retryCount = 0;
39196
39311
  while (true) {
39197
39312
  try {
@@ -39206,6 +39321,20 @@ var Function_ = class _Function_ {
39206
39321
  }
39207
39322
  }
39208
39323
  }
39324
+ async #createRemoteInvocation(input) {
39325
+ if (this.inputPlaneUrl) {
39326
+ return await InputPlaneInvocation.create(
39327
+ this.inputPlaneUrl,
39328
+ this.functionId,
39329
+ input
39330
+ );
39331
+ }
39332
+ return await ControlPlaneInvocation.create(
39333
+ this.functionId,
39334
+ input,
39335
+ 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39336
+ );
39337
+ }
39209
39338
  // Spawn a single input into a remote function.
39210
39339
  async spawn(args = [], kwargs = {}) {
39211
39340
  const input = await this.#createInput(args, kwargs);
@@ -39267,11 +39396,13 @@ var Cls = class _Cls {
39267
39396
  #serviceFunctionId;
39268
39397
  #schema;
39269
39398
  #methodNames;
39399
+ #inputPlaneUrl;
39270
39400
  /** @ignore */
39271
- constructor(serviceFunctionId, schema, methodNames) {
39401
+ constructor(serviceFunctionId, schema, methodNames, inputPlaneUrl) {
39272
39402
  this.#serviceFunctionId = serviceFunctionId;
39273
39403
  this.#schema = schema;
39274
39404
  this.#methodNames = methodNames;
39405
+ this.#inputPlaneUrl = inputPlaneUrl;
39275
39406
  }
39276
39407
  static async lookup(appName, name, options = {}) {
39277
39408
  try {
@@ -39279,7 +39410,6 @@ var Cls = class _Cls {
39279
39410
  const serviceFunction = await client.functionGet({
39280
39411
  appName,
39281
39412
  objectTag: serviceFunctionName,
39282
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39283
39413
  environmentName: environmentName(options.environment)
39284
39414
  });
39285
39415
  const parameterInfo = serviceFunction.handleMetadata?.classParameterInfo;
@@ -39299,7 +39429,12 @@ var Cls = class _Cls {
39299
39429
  "Cls requires Modal deployments using client v0.67 or later."
39300
39430
  );
39301
39431
  }
39302
- return new _Cls(serviceFunction.functionId, schema, methodNames);
39432
+ return new _Cls(
39433
+ serviceFunction.functionId,
39434
+ schema,
39435
+ methodNames,
39436
+ serviceFunction.handleMetadata?.inputPlaneUrl
39437
+ );
39303
39438
  } catch (err) {
39304
39439
  if (err instanceof ClientError5 && err.code === Status5.NOT_FOUND)
39305
39440
  throw new NotFoundError(`Class '${appName}/${name}' not found`);
@@ -39316,7 +39451,7 @@ var Cls = class _Cls {
39316
39451
  }
39317
39452
  const methods = /* @__PURE__ */ new Map();
39318
39453
  for (const name of this.#methodNames) {
39319
- methods.set(name, new Function_(functionId, name));
39454
+ methods.set(name, new Function_(functionId, name, this.#inputPlaneUrl));
39320
39455
  }
39321
39456
  return new ClsInstance(methods);
39322
39457
  }
@@ -39467,7 +39602,6 @@ var Queue = class _Queue {
39467
39602
  const resp = await client.queueGetOrCreate({
39468
39603
  deploymentName: name,
39469
39604
  objectCreationType: options.createIfMissing ? 1 /* OBJECT_CREATION_TYPE_CREATE_IF_MISSING */ : void 0,
39470
- namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39471
39605
  environmentName: environmentName(options.environment)
39472
39606
  });
39473
39607
  return new _Queue(resp.queueId);
@@ -39645,6 +39779,30 @@ var Queue = class _Queue {
39645
39779
  }
39646
39780
  }
39647
39781
  };
39782
+
39783
+ // src/volume.ts
39784
+ import { ClientError as ClientError7, Status as Status7 } from "nice-grpc";
39785
+ var Volume = class _Volume {
39786
+ volumeId;
39787
+ /** @ignore */
39788
+ constructor(volumeId) {
39789
+ this.volumeId = volumeId;
39790
+ }
39791
+ static async fromName(name, options) {
39792
+ try {
39793
+ const resp = await client.volumeGetOrCreate({
39794
+ deploymentName: name,
39795
+ environmentName: environmentName(options?.environment),
39796
+ objectCreationType: options?.createIfMissing ? 1 /* OBJECT_CREATION_TYPE_CREATE_IF_MISSING */ : 0 /* OBJECT_CREATION_TYPE_UNSPECIFIED */
39797
+ });
39798
+ return new _Volume(resp.volumeId);
39799
+ } catch (err) {
39800
+ if (err instanceof ClientError7 && err.code === Status7.NOT_FOUND)
39801
+ throw new NotFoundError(err.details);
39802
+ throw err;
39803
+ }
39804
+ }
39805
+ };
39648
39806
  export {
39649
39807
  App,
39650
39808
  Cls,
@@ -39664,5 +39822,6 @@ export {
39664
39822
  Sandbox2 as Sandbox,
39665
39823
  SandboxFile,
39666
39824
  Secret,
39825
+ Volume,
39667
39826
  initializeClient
39668
39827
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modal",
3
- "version": "0.3.11",
3
+ "version": "0.3.13",
4
4
  "description": "Modal client library for JavaScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://modal.com/docs",