modal 0.3.10 → 0.3.12

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
@@ -47,7 +47,8 @@ __export(index_exports, {
47
47
  RemoteError: () => RemoteError,
48
48
  Sandbox: () => Sandbox2,
49
49
  SandboxFile: () => SandboxFile,
50
- Secret: () => Secret
50
+ Secret: () => Secret,
51
+ initializeClient: () => initializeClient
51
52
  });
52
53
  module.exports = __toCommonJS(index_exports);
53
54
 
@@ -37858,12 +37859,11 @@ function readConfigFile() {
37858
37859
  if (err.code === "ENOENT") {
37859
37860
  return {};
37860
37861
  }
37861
- throw new Error(`Failed to read or parse .modal.toml: ${err.message}`);
37862
+ return {};
37862
37863
  }
37863
37864
  }
37864
37865
  var config = readConfigFile();
37865
37866
  function getProfile(profileName) {
37866
- profileName = profileName || process.env["MODAL_PROFILE"] || void 0;
37867
37867
  if (!profileName) {
37868
37868
  for (const [name, profileData2] of Object.entries(config)) {
37869
37869
  if (profileData2.active) {
@@ -37872,45 +37872,61 @@ function getProfile(profileName) {
37872
37872
  }
37873
37873
  }
37874
37874
  }
37875
- if (profileName && !Object.hasOwn(config, profileName)) {
37876
- throw new Error(
37877
- `Profile "${profileName}" not found in .modal.toml. Please set the MODAL_PROFILE environment variable or specify a valid profile.`
37878
- );
37879
- }
37880
- const profileData = profileName ? config[profileName] : {};
37881
- const profile2 = {
37875
+ const profileData = profileName && Object.hasOwn(config, profileName) ? config[profileName] : {};
37876
+ const profile = {
37882
37877
  serverUrl: process.env["MODAL_SERVER_URL"] || profileData.server_url || "https://api.modal.com:443",
37883
37878
  tokenId: process.env["MODAL_TOKEN_ID"] || profileData.token_id,
37884
37879
  tokenSecret: process.env["MODAL_TOKEN_SECRET"] || profileData.token_secret,
37885
37880
  environment: process.env["MODAL_ENVIRONMENT"] || profileData.environment,
37886
37881
  imageBuilderVersion: process.env["MODAL_IMAGE_BUILDER_VERSION"] || profileData.imageBuilderVersion
37887
37882
  };
37888
- if (!profile2.tokenId || !profile2.tokenSecret) {
37889
- throw new Error(
37890
- `Profile "${profileName}" is missing token_id or token_secret. Please set them in .modal.toml or as environment variables.`
37891
- );
37892
- }
37893
- return profile2;
37883
+ return profile;
37894
37884
  }
37895
- var profile = getProfile(process.env["MODAL_PROFILE"] || void 0);
37896
37885
  function environmentName(environment) {
37897
- return environment || profile.environment || "";
37886
+ return environment || clientProfile.environment || "";
37898
37887
  }
37899
37888
  function imageBuilderVersion(version) {
37900
- return version || profile.imageBuilderVersion || "2024.10";
37889
+ return version || clientProfile.imageBuilderVersion || "2024.10";
37901
37890
  }
37902
37891
 
37903
37892
  // src/client.ts
37904
- function authMiddleware(profile2) {
37893
+ var defaultProfile = getProfile(process.env["MODAL_PROFILE"]);
37894
+ var modalAuthToken;
37895
+ function authMiddleware(profile) {
37905
37896
  return async function* authMiddleware2(call, options) {
37897
+ if (!profile.tokenId || !profile.tokenSecret) {
37898
+ throw new Error(
37899
+ `Profile is missing token_id or token_secret. Please set them in .modal.toml, or as environment variables, or initializeClient().`
37900
+ );
37901
+ }
37902
+ const { tokenId, tokenSecret } = profile;
37906
37903
  options.metadata ??= new import_nice_grpc.Metadata();
37907
37904
  options.metadata.set(
37908
37905
  "x-modal-client-type",
37909
37906
  String(7 /* CLIENT_TYPE_LIBMODAL */)
37910
37907
  );
37911
37908
  options.metadata.set("x-modal-client-version", "1.0.0");
37912
- options.metadata.set("x-modal-token-id", profile2.tokenId);
37913
- options.metadata.set("x-modal-token-secret", profile2.tokenSecret);
37909
+ options.metadata.set("x-modal-token-id", tokenId);
37910
+ options.metadata.set("x-modal-token-secret", tokenSecret);
37911
+ if (modalAuthToken) {
37912
+ options.metadata.set("x-modal-auth-token", modalAuthToken);
37913
+ }
37914
+ const prevOnHeader = options.onHeader;
37915
+ options.onHeader = (header) => {
37916
+ const token = header.get("x-modal-auth-token");
37917
+ if (token) {
37918
+ modalAuthToken = token;
37919
+ }
37920
+ prevOnHeader?.(header);
37921
+ };
37922
+ const prevOnTrailer = options.onTrailer;
37923
+ options.onTrailer = (trailer) => {
37924
+ const token = trailer.get("x-modal-auth-token");
37925
+ if (token) {
37926
+ modalAuthToken = token;
37927
+ }
37928
+ prevOnTrailer?.(trailer);
37929
+ };
37914
37930
  return yield* call.next(call.request, options);
37915
37931
  };
37916
37932
  }
@@ -38016,12 +38032,37 @@ var retryMiddleware = async function* retryMiddleware2(call, options) {
38016
38032
  }
38017
38033
  }
38018
38034
  };
38019
- var channel = (0, import_nice_grpc.createChannel)(profile.serverUrl, void 0, {
38020
- "grpc.max_receive_message_length": 100 * 1024 * 1024,
38021
- "grpc.max_send_message_length": 100 * 1024 * 1024,
38022
- "grpc-node.flow_control_window": 64 * 1024 * 1024
38023
- });
38024
- var client = (0, import_nice_grpc.createClientFactory)().use(authMiddleware(profile)).use(retryMiddleware).use(timeoutMiddleware).create(ModalClientDefinition, channel);
38035
+ var inputPlaneClients = {};
38036
+ var getOrCreateInputPlaneClient = (serverUrl) => {
38037
+ const client2 = inputPlaneClients[serverUrl];
38038
+ if (client2) {
38039
+ return client2;
38040
+ }
38041
+ const profile = { ...clientProfile, serverUrl };
38042
+ const newClient = createClient(profile);
38043
+ inputPlaneClients[serverUrl] = newClient;
38044
+ return newClient;
38045
+ };
38046
+ function createClient(profile) {
38047
+ const channel = (0, import_nice_grpc.createChannel)(profile.serverUrl, void 0, {
38048
+ "grpc.max_receive_message_length": 100 * 1024 * 1024,
38049
+ "grpc.max_send_message_length": 100 * 1024 * 1024,
38050
+ "grpc-node.flow_control_window": 64 * 1024 * 1024
38051
+ });
38052
+ return (0, import_nice_grpc.createClientFactory)().use(authMiddleware(profile)).use(retryMiddleware).use(timeoutMiddleware).create(ModalClientDefinition, channel);
38053
+ }
38054
+ var clientProfile = defaultProfile;
38055
+ var client = createClient(clientProfile);
38056
+ function initializeClient(options) {
38057
+ const mergedProfile = {
38058
+ ...defaultProfile,
38059
+ tokenId: options.tokenId,
38060
+ tokenSecret: options.tokenSecret,
38061
+ environment: options.environment || defaultProfile.environment
38062
+ };
38063
+ clientProfile = mergedProfile;
38064
+ client = createClient(mergedProfile);
38065
+ }
38025
38066
 
38026
38067
  // src/image.ts
38027
38068
  var Image2 = class {
@@ -38659,8 +38700,20 @@ var App = class _App {
38659
38700
  });
38660
38701
  return new Sandbox2(createResp.sandboxId);
38661
38702
  }
38662
- async imageFromRegistry(tag) {
38663
- return await fromRegistryInternal(this.appId, tag);
38703
+ async imageFromRegistry(tag, secret) {
38704
+ let imageRegistryConfig;
38705
+ if (secret) {
38706
+ if (!(secret instanceof Secret)) {
38707
+ throw new TypeError(
38708
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38709
+ );
38710
+ }
38711
+ imageRegistryConfig = {
38712
+ registryAuthType: 4 /* REGISTRY_AUTH_TYPE_STATIC_CREDS */,
38713
+ secretId: secret.secretId
38714
+ };
38715
+ }
38716
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38664
38717
  }
38665
38718
  async imageFromAwsEcr(tag, secret) {
38666
38719
  if (!(secret instanceof Secret)) {
@@ -38674,6 +38727,18 @@ var App = class _App {
38674
38727
  };
38675
38728
  return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38676
38729
  }
38730
+ async imageFromGcpArtifactRegistry(tag, secret) {
38731
+ if (!(secret instanceof Secret)) {
38732
+ throw new TypeError(
38733
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38734
+ );
38735
+ }
38736
+ const imageRegistryConfig = {
38737
+ registryAuthType: 2 /* REGISTRY_AUTH_TYPE_GCP */,
38738
+ secretId: secret.secretId
38739
+ };
38740
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38741
+ }
38677
38742
  };
38678
38743
 
38679
38744
  // src/cls.ts
@@ -39064,7 +39129,22 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39064
39129
  return new _ControlPlaneInvocation(functionCallId);
39065
39130
  }
39066
39131
  async awaitOutput(timeout) {
39067
- return await pollFunctionOutput(this.functionCallId, timeout);
39132
+ return await pollFunctionOutput(
39133
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39134
+ timeout
39135
+ );
39136
+ }
39137
+ async #getOutput(timeoutMillis) {
39138
+ const response = await client.functionGetOutputs({
39139
+ functionCallId: this.functionCallId,
39140
+ maxValues: 1,
39141
+ timeout: timeoutMillis / 1e3,
39142
+ // Backend needs seconds
39143
+ lastEntryId: "0-0",
39144
+ clearOnSuccess: true,
39145
+ requestedAt: timeNowSeconds()
39146
+ });
39147
+ return response.outputs ? response.outputs[0] : void 0;
39068
39148
  }
39069
39149
  async retry(retryCount) {
39070
39150
  if (!this.input) {
@@ -39082,33 +39162,70 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39082
39162
  this.inputJwt = functionRetryResponse.inputJwts[0];
39083
39163
  }
39084
39164
  };
39165
+ var InputPlaneInvocation = class _InputPlaneInvocation {
39166
+ client;
39167
+ functionId;
39168
+ input;
39169
+ attemptToken;
39170
+ constructor(client2, functionId, input, attemptToken) {
39171
+ this.client = client2;
39172
+ this.functionId = functionId;
39173
+ this.input = input;
39174
+ this.attemptToken = attemptToken;
39175
+ }
39176
+ static async create(inputPlaneUrl, functionId, input) {
39177
+ const functionPutInputsItem = {
39178
+ idx: 0,
39179
+ input
39180
+ };
39181
+ const client2 = getOrCreateInputPlaneClient(inputPlaneUrl);
39182
+ const attemptStartResponse = await client2.attemptStart({
39183
+ functionId,
39184
+ input: functionPutInputsItem
39185
+ });
39186
+ return new _InputPlaneInvocation(
39187
+ client2,
39188
+ functionId,
39189
+ functionPutInputsItem,
39190
+ attemptStartResponse.attemptToken
39191
+ );
39192
+ }
39193
+ async awaitOutput(timeout) {
39194
+ return await pollFunctionOutput(
39195
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39196
+ timeout
39197
+ );
39198
+ }
39199
+ async #getOutput(timeoutMillis) {
39200
+ const response = await this.client.attemptAwait({
39201
+ attemptToken: this.attemptToken,
39202
+ requestedAt: timeNowSeconds(),
39203
+ timeoutSecs: timeoutMillis / 1e3
39204
+ });
39205
+ return response.output;
39206
+ }
39207
+ async retry(_retryCount) {
39208
+ const attemptRetryResponse = await this.client.attemptRetry({
39209
+ functionId: this.functionId,
39210
+ input: this.input,
39211
+ attemptToken: this.attemptToken
39212
+ });
39213
+ this.attemptToken = attemptRetryResponse.attemptToken;
39214
+ }
39215
+ };
39085
39216
  function timeNowSeconds() {
39086
39217
  return Date.now() / 1e3;
39087
39218
  }
39088
- async function pollFunctionOutput(functionCallId, timeout) {
39219
+ async function pollFunctionOutput(getOutput, timeout) {
39089
39220
  const startTime = Date.now();
39090
39221
  let pollTimeout = outputsTimeout;
39091
39222
  if (timeout !== void 0) {
39092
39223
  pollTimeout = Math.min(timeout, outputsTimeout);
39093
39224
  }
39094
39225
  while (true) {
39095
- let response;
39096
- try {
39097
- response = await client.functionGetOutputs({
39098
- functionCallId,
39099
- maxValues: 1,
39100
- timeout: pollTimeout / 1e3,
39101
- // Backend needs seconds
39102
- lastEntryId: "0-0",
39103
- clearOnSuccess: true,
39104
- requestedAt: timeNowSeconds()
39105
- });
39106
- } catch (err) {
39107
- throw new Error(`FunctionGetOutputs failed: ${err}`);
39108
- }
39109
- const outputs = response.outputs;
39110
- if (outputs.length > 0) {
39111
- return await processResult(outputs[0].result, outputs[0].dataFormat);
39226
+ const output = await getOutput(pollTimeout);
39227
+ if (output) {
39228
+ return await processResult(output.result, output.dataFormat);
39112
39229
  }
39113
39230
  if (timeout !== void 0) {
39114
39231
  const remainingTime = timeout - (Date.now() - startTime);
@@ -39202,10 +39319,12 @@ var maxSystemRetries = 8;
39202
39319
  var Function_ = class _Function_ {
39203
39320
  functionId;
39204
39321
  methodName;
39322
+ inputPlaneUrl;
39205
39323
  /** @ignore */
39206
- constructor(functionId, methodName) {
39324
+ constructor(functionId, methodName, inputPlaneUrl) {
39207
39325
  this.functionId = functionId;
39208
39326
  this.methodName = methodName;
39327
+ this.inputPlaneUrl = inputPlaneUrl;
39209
39328
  }
39210
39329
  static async lookup(appName, name, options = {}) {
39211
39330
  try {
@@ -39215,7 +39334,11 @@ var Function_ = class _Function_ {
39215
39334
  namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39216
39335
  environmentName: environmentName(options.environment)
39217
39336
  });
39218
- return new _Function_(resp.functionId);
39337
+ return new _Function_(
39338
+ resp.functionId,
39339
+ void 0,
39340
+ resp.handleMetadata?.inputPlaneUrl
39341
+ );
39219
39342
  } catch (err) {
39220
39343
  if (err instanceof import_nice_grpc4.ClientError && err.code === import_nice_grpc4.Status.NOT_FOUND)
39221
39344
  throw new NotFoundError(`Function '${appName}/${name}' not found`);
@@ -39225,11 +39348,7 @@ var Function_ = class _Function_ {
39225
39348
  // Execute a single input into a remote Function.
39226
39349
  async remote(args = [], kwargs = {}) {
39227
39350
  const input = await this.#createInput(args, kwargs);
39228
- const invocation = await ControlPlaneInvocation.create(
39229
- this.functionId,
39230
- input,
39231
- 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39232
- );
39351
+ const invocation = await this.#createRemoteInvocation(input);
39233
39352
  let retryCount = 0;
39234
39353
  while (true) {
39235
39354
  try {
@@ -39244,6 +39363,20 @@ var Function_ = class _Function_ {
39244
39363
  }
39245
39364
  }
39246
39365
  }
39366
+ async #createRemoteInvocation(input) {
39367
+ if (this.inputPlaneUrl) {
39368
+ return await InputPlaneInvocation.create(
39369
+ this.inputPlaneUrl,
39370
+ this.functionId,
39371
+ input
39372
+ );
39373
+ }
39374
+ return await ControlPlaneInvocation.create(
39375
+ this.functionId,
39376
+ input,
39377
+ 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39378
+ );
39379
+ }
39247
39380
  // Spawn a single input into a remote function.
39248
39381
  async spawn(args = [], kwargs = {}) {
39249
39382
  const input = await this.#createInput(args, kwargs);
@@ -39702,5 +39835,6 @@ var Queue = class _Queue {
39702
39835
  RemoteError,
39703
39836
  Sandbox,
39704
39837
  SandboxFile,
39705
- Secret
39838
+ Secret,
39839
+ initializeClient
39706
39840
  });
package/dist/index.d.cts CHANGED
@@ -235,10 +235,25 @@ declare class App {
235
235
  /** Lookup a deployed app by name, or create if it does not exist. */
236
236
  static lookup(name: string, options?: LookupOptions): Promise<App>;
237
237
  createSandbox(image: Image, options?: SandboxCreateOptions): Promise<Sandbox>;
238
- imageFromRegistry(tag: string): Promise<Image>;
238
+ imageFromRegistry(tag: string, secret?: Secret): Promise<Image>;
239
239
  imageFromAwsEcr(tag: string, secret: Secret): Promise<Image>;
240
+ imageFromGcpArtifactRegistry(tag: string, secret: Secret): Promise<Image>;
240
241
  }
241
242
 
243
+ /** Options for initializing a client at runtime. */
244
+ type ClientOptions = {
245
+ tokenId: string;
246
+ tokenSecret: string;
247
+ environment?: string;
248
+ };
249
+ /**
250
+ * Initialize the Modal client, passing in token authentication credentials.
251
+ *
252
+ * You should call this function at the start of your application if not
253
+ * configuring Modal with a `.modal.toml` file or environment variables.
254
+ */
255
+ declare function initializeClient(options: ClientOptions): void;
256
+
242
257
  /** Options for `FunctionCall.get()`. */
243
258
  type FunctionCallGetOptions = {
244
259
  timeout?: number;
@@ -269,8 +284,9 @@ declare class Function_ {
269
284
  #private;
270
285
  readonly functionId: string;
271
286
  readonly methodName: string | undefined;
287
+ readonly inputPlaneUrl: string | undefined;
272
288
  /** @ignore */
273
- constructor(functionId: string, methodName?: string);
289
+ constructor(functionId: string, methodName?: string, inputPlaneUrl?: string);
274
290
  static lookup(appName: string, name: string, options?: LookupOptions): Promise<Function_>;
275
291
  remote(args?: any[], kwargs?: Record<string, any>): Promise<any>;
276
292
  spawn(args?: any[], kwargs?: Record<string, any>): Promise<FunctionCall>;
@@ -421,4 +437,4 @@ declare class Queue {
421
437
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
422
438
  }
423
439
 
424
- export { App, 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 };
440
+ 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 };
package/dist/index.d.ts CHANGED
@@ -235,10 +235,25 @@ declare class App {
235
235
  /** Lookup a deployed app by name, or create if it does not exist. */
236
236
  static lookup(name: string, options?: LookupOptions): Promise<App>;
237
237
  createSandbox(image: Image, options?: SandboxCreateOptions): Promise<Sandbox>;
238
- imageFromRegistry(tag: string): Promise<Image>;
238
+ imageFromRegistry(tag: string, secret?: Secret): Promise<Image>;
239
239
  imageFromAwsEcr(tag: string, secret: Secret): Promise<Image>;
240
+ imageFromGcpArtifactRegistry(tag: string, secret: Secret): Promise<Image>;
240
241
  }
241
242
 
243
+ /** Options for initializing a client at runtime. */
244
+ type ClientOptions = {
245
+ tokenId: string;
246
+ tokenSecret: string;
247
+ environment?: string;
248
+ };
249
+ /**
250
+ * Initialize the Modal client, passing in token authentication credentials.
251
+ *
252
+ * You should call this function at the start of your application if not
253
+ * configuring Modal with a `.modal.toml` file or environment variables.
254
+ */
255
+ declare function initializeClient(options: ClientOptions): void;
256
+
242
257
  /** Options for `FunctionCall.get()`. */
243
258
  type FunctionCallGetOptions = {
244
259
  timeout?: number;
@@ -269,8 +284,9 @@ declare class Function_ {
269
284
  #private;
270
285
  readonly functionId: string;
271
286
  readonly methodName: string | undefined;
287
+ readonly inputPlaneUrl: string | undefined;
272
288
  /** @ignore */
273
- constructor(functionId: string, methodName?: string);
289
+ constructor(functionId: string, methodName?: string, inputPlaneUrl?: string);
274
290
  static lookup(appName: string, name: string, options?: LookupOptions): Promise<Function_>;
275
291
  remote(args?: any[], kwargs?: Record<string, any>): Promise<any>;
276
292
  spawn(args?: any[], kwargs?: Record<string, any>): Promise<FunctionCall>;
@@ -421,4 +437,4 @@ declare class Queue {
421
437
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
422
438
  }
423
439
 
424
- export { App, 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 };
440
+ 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 };
package/dist/index.js CHANGED
@@ -37811,12 +37811,11 @@ function readConfigFile() {
37811
37811
  if (err.code === "ENOENT") {
37812
37812
  return {};
37813
37813
  }
37814
- throw new Error(`Failed to read or parse .modal.toml: ${err.message}`);
37814
+ return {};
37815
37815
  }
37816
37816
  }
37817
37817
  var config = readConfigFile();
37818
37818
  function getProfile(profileName) {
37819
- profileName = profileName || process.env["MODAL_PROFILE"] || void 0;
37820
37819
  if (!profileName) {
37821
37820
  for (const [name, profileData2] of Object.entries(config)) {
37822
37821
  if (profileData2.active) {
@@ -37825,45 +37824,61 @@ function getProfile(profileName) {
37825
37824
  }
37826
37825
  }
37827
37826
  }
37828
- if (profileName && !Object.hasOwn(config, profileName)) {
37829
- throw new Error(
37830
- `Profile "${profileName}" not found in .modal.toml. Please set the MODAL_PROFILE environment variable or specify a valid profile.`
37831
- );
37832
- }
37833
- const profileData = profileName ? config[profileName] : {};
37834
- const profile2 = {
37827
+ const profileData = profileName && Object.hasOwn(config, profileName) ? config[profileName] : {};
37828
+ const profile = {
37835
37829
  serverUrl: process.env["MODAL_SERVER_URL"] || profileData.server_url || "https://api.modal.com:443",
37836
37830
  tokenId: process.env["MODAL_TOKEN_ID"] || profileData.token_id,
37837
37831
  tokenSecret: process.env["MODAL_TOKEN_SECRET"] || profileData.token_secret,
37838
37832
  environment: process.env["MODAL_ENVIRONMENT"] || profileData.environment,
37839
37833
  imageBuilderVersion: process.env["MODAL_IMAGE_BUILDER_VERSION"] || profileData.imageBuilderVersion
37840
37834
  };
37841
- if (!profile2.tokenId || !profile2.tokenSecret) {
37842
- throw new Error(
37843
- `Profile "${profileName}" is missing token_id or token_secret. Please set them in .modal.toml or as environment variables.`
37844
- );
37845
- }
37846
- return profile2;
37835
+ return profile;
37847
37836
  }
37848
- var profile = getProfile(process.env["MODAL_PROFILE"] || void 0);
37849
37837
  function environmentName(environment) {
37850
- return environment || profile.environment || "";
37838
+ return environment || clientProfile.environment || "";
37851
37839
  }
37852
37840
  function imageBuilderVersion(version) {
37853
- return version || profile.imageBuilderVersion || "2024.10";
37841
+ return version || clientProfile.imageBuilderVersion || "2024.10";
37854
37842
  }
37855
37843
 
37856
37844
  // src/client.ts
37857
- function authMiddleware(profile2) {
37845
+ var defaultProfile = getProfile(process.env["MODAL_PROFILE"]);
37846
+ var modalAuthToken;
37847
+ function authMiddleware(profile) {
37858
37848
  return async function* authMiddleware2(call, options) {
37849
+ if (!profile.tokenId || !profile.tokenSecret) {
37850
+ throw new Error(
37851
+ `Profile is missing token_id or token_secret. Please set them in .modal.toml, or as environment variables, or initializeClient().`
37852
+ );
37853
+ }
37854
+ const { tokenId, tokenSecret } = profile;
37859
37855
  options.metadata ??= new Metadata();
37860
37856
  options.metadata.set(
37861
37857
  "x-modal-client-type",
37862
37858
  String(7 /* CLIENT_TYPE_LIBMODAL */)
37863
37859
  );
37864
37860
  options.metadata.set("x-modal-client-version", "1.0.0");
37865
- options.metadata.set("x-modal-token-id", profile2.tokenId);
37866
- options.metadata.set("x-modal-token-secret", profile2.tokenSecret);
37861
+ options.metadata.set("x-modal-token-id", tokenId);
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
+ };
37867
37882
  return yield* call.next(call.request, options);
37868
37883
  };
37869
37884
  }
@@ -37969,12 +37984,37 @@ var retryMiddleware = async function* retryMiddleware2(call, options) {
37969
37984
  }
37970
37985
  }
37971
37986
  };
37972
- var channel = createChannel(profile.serverUrl, void 0, {
37973
- "grpc.max_receive_message_length": 100 * 1024 * 1024,
37974
- "grpc.max_send_message_length": 100 * 1024 * 1024,
37975
- "grpc-node.flow_control_window": 64 * 1024 * 1024
37976
- });
37977
- var client = createClientFactory().use(authMiddleware(profile)).use(retryMiddleware).use(timeoutMiddleware).create(ModalClientDefinition, channel);
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
+ };
37998
+ function createClient(profile) {
37999
+ const channel = createChannel(profile.serverUrl, void 0, {
38000
+ "grpc.max_receive_message_length": 100 * 1024 * 1024,
38001
+ "grpc.max_send_message_length": 100 * 1024 * 1024,
38002
+ "grpc-node.flow_control_window": 64 * 1024 * 1024
38003
+ });
38004
+ return createClientFactory().use(authMiddleware(profile)).use(retryMiddleware).use(timeoutMiddleware).create(ModalClientDefinition, channel);
38005
+ }
38006
+ var clientProfile = defaultProfile;
38007
+ var client = createClient(clientProfile);
38008
+ function initializeClient(options) {
38009
+ const mergedProfile = {
38010
+ ...defaultProfile,
38011
+ tokenId: options.tokenId,
38012
+ tokenSecret: options.tokenSecret,
38013
+ environment: options.environment || defaultProfile.environment
38014
+ };
38015
+ clientProfile = mergedProfile;
38016
+ client = createClient(mergedProfile);
38017
+ }
37978
38018
 
37979
38019
  // src/image.ts
37980
38020
  var Image2 = class {
@@ -38612,8 +38652,20 @@ var App = class _App {
38612
38652
  });
38613
38653
  return new Sandbox2(createResp.sandboxId);
38614
38654
  }
38615
- async imageFromRegistry(tag) {
38616
- return await fromRegistryInternal(this.appId, tag);
38655
+ async imageFromRegistry(tag, secret) {
38656
+ let imageRegistryConfig;
38657
+ if (secret) {
38658
+ if (!(secret instanceof Secret)) {
38659
+ throw new TypeError(
38660
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38661
+ );
38662
+ }
38663
+ imageRegistryConfig = {
38664
+ registryAuthType: 4 /* REGISTRY_AUTH_TYPE_STATIC_CREDS */,
38665
+ secretId: secret.secretId
38666
+ };
38667
+ }
38668
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38617
38669
  }
38618
38670
  async imageFromAwsEcr(tag, secret) {
38619
38671
  if (!(secret instanceof Secret)) {
@@ -38627,6 +38679,18 @@ var App = class _App {
38627
38679
  };
38628
38680
  return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38629
38681
  }
38682
+ async imageFromGcpArtifactRegistry(tag, secret) {
38683
+ if (!(secret instanceof Secret)) {
38684
+ throw new TypeError(
38685
+ "secret must be a reference to an existing Secret, e.g. `await Secret.fromName('my_secret')`"
38686
+ );
38687
+ }
38688
+ const imageRegistryConfig = {
38689
+ registryAuthType: 2 /* REGISTRY_AUTH_TYPE_GCP */,
38690
+ secretId: secret.secretId
38691
+ };
38692
+ return await fromRegistryInternal(this.appId, tag, imageRegistryConfig);
38693
+ }
38630
38694
  };
38631
38695
 
38632
38696
  // src/cls.ts
@@ -39017,7 +39081,22 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39017
39081
  return new _ControlPlaneInvocation(functionCallId);
39018
39082
  }
39019
39083
  async awaitOutput(timeout) {
39020
- return await pollFunctionOutput(this.functionCallId, timeout);
39084
+ return await pollFunctionOutput(
39085
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39086
+ timeout
39087
+ );
39088
+ }
39089
+ async #getOutput(timeoutMillis) {
39090
+ const response = await client.functionGetOutputs({
39091
+ functionCallId: this.functionCallId,
39092
+ maxValues: 1,
39093
+ timeout: timeoutMillis / 1e3,
39094
+ // Backend needs seconds
39095
+ lastEntryId: "0-0",
39096
+ clearOnSuccess: true,
39097
+ requestedAt: timeNowSeconds()
39098
+ });
39099
+ return response.outputs ? response.outputs[0] : void 0;
39021
39100
  }
39022
39101
  async retry(retryCount) {
39023
39102
  if (!this.input) {
@@ -39035,33 +39114,70 @@ var ControlPlaneInvocation = class _ControlPlaneInvocation {
39035
39114
  this.inputJwt = functionRetryResponse.inputJwts[0];
39036
39115
  }
39037
39116
  };
39117
+ var InputPlaneInvocation = class _InputPlaneInvocation {
39118
+ client;
39119
+ functionId;
39120
+ input;
39121
+ attemptToken;
39122
+ constructor(client2, functionId, input, attemptToken) {
39123
+ this.client = client2;
39124
+ this.functionId = functionId;
39125
+ this.input = input;
39126
+ this.attemptToken = attemptToken;
39127
+ }
39128
+ static async create(inputPlaneUrl, functionId, input) {
39129
+ const functionPutInputsItem = {
39130
+ idx: 0,
39131
+ input
39132
+ };
39133
+ const client2 = getOrCreateInputPlaneClient(inputPlaneUrl);
39134
+ const attemptStartResponse = await client2.attemptStart({
39135
+ functionId,
39136
+ input: functionPutInputsItem
39137
+ });
39138
+ return new _InputPlaneInvocation(
39139
+ client2,
39140
+ functionId,
39141
+ functionPutInputsItem,
39142
+ attemptStartResponse.attemptToken
39143
+ );
39144
+ }
39145
+ async awaitOutput(timeout) {
39146
+ return await pollFunctionOutput(
39147
+ (timeoutMillis) => this.#getOutput(timeoutMillis),
39148
+ timeout
39149
+ );
39150
+ }
39151
+ async #getOutput(timeoutMillis) {
39152
+ const response = await this.client.attemptAwait({
39153
+ attemptToken: this.attemptToken,
39154
+ requestedAt: timeNowSeconds(),
39155
+ timeoutSecs: timeoutMillis / 1e3
39156
+ });
39157
+ return response.output;
39158
+ }
39159
+ async retry(_retryCount) {
39160
+ const attemptRetryResponse = await this.client.attemptRetry({
39161
+ functionId: this.functionId,
39162
+ input: this.input,
39163
+ attemptToken: this.attemptToken
39164
+ });
39165
+ this.attemptToken = attemptRetryResponse.attemptToken;
39166
+ }
39167
+ };
39038
39168
  function timeNowSeconds() {
39039
39169
  return Date.now() / 1e3;
39040
39170
  }
39041
- async function pollFunctionOutput(functionCallId, timeout) {
39171
+ async function pollFunctionOutput(getOutput, timeout) {
39042
39172
  const startTime = Date.now();
39043
39173
  let pollTimeout = outputsTimeout;
39044
39174
  if (timeout !== void 0) {
39045
39175
  pollTimeout = Math.min(timeout, outputsTimeout);
39046
39176
  }
39047
39177
  while (true) {
39048
- let response;
39049
- try {
39050
- response = await client.functionGetOutputs({
39051
- functionCallId,
39052
- maxValues: 1,
39053
- timeout: pollTimeout / 1e3,
39054
- // Backend needs seconds
39055
- lastEntryId: "0-0",
39056
- clearOnSuccess: true,
39057
- requestedAt: timeNowSeconds()
39058
- });
39059
- } catch (err) {
39060
- throw new Error(`FunctionGetOutputs failed: ${err}`);
39061
- }
39062
- const outputs = response.outputs;
39063
- if (outputs.length > 0) {
39064
- return await processResult(outputs[0].result, outputs[0].dataFormat);
39178
+ const output = await getOutput(pollTimeout);
39179
+ if (output) {
39180
+ return await processResult(output.result, output.dataFormat);
39065
39181
  }
39066
39182
  if (timeout !== void 0) {
39067
39183
  const remainingTime = timeout - (Date.now() - startTime);
@@ -39155,10 +39271,12 @@ var maxSystemRetries = 8;
39155
39271
  var Function_ = class _Function_ {
39156
39272
  functionId;
39157
39273
  methodName;
39274
+ inputPlaneUrl;
39158
39275
  /** @ignore */
39159
- constructor(functionId, methodName) {
39276
+ constructor(functionId, methodName, inputPlaneUrl) {
39160
39277
  this.functionId = functionId;
39161
39278
  this.methodName = methodName;
39279
+ this.inputPlaneUrl = inputPlaneUrl;
39162
39280
  }
39163
39281
  static async lookup(appName, name, options = {}) {
39164
39282
  try {
@@ -39168,7 +39286,11 @@ var Function_ = class _Function_ {
39168
39286
  namespace: 1 /* DEPLOYMENT_NAMESPACE_WORKSPACE */,
39169
39287
  environmentName: environmentName(options.environment)
39170
39288
  });
39171
- return new _Function_(resp.functionId);
39289
+ return new _Function_(
39290
+ resp.functionId,
39291
+ void 0,
39292
+ resp.handleMetadata?.inputPlaneUrl
39293
+ );
39172
39294
  } catch (err) {
39173
39295
  if (err instanceof ClientError4 && err.code === Status4.NOT_FOUND)
39174
39296
  throw new NotFoundError(`Function '${appName}/${name}' not found`);
@@ -39178,11 +39300,7 @@ var Function_ = class _Function_ {
39178
39300
  // Execute a single input into a remote Function.
39179
39301
  async remote(args = [], kwargs = {}) {
39180
39302
  const input = await this.#createInput(args, kwargs);
39181
- const invocation = await ControlPlaneInvocation.create(
39182
- this.functionId,
39183
- input,
39184
- 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39185
- );
39303
+ const invocation = await this.#createRemoteInvocation(input);
39186
39304
  let retryCount = 0;
39187
39305
  while (true) {
39188
39306
  try {
@@ -39197,6 +39315,20 @@ var Function_ = class _Function_ {
39197
39315
  }
39198
39316
  }
39199
39317
  }
39318
+ async #createRemoteInvocation(input) {
39319
+ if (this.inputPlaneUrl) {
39320
+ return await InputPlaneInvocation.create(
39321
+ this.inputPlaneUrl,
39322
+ this.functionId,
39323
+ input
39324
+ );
39325
+ }
39326
+ return await ControlPlaneInvocation.create(
39327
+ this.functionId,
39328
+ input,
39329
+ 4 /* FUNCTION_CALL_INVOCATION_TYPE_SYNC */
39330
+ );
39331
+ }
39200
39332
  // Spawn a single input into a remote function.
39201
39333
  async spawn(args = [], kwargs = {}) {
39202
39334
  const input = await this.#createInput(args, kwargs);
@@ -39654,5 +39786,6 @@ export {
39654
39786
  RemoteError,
39655
39787
  Sandbox2 as Sandbox,
39656
39788
  SandboxFile,
39657
- Secret
39789
+ Secret,
39790
+ initializeClient
39658
39791
  };
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "modal",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "Modal client library for JavaScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://modal.com/docs",
7
- "repository": "github:modal-labs/libmodal",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/modal-labs/libmodal.git"
10
+ },
8
11
  "bugs": "https://github.com/modal-labs/libmodal/issues",
9
12
  "type": "module",
10
13
  "sideEffects": false,
@@ -30,9 +33,9 @@
30
33
  "lint": "eslint",
31
34
  "prepare": "scripts/gen-proto.sh",
32
35
  "test": "vitest",
33
- "preversion": "(git update-index --really-refresh && git diff-index --quiet HEAD) || (echo 'You must commit all changes before running npm version' && exit 1)",
34
- "version": "npm run build && git add package.json package-lock.json && git commit -m \"modal-js/v$npm_package_version\" && git tag modal-js/v$npm_package_version",
35
- "postversion": "git push && git push --tags && npm publish"
36
+ "version": "npm run check && git add -A && git commit -m \"modal-js/v$npm_package_version\"",
37
+ "prepublishOnly": "npm run build && git push",
38
+ "postpublish": "git tag modal-js/v$npm_package_version && git push --tags"
36
39
  },
37
40
  "dependencies": {
38
41
  "long": "^5.3.1",