Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
292c48b
initial cloud run no build deploy implementation mvp
brittanycho Dec 19, 2025
57855da
manual testing files - will remove later
brittanycho Dec 19, 2025
b4f9633
fixes formatting issues
brittanycho Dec 20, 2025
f24830e
Fix runv2.spec.ts concurrency expectation
brittanycho Jan 6, 2026
6670e13
removing manual test files
brittanycho Jan 6, 2026
dc1978e
Merge branch 'master' into zip-deploy-4
brittanycho Jan 7, 2026
487ad04
Fix lint errors
brittanycho Jan 7, 2026
15a7b26
Update json schema for dart3
brittanycho Jan 7, 2026
23c6d27
Fix lint errors and remove verify script
brittanycho Jan 7, 2026
366fe34
Fix TS errors in fabricator.ts for vscode build
brittanycho Jan 7, 2026
e66d9de
Fixes minor formatting issues
brittanycho Jan 7, 2026
1975dec
Fixed linting
brittanycho Jan 7, 2026
6b53bf9
Merge branch 'master' into zip-deploy-4
brittanycho Jan 9, 2026
f385cc3
addresses feedback
brittanycho Jan 12, 2026
e98b149
Merge branch 'master' into zip-deploy-4
brittanycho Jan 12, 2026
55ee19f
remove runtime config for v2 and run
brittanycho Jan 13, 2026
92124c2
Merge branch 'master' into zip-deploy-4
brittanycho Jan 14, 2026
2e3ceec
adds relevant comments
brittanycho Jan 15, 2026
f767878
Merge branch 'master' into zip-deploy-4
brittanycho Jan 21, 2026
cc924c4
Merge branch 'master' into zip-deploy-4
brittanycho Jan 24, 2026
7a32e38
add additional fields and other minor fixes
brittanycho Jan 27, 2026
0d985f2
Merge branch 'master' into zip-deploy-4
brittanycho Jan 27, 2026
9a63a38
corrects test
brittanycho Jan 27, 2026
13fb338
fixes error
brittanycho Jan 27, 2026
4714005
fix prettier
brittanycho Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/deploy/functions/backend.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe("Backend", () => {
},
},
],
containerConcurrency: 80,
maxInstanceRequestConcurrency: 80,
},
generation: 1,
createTime: "2023-01-01T00:00:00Z",
Expand Down
5 changes: 5 additions & 0 deletions src/deploy/functions/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
return allMemoryOptions.includes(mem as MemoryOptions);
}

export function isValidEgressSetting(egress: unknown): egress is VpcEgressSettings {

Check warning on line 198 in src/deploy/functions/backend.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc comment
return egress === "PRIVATE_RANGES_ONLY" || egress === "ALL_TRAFFIC";
}

Expand Down Expand Up @@ -407,6 +407,11 @@

// State of the endpoint.
state?: EndpointState;

// Fields for "run" platform (no-build)
baseImageUri?: string;
command?: string[];
args?: string[];
};

export interface RequiredAPI {
Expand Down Expand Up @@ -572,8 +577,8 @@
existingBackend.endpoints[endpoint.region] || {};
existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
}
} catch (err: any) {

Check warning on line 580 in src/deploy/functions/backend.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
logger.debug(err.message);

Check warning on line 581 in src/deploy/functions/backend.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .message on an `any` value

Check warning on line 581 in src/deploy/functions/backend.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `Error`
unreachableRegions.run = ["unknown"];
}
} else {
Expand Down
14 changes: 11 additions & 3 deletions src/deploy/functions/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@
const allMemoryOptions: MemoryOption[] = [128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768];

// Run is an automatic migration from gcfv2 and is not used on the wire.
export type FunctionsPlatform = Exclude<backend.FunctionsPlatform, "run">;
export const AllFunctionsPlatforms: FunctionsPlatform[] = ["gcfv1", "gcfv2"];
export type FunctionsPlatform = backend.FunctionsPlatform;
export const AllFunctionsPlatforms: FunctionsPlatform[] = ["gcfv1", "gcfv2", "run"];
export type VpcEgressSetting = backend.VpcEgressSettings;
export const AllVpcEgressSettings: VpcEgressSetting[] = ["PRIVATE_RANGES_ONLY", "ALL_TRAFFIC"];
export type IngressSetting = backend.IngressSettings;
Expand All @@ -242,7 +242,7 @@
omit?: Field<boolean>;

// Defaults to "gcfv2". "Run" will be an additional option defined later
platform?: "gcfv1" | "gcfv2";
platform?: "gcfv1" | "gcfv2" | "run";

// Necessary for the GCF API to determine what code to load with the Functions Framework.
// Will become optional once "run" is supported as a platform
Expand Down Expand Up @@ -287,6 +287,11 @@
environmentVariables?: Record<string, string | Expression<string>> | null;
secretEnvironmentVariables?: SecretEnvVar[] | null;
labels?: Record<string, string | Expression<string>> | null;

// Fields for "run" platform (no-build)
baseImageUri?: string;
command?: string[];
args?: string[];
};

type SecretParam = ReturnType<typeof defineSecret>;
Expand Down Expand Up @@ -474,7 +479,7 @@
// List param, we try resolving a String param instead.
try {
regions = params.resolveList(bdEndpoint.region, paramValues);
} catch (err: any) {

Check warning on line 482 in src/deploy/functions/build.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
if (err instanceof ExprParseError) {
regions = [params.resolveString(bdEndpoint.region, paramValues)];
} else {
Expand Down Expand Up @@ -503,6 +508,9 @@
"environmentVariables",
"labels",
"secretEnvironmentVariables",
"baseImageUri",
"command",
"args",
);
r.resolveStrings(bkEndpoint, bdEndpoint, "serviceAccount");

Expand Down
22 changes: 11 additions & 11 deletions src/deploy/functions/checkIam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
["iam.serviceAccounts.actAs"],
);
passed = iamResult.passed;
} catch (err: any) {

Check warning on line 44 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
logger.debug("[functions] service account IAM check errored, deploy may fail:", err);
// we want to fail this check open and not rethrow since it's informational only
return;
Expand All @@ -52,16 +52,15 @@
`Missing permissions required for functions deploy. You must have permission ${bold(
"iam.serviceAccounts.ActAs",
)} on service account ${bold(saEmail)}.\n\n` +
`To address this error, ask a project Owner to assign your account the "Service Account User" role from this URL:\n\n` +
`https://console.cloud.google.com/iam-admin/iam?project=${projectId}`,
`To address this error, ask a project Owner to assign your account the "Service Account User" role from this URL:\n\n` +

Check failure on line 55 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 55 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 55 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
`https://console.cloud.google.com/iam-admin/iam?project=${projectId}`,

Check failure on line 56 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 56 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 56 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
);
}
}

/**
* Checks a functions deployment for HTTP function creation, and tests IAM
* permissions accordingly.
*
* @param context The deploy context.
* @param options The command-wide options object.
* @param payload The deploy payload.
Expand All @@ -74,11 +73,12 @@
if (!payload.functions) {
return;
}
const filters = context.filters || getEndpointFilters(options, context.config!);

Check warning on line 76 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Forbidden non-null assertion
const wantBackends = Object.values(payload.functions).map(({ wantBackend }) => wantBackend);
const httpEndpoints = [...flattenArray(wantBackends.map((b) => backend.allEndpoints(b)))]
.filter(backend.isHttpsTriggered || backend.isDataConnectGraphqlTriggered)
.filter((f) => endpointMatchesAnyFilter(f, filters));
.filter((f) => backend.isHttpsTriggered(f) || backend.isDataConnectGraphqlTriggered(f))
.filter((f) => endpointMatchesAnyFilter(f, filters))
.filter((f) => f.platform !== "run");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: why exclude run?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iiuc GCF and Run have different permissions (cloudfunctions.functions.setIamPolicy vs. run.services.setIamPolicy) and the specific check where it is excluded here is for GCF permission

GCF: https://docs.cloud.google.com/functions/docs/reference/iam/permissions#functions
Run: https://docs.cloud.google.com/run/docs/reference/iam/permissions#services

For current no build mvp implementation, I think the current approach is implemented so that we try to set the policy in setInvoker (within fabricator.ts) and if it fails iiuc there should be an IAM error from Cloud Run


const existing = await backend.existingBackend(context);
const newHttpsEndpoints = httpEndpoints.filter(backend.missingEndpoint(existing));
Expand All @@ -97,7 +97,7 @@
try {
const iamResult = await iam.testIamPermissions(context.projectId, [PERMISSION]);
passed = iamResult.passed;
} catch (e: any) {

Check warning on line 100 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
logger.debug(
"[functions] failed http create setIamPolicy permission check. deploy may fail:",
e,
Expand All @@ -117,8 +117,8 @@
)} to deploy new HTTPS functions. The permission ${bold(
PERMISSION,
)} is required to deploy the following functions:\n\n- ` +
newHttpsEndpoints.map((func) => func.id).join("\n- ") +
`\n\nTo address this error, please ask a project Owner to assign your account the "Cloud Functions Admin" role at the following URL:\n\nhttps://console.cloud.google.com/iam-admin/iam?project=${context.projectId}`,
newHttpsEndpoints.map((func) => func.id).join("\n- ") +

Check failure on line 120 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 120 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 120 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
`\n\nTo address this error, please ask a project Owner to assign your account the "Cloud Functions Admin" role at the following URL:\n\nhttps://console.cloud.google.com/iam-admin/iam?project=${context.projectId}`,

Check failure on line 121 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 121 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 121 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
);
}
logger.debug("[functions] found setIamPolicy permission, proceeding with deploy");
Expand All @@ -130,7 +130,7 @@
}

/** Callback reducer function */
function reduceEventsToServices(services: Array<Service>, endpoint: backend.Endpoint) {

Check warning on line 133 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
const service = serviceForEndpoint(endpoint);
if (service.requiredProjectBindings && !services.find((s) => s.name === service.name)) {
services.push(service);
Expand All @@ -149,7 +149,7 @@
* Finds the required project level IAM bindings for the Pub/Sub service agent.
* If the user enabled Pub/Sub on or before April 8, 2021, then we must enable the token creator role.
* @param projectNumber project number
* @param existingPolicy the project level IAM policy

Check warning on line 152 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

@param "existingPolicy" does not match an existing function parameter
*/
export function obtainPubSubServiceAgentBindings(projectNumber: string): iam.Binding[] {
const serviceAccountTokenCreatorBinding: iam.Binding = {
Expand Down Expand Up @@ -290,8 +290,8 @@
utils.logLabeledBullet(
"functions",
"Could not verify the necessary IAM configuration for the following newly-integrated services: " +
`${newServicesOrEndpoints.join(", ")}` +
". Deployment may fail.",
`${newServicesOrEndpoints.join(", ")}` +

Check failure on line 293 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 293 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 293 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
". Deployment may fail.",

Check failure on line 294 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 294 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 294 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
"warn",
);
return;
Expand All @@ -316,8 +316,8 @@
iam.printManualIamConfig(requiredBindings, projectId, "functions");
throw new FirebaseError(
"We failed to modify the IAM policy for the project. The functions " +
"deployment requires specific roles to be granted to service agents," +
" otherwise the deployment will fail.",
"deployment requires specific roles to be granted to service agents," +

Check failure on line 319 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 319 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 319 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
" otherwise the deployment will fail.",

Check failure on line 320 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 320 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 320 in src/deploy/functions/checkIam.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
{ original: err },
);
}
Expand Down
13 changes: 11 additions & 2 deletions src/deploy/functions/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as experiments from "../../experiments";
import { findEndpoint } from "./backend";
import { deploy as extDeploy } from "../extensions";
import { getProjectNumber } from "../../getProjectNumber";
import * as path from "path";

setGracefulCleanup();

Expand Down Expand Up @@ -51,10 +52,16 @@ async function uploadSourceV1(
}

// Trampoline to allow tests to mock out createStream.
/**
*
*/
export function createReadStream(filePath: string): NodeJS.ReadableStream {
return fs.createReadStream(filePath);
}

/**
*
*/
export async function uploadSourceV2(
projectId: string,
projectNumber: string,
Expand All @@ -80,7 +87,9 @@ export async function uploadSourceV2(
};

// Legacy behavior: use the GCF API
if (!experiments.isEnabled("runfunctions")) {
// We use BYO bucket if the "runfunctions" experiment is enabled OR if we have any platform: run endpoints.
// Otherwise, we use the GCF API.
if (!experiments.isEnabled("runfunctions") && !v2Endpoints.some((e) => e.platform === "run")) {
if (process.env.GOOGLE_CLOUD_QUOTA_PROJECT) {
logLabeledWarning(
"functions",
Expand Down Expand Up @@ -116,7 +125,7 @@ export async function uploadSourceV2(
},
},
});
const objectPath = `${source.functionsSourceV2Hash}.zip`;
const objectPath = `${source.functionsSourceV2Hash}${path.extname(source.functionsSourceV2!)}`;
await gcs.upload(
uploadOpts,
`${bucketName}/${objectPath}`,
Expand Down
18 changes: 12 additions & 6 deletions src/deploy/functions/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
// ===Phase 1. Load codebases from source with optional runtime config.
let runtimeConfig: Record<string, unknown> = { firebase: firebaseConfig };

const targetedCodebaseConfigs = context.config!.filter((cfg) => codebases.includes(cfg.codebase));
const targetedCodebaseConfigs = context.config.filter((cfg) => codebases.includes(cfg.codebase));

// Load runtime config if API is enabled and at least one targeted codebase uses it
if (checkAPIsEnabled[1] && targetedCodebaseConfigs.some(shouldUseRuntimeConfig)) {
Expand Down Expand Up @@ -223,7 +223,7 @@
);
}

if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2" || e.platform === "run")) {
const schPathSet = new Set<string>();
for (const e of backend.allEndpoints(wantBackend)) {
if (
Expand All @@ -233,11 +233,17 @@
schPathSet.add(e.dataConnectGraphqlTrigger.schemaFilePath);
}
}
const configForUpload = shouldUseRuntimeConfig(localCfg) ? runtimeConfig : undefined;
const exportType = backend.someEndpoint(wantBackend, (e) => e.platform === "run")
? "tar.gz"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit* I think zip deploy may add support for .zip soonish?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, do you think above would be best to add as a todo once they add the support or preemptively add it now?

: "zip";
const packagedSource = await prepareFunctionsUpload(
options.config.projectDir,
sourceDir,
localCfg,
[...schPathSet],
configForUpload,
{ exportType },
);
source.functionsSourceV2 = packagedSource?.pathToSource;
source.functionsSourceV2Hash = packagedSource?.hash;
Expand Down Expand Up @@ -490,10 +496,10 @@
if (firebaseJsonRuntime && !supported.isRuntime(firebaseJsonRuntime as string)) {
throw new FirebaseError(
`Functions codebase ${codebase} has invalid runtime ` +
`${firebaseJsonRuntime} specified in firebase.json. Valid values are: \n` +
Object.keys(supported.RUNTIMES)
.map((s) => `- ${s}`)
.join("\n"),
`${firebaseJsonRuntime} specified in firebase.json. Valid values are: \n` +

Check failure on line 499 in src/deploy/functions/prepare.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 499 in src/deploy/functions/prepare.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 499 in src/deploy/functions/prepare.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
Object.keys(supported.RUNTIMES)

Check failure on line 500 in src/deploy/functions/prepare.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`

Check failure on line 500 in src/deploy/functions/prepare.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `··`

Check failure on line 500 in src/deploy/functions/prepare.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Insert `··`
.map((s) => `- ${s}`)
.join("\n"),
);
}
const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
Expand Down
35 changes: 24 additions & 11 deletions src/deploy/functions/prepareFunctionsUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ interface PackagedSourceInfo {
type SortedConfig = string | { key: string; value: SortedConfig }[];

// TODO(inlined): move to a file that's not about uploading source code
/**
*
*/
export async function getFunctionsConfig(projectId: string): Promise<Record<string, unknown>> {
try {
return await functionsConfig.materializeAll(projectId);
Expand All @@ -37,9 +40,9 @@ export async function getFunctionsConfig(projectId: string): Promise<Record<stri
if (errorCode === 500 || errorCode === 503) {
throw new FirebaseError(
"Cloud Runtime Config is currently experiencing issues, " +
"which is preventing your functions from being deployed. " +
"Please wait a few minutes and then try to deploy your functions again." +
"\nRun `firebase deploy --except functions` if you want to continue deploying the rest of your project.",
"which is preventing your functions from being deployed. " +
"Please wait a few minutes and then try to deploy your functions again." +
"\nRun `firebase deploy --except functions` if you want to continue deploying the rest of your project.",
);
}
}
Expand All @@ -61,13 +64,16 @@ async function packageSource(
config: projectConfig.ValidatedSingle,
additionalSources: string[],
runtimeConfig: any,
options?: { exportType: "zip" | "tar.gz" },
): Promise<PackagedSourceInfo | undefined> {
const tmpFile = tmp.fileSync({ prefix: "firebase-functions-", postfix: ".zip" }).name;
const exportType = options?.exportType || "zip";
const postfix = exportType === "tar.gz" ? ".tar.gz" : ".zip";
const tmpFile = tmp.fileSync({ prefix: "firebase-functions-", postfix }).name;
const fileStream = fs.createWriteStream(tmpFile, {
flags: "w",
encoding: "binary",
});
const archive = archiver("zip");
const archive = exportType === "tar.gz" ? archiver("tar", { gzip: true }) : archiver("zip");
const hashes: string[] = [];

// We must ignore firebase-debug.log or weird things happen if
Expand Down Expand Up @@ -138,26 +144,33 @@ async function packageSource(

utils.logBullet(
clc.cyan(clc.bold("functions:")) +
" packaged " +
clc.bold(sourceDir) +
" (" +
filesize(archive.pointer()) +
") for uploading",
" packaged " +
clc.bold(sourceDir) +
" (" +
filesize(archive.pointer()) +
") for uploading",
);
const hash = hashes.join(".");
return { pathToSource: tmpFile, hash };
}

/**
*
*/
export async function prepareFunctionsUpload(
projectDir: string,
sourceDir: string,
config: projectConfig.ValidatedSingle,
additionalSources: string[],
runtimeConfig?: backend.RuntimeConfigValues,
options?: { exportType: "zip" | "tar.gz" },
): Promise<PackagedSourceInfo | undefined> {
return packageSource(projectDir, sourceDir, config, additionalSources, runtimeConfig);
return packageSource(projectDir, sourceDir, config, additionalSources, runtimeConfig, options);
}

/**
*
*/
export function convertToSortedKeyValueArray(config: any): SortedConfig {
if (typeof config !== "object" || config === null) return config;

Expand Down
Loading
Loading