diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml
index a7f44e8f..6299155b 100644
--- a/.github/workflows/beta-release.yml
+++ b/.github/workflows/beta-release.yml
@@ -12,5 +12,7 @@ jobs:
build-and-deploy:
uses: ./.github/workflows/common-release.yml
with:
- tag: beta
+ product: skyflow-node
+ package-dir: skyvault
+ release-type: beta
secrets: inherit
diff --git a/.github/workflows/common-ci.yml b/.github/workflows/common-ci.yml
index 9b79c71b..6eb5117d 100644
--- a/.github/workflows/common-ci.yml
+++ b/.github/workflows/common-ci.yml
@@ -22,6 +22,11 @@ jobs:
- name: Run ESLint
run: npm run eslint
+ # One command builds BOTH SDKs (common -> skyvault/flowvault via project references).
+ - name: Build all packages
+ run: npm run build
+
+ # Fans out `npm test` across every workspace (common, skyvault, flowvault).
- name: Run Tests
env:
SA_WITHOUT_CONTEXT: ${{ secrets.SA_WITHOUT_CONTEXT }}
@@ -33,6 +38,6 @@ jobs:
uses: codecov/codecov-action@v2.1.0
with:
token: ${{ secrets.CODECOV_REPO_UPLOAD_TOKEN }}
- files: coverage/lcov.info
+ files: skyvault/coverage/lcov.info,flowvault/coverage/lcov.info
name: codecov-skyflow-node
verbose: true
diff --git a/.github/workflows/common-release.yml b/.github/workflows/common-release.yml
index 2fad6eb9..c220c487 100644
--- a/.github/workflows/common-release.yml
+++ b/.github/workflows/common-release.yml
@@ -1,16 +1,30 @@
name: Common Release Workflow
+# Reusable release pipeline, parameterized per product.
+# product npm package name (skyflow-node | skyflow-flowvault-node)
+# package-dir workspace dir (skyvault | flowvault)
+# release-type public | beta | internal
+# version semver to release (caller strips any tag prefix, e.g. flowvault/1.0.0 -> 1.0.0)
+
on:
workflow_call:
inputs:
- tag:
+ product:
+ required: true
+ type: string
+ package-dir:
+ required: true
+ type: string
+ release-type:
+ required: true
+ type: string
+ version:
required: false
type: string
jobs:
build-sdk:
runs-on: ubuntu-latest
-
steps:
- uses: actions/checkout@v4
with:
@@ -24,68 +38,68 @@ jobs:
- name: Install Packages
run: npm install --ignore-scripts
- - name: Build
- run: npm run build
+ - name: Resolve Version
+ id: version
+ run: |
+ if [ -n "${{ inputs.version }}" ]; then
+ echo "value=${{ inputs.version }}" >> $GITHUB_OUTPUT
+ else
+ # Fallback: strip an optional "
/" prefix from the pushed tag.
+ echo "value=${GITHUB_REF_NAME##*/}" >> $GITHUB_OUTPUT
+ fi
- - name: Get Previous Tag
- id: previoustag
- uses: WyriHaximus/github-action-get-previous-tag@v1
- with:
- fallback: 1.0.0
+ - name: Bump Version (${{ inputs.product }})
+ run: |
+ chmod +x ./scripts/bump_version.sh
+ if [[ "${{ inputs.release-type }}" == "internal" ]]; then
+ ./scripts/bump_version.sh "${{ inputs.product }}" "${{ steps.version.outputs.value }}" "$(git rev-parse --short "$GITHUB_SHA")"
+ else
+ ./scripts/bump_version.sh "${{ inputs.product }}" "${{ steps.version.outputs.value }}"
+ fi
+
+ # Build everything (common -> layers) then bundle the published package
+ # (inlines skyflow-common into a self-contained dist/).
+ - name: Build
+ run: |
+ npm run build
+ npm run build:dist --workspace ${{ inputs.package-dir }}
- name: Resolve Branch Name
id: resolve-branch
- if: ${{ inputs.tag == 'beta' || inputs.tag == 'public' }}
+ if: ${{ inputs.release-type == 'beta' || inputs.release-type == 'public' }}
run: |
TAG_COMMIT=$(git rev-list -n 1 ${{ github.ref_name }})
-
BRANCH_NAME=$(git branch -r --contains $TAG_COMMIT | grep -o 'origin/.*' | sed 's|origin/||' | head -n 1)
-
if [ -z "$BRANCH_NAME" ]; then
echo "Error: Could not resolve branch for the tag."
exit 1
fi
-
- echo "Resolved Branch Name: $BRANCH_NAME"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_ENV
- - name: Bump Version
- run: |
- chmod +x ./scripts/bump_version.sh
- if ${{ inputs.tag == 'internal' }}; then
- ./scripts/bump_version.sh "${{ steps.previoustag.outputs.tag }}" "$(git rev-parse --short "$GITHUB_SHA")"
- else
- ./scripts/bump_version.sh "${{ steps.previoustag.outputs.tag }}"
- fi
-
- name: Commit Changes
run: |
git config user.name ${{ github.actor }}
git config user.email ${{ github.actor }}@users.noreply.github.com
- if [[ "${{ inputs.tag }}" == "beta" || "${{ inputs.tag }}" == "public" ]]; then
+ if [[ "${{ inputs.release-type }}" == "beta" || "${{ inputs.release-type }}" == "public" ]]; then
git checkout ${{ env.branch_name }}
fi
- git add package.json
- if [[ "${{ inputs.tag }}" == "beta" || "${{ inputs.tag }}" == "public" ]]; then
- git commit -m "[AUTOMATED] Release - ${{ steps.previoustag.outputs.tag }}"
+ git add ${{ inputs.package-dir }}/package.json ${{ inputs.package-dir }}/src/version.ts
+ if [[ "${{ inputs.release-type }}" == "beta" || "${{ inputs.release-type }}" == "public" ]]; then
+ git commit -m "[AUTOMATED] Release ${{ inputs.product }} - ${{ steps.version.outputs.value }}" || echo "nothing to commit"
git push origin ${{ env.branch_name }}
- elif [[ "${{ inputs.tag }}" == "internal" ]]; then
- git commit -m "[AUTOMATED] Private Release ${{ steps.previoustag.outputs.tag }}-dev.$(git rev-parse --short $GITHUB_SHA)"
+ elif [[ "${{ inputs.release-type }}" == "internal" ]]; then
+ git commit -m "[AUTOMATED] Private Release ${{ inputs.product }} ${{ steps.version.outputs.value }}-dev.$(git rev-parse --short $GITHUB_SHA)" || echo "nothing to commit"
git push origin ${{ github.ref_name }} -f
fi
- name: Publish
+ working-directory: ${{ inputs.package-dir }}
run: |
- if [[ "${{ inputs.tag }}" == "beta" ]]; then
+ if [[ "${{ inputs.release-type }}" == "beta" ]]; then
npm publish --tag beta
- elif [[ "${{ inputs.tag }}" == "public" ]]; then
- MAJOR_VERSION=$(node -p "require('./package.json').version.split('.')[0]")
- if [[ "$MAJOR_VERSION" == "1" ]]; then
- npm publish --tag v1
- else
- npm publish
- fi
- elif [[ "${{ inputs.tag }}" == "internal" ]]; then
+ elif [[ "${{ inputs.release-type }}" == "public" ]]; then
+ npm publish
+ elif [[ "${{ inputs.release-type }}" == "internal" ]]; then
JFROG_AUTH=$(echo -n "$JFROG_USERNAME:$JFROG_PASSWORD" | base64 -w 0)
npm config set registry https://prekarilabs.jfrog.io/prekarilabs/api/npm/npm/
npm config set //prekarilabs.jfrog.io/prekarilabs/api/npm/npm/:_auth "$JFROG_AUTH"
diff --git a/.github/workflows/contract-tests.yml b/.github/workflows/contract-tests.yml
index 58471087..db487535 100644
--- a/.github/workflows/contract-tests.yml
+++ b/.github/workflows/contract-tests.yml
@@ -24,7 +24,9 @@ jobs:
- name: Install dependencies
run: npm ci --ignore-scripts
- - name: Verify API surface snapshot
+ # Builds every package, bundles the two published dist/ surfaces, then
+ # verifies BOTH SDKs' public-API snapshots (skyflow-node + skyflow-flowvault-node).
+ - name: Verify API surface snapshots (both SDKs)
run: npm run contract-snapshot-verify
- name: Show API surface diff
@@ -34,12 +36,18 @@ jobs:
echo "Lines prefixed with '-' were REMOVED from the public API."
echo "Lines prefixed with '+' were ADDED to the public API."
echo ""
- diff -u api-report/skyflow-node.api.md temp/skyflow-node.api.md || true
+ echo "===== skyflow-node (skyvault) ====="
+ diff -u skyvault/api-report/skyflow-node.api.md skyvault/temp/skyflow-node.api.md || true
+ echo ""
+ echo "===== skyflow-flowvault-node (flowvault) ====="
+ diff -u flowvault/api-report/skyflow-flowvault-node.api.md flowvault/temp/skyflow-flowvault-node.api.md || true
- - name: Upload API surface diff on failure
+ - name: Upload API surface diffs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
- name: api-surface-diff
- path: temp/skyflow-node.api.md
+ name: api-surface-diffs
+ path: |
+ skyvault/temp/skyflow-node.api.md
+ flowvault/temp/skyflow-flowvault-node.api.md
retention-days: 7
diff --git a/.github/workflows/flowvault-beta-release.yml b/.github/workflows/flowvault-beta-release.yml
new file mode 100644
index 00000000..8bab6bbd
--- /dev/null
+++ b/.github/workflows/flowvault-beta-release.yml
@@ -0,0 +1,18 @@
+name: FlowVault Public Beta Release
+
+on:
+ push:
+ tags: 'flowvault/*.*.*-beta.*'
+ paths-ignore:
+ - "package.json"
+ - "package-lock.json"
+ - "*.md"
+
+jobs:
+ build-and-deploy:
+ uses: ./.github/workflows/common-release.yml
+ with:
+ product: skyflow-flowvault-node
+ package-dir: flowvault
+ release-type: beta
+ secrets: inherit
diff --git a/.github/workflows/flowvault-release.yml b/.github/workflows/flowvault-release.yml
new file mode 100644
index 00000000..8f2423f2
--- /dev/null
+++ b/.github/workflows/flowvault-release.yml
@@ -0,0 +1,19 @@
+name: FlowVault Public Release
+
+# Prefixed tags select the FlowVault product: flowvault/x.y.z -> skyflow-flowvault-node.
+on:
+ push:
+ tags: 'flowvault/[0-9]+.[0-9]+.[0-9]+'
+ paths-ignore:
+ - "package.json"
+ - "package-lock.json"
+ - "*.md"
+
+jobs:
+ call-common-workflow:
+ uses: ./.github/workflows/common-release.yml
+ with:
+ product: skyflow-flowvault-node
+ package-dir: flowvault
+ release-type: public
+ secrets: inherit
diff --git a/.github/workflows/internal-release.yml b/.github/workflows/internal-release.yml
index a87afa1d..8a21e2e8 100644
--- a/.github/workflows/internal-release.yml
+++ b/.github/workflows/internal-release.yml
@@ -1,5 +1,8 @@
name: Internal Release
+# Branch-namespaced internal (JFrog) builds, routed per product:
+# flowvault-release/* -> skyflow-flowvault-node
+# skyvault-release/* | release/* -> skyflow-node (legacy release/* kept for skyvault)
on:
push:
tags-ignore:
@@ -10,10 +13,14 @@ on:
- '*.md'
branches:
- release/*
+ - skyvault-release/*
+ - flowvault-release/*
jobs:
call-common-workflow:
uses: ./.github/workflows/common-release.yml
with:
- tag: internal
+ product: ${{ startsWith(github.ref_name, 'flowvault-release/') && 'skyflow-flowvault-node' || 'skyflow-node' }}
+ package-dir: ${{ startsWith(github.ref_name, 'flowvault-release/') && 'flowvault' || 'skyvault' }}
+ release-type: internal
secrets: inherit
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3f483a74..2b00c620 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,5 +12,7 @@ jobs:
call-common-workflow:
uses: ./.github/workflows/common-release.yml
with:
- tag: public
+ product: skyflow-node
+ package-dir: skyvault
+ release-type: public
secrets: inherit
diff --git a/.gitignore b/.gitignore
index 2d76dc20..68f04726 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
node_modules
lib
+dist
+*.tsbuildinfo
.vscode
-coverage
\ No newline at end of file
+coverage
+temp
diff --git a/Rule/gitleaks.toml b/Rule/gitleaks.toml
index e127f827..be258799 100644
--- a/Rule/gitleaks.toml
+++ b/Rule/gitleaks.toml
@@ -55,6 +55,13 @@ paths = [
'''\.gem$''',
'''verification-metadata\.xml''',
'''Database.refactorlog''',
+ # Maintainer-owned generated (Fern) clients contain example JWTs/tokens in
+ # their doc comments. Allowlist every generated dir across the workspaces
+ # (both the plain and the legacy leading-space directory names).
+ '''(?:^|/)_generated_(?:/|$)''',
+ '''(?:^|/) _generated_(?:/|$)''',
+ # Bundled publish output inlines the generated clients (example JWTs).
+ '''(?:^|/)dist/''',
]
stopwords = [
"abcdefghijklmnopqrstuvwxyz",
diff --git a/api-extractor.json b/api-extractor.json
deleted file mode 100644
index 80bfdd6d..00000000
--- a/api-extractor.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
- "projectFolder": ".",
- "mainEntryPointFilePath": "/lib/index.d.ts",
- "bundledPackages": [],
- "compiler": {
- "tsconfigFilePath": "/tsconfig.json"
- },
- "apiReport": {
- "enabled": true,
- "reportFolder": "/api-report/",
- "reportTempFolder": "/temp/",
- "reportFileName": ".api.md"
- },
- "docModel": {
- "enabled": false
- },
- "dtsRollup": {
- "enabled": false
- },
- "tsdocMetadata": {
- "enabled": false
- },
- "messages": {
- "compilerMessageReporting": {
- "default": {
- "logLevel": "warning"
- }
- },
- "extractorMessageReporting": {
- "default": {
- "logLevel": "warning"
- },
- "ae-missing-release-tag": {
- "logLevel": "none"
- },
- "ae-setter-with-docs": {
- "logLevel": "none"
- }
- },
- "tsdocMessageReporting": {
- "default": {
- "logLevel": "none"
- }
- }
- }
-}
diff --git a/common/.babelrc b/common/.babelrc
new file mode 100644
index 00000000..eb19681f
--- /dev/null
+++ b/common/.babelrc
@@ -0,0 +1,14 @@
+{
+ "presets": [
+ ["@babel/preset-env", {
+ "targets": {
+ "browsers": ["last 2 versions", "ie >= 10"]
+ }
+ }]
+ ],
+ "sourceType": "unambiguous",
+ "plugins": [
+ "@babel/plugin-transform-object-assign",
+ "@babel/plugin-transform-runtime"
+ ]
+}
\ No newline at end of file
diff --git a/common/babel.config.js b/common/babel.config.js
new file mode 100644
index 00000000..7bcee91b
--- /dev/null
+++ b/common/babel.config.js
@@ -0,0 +1,32 @@
+/*
+ Copyright (c) 2022 Skyflow, Inc.
+*/
+module.exports = function (api) {
+ api.cache(false);
+ const presets = [
+ ['@babel/preset-typescript'],
+ [
+ '@babel/preset-env',
+ {
+ corejs: { version: 3 },
+ useBuiltIns: 'usage',
+ targets: {
+ edge: '17',
+ firefox: '60',
+ chrome: '67',
+ safari: '11.1',
+ ie: '11',
+ },
+ },
+ ],
+ ];
+ const plugins = [
+ ['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
+ ['@babel/plugin-transform-class-properties'],
+ ['@babel/transform-runtime'],
+ ];
+ return {
+ presets,
+ plugins,
+ };
+};
diff --git a/common/jest.config.js b/common/jest.config.js
new file mode 100644
index 00000000..35014f57
--- /dev/null
+++ b/common/jest.config.js
@@ -0,0 +1,12 @@
+module.exports = {
+ transform: {
+ '^.+\\.(ts|tsx)?$': 'ts-jest',
+ '^.+\\.(js|jsx)$': 'babel-jest',
+ },
+ verbose: true,
+ collectCoverage: false,
+ testEnvironmentOptions: {
+ "url": "https://skyflow-test.com"
+ },
+ testEnvironment: "node",
+ };
diff --git a/common/package.json b/common/package.json
new file mode 100644
index 00000000..e2fda3e8
--- /dev/null
+++ b/common/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "skyflow-common",
+ "version": "0.0.0",
+ "private": true,
+ "description": "Shared, contract-agnostic core for the Skyflow Node SDKs (internal; bundled into each published package).",
+ "main": "./lib/index.js",
+ "types": "./lib/index.d.ts",
+ "license": "MIT",
+ "scripts": {
+ "build": "tsc -b",
+ "test": "jest"
+ },
+ "dependencies": {
+ "dotenv": "16.6.1",
+ "js-base64": "3.7.7",
+ "jsonwebtoken": "9.0.3",
+ "jwt-decode": "4.0.0",
+ "node-fetch": "2.7.0",
+ "qs": "6.15.2",
+ "readable-stream": "4.7.0",
+ "url-join": "4.0.1"
+ }
+}
diff --git a/common/src/_generated_/rest/Client.ts b/common/src/_generated_/rest/Client.ts
new file mode 100644
index 00000000..b278e5b7
--- /dev/null
+++ b/common/src/_generated_/rest/Client.ts
@@ -0,0 +1,119 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+import * as environments from "./environments";
+import * as core from "./core";
+import * as Skyflow from "./api/index";
+import urlJoin from "url-join";
+import * as errors from "./errors/index";
+import { Authentication } from "./api/resources/authentication/client/Client";
+
+export declare namespace SkyflowClient {
+ export interface Options {
+ environment?: core.Supplier;
+ /** Specify a custom URL to connect the client to. */
+ baseUrl?: core.Supplier;
+ token: core.Supplier;
+ fetcher?: core.FetchFunction;
+ }
+
+ export interface RequestOptions {
+ /** The maximum time to wait for a response in seconds. */
+ timeoutInSeconds?: number;
+ /** The number of times to retry the request. Defaults to 2. */
+ maxRetries?: number;
+ /** A hook to abort the request. */
+ abortSignal?: AbortSignal;
+ /** Additional headers to include in the request. */
+ headers?: Record;
+ }
+}
+
+export class SkyflowClient {
+ protected _authentication: Authentication | undefined;
+
+ constructor(protected readonly _options: SkyflowClient.Options) {}
+
+ public get authentication(): Authentication {
+ return (this._authentication ??= new Authentication(this._options));
+ }
+
+ /**
+ * @param {string | undefined} vaultId
+ * @param {Skyflow.PatchV2VaultsIdRequest} request
+ * @param {SkyflowClient.RequestOptions} requestOptions - Request-specific configuration.
+ *
+ * @example
+ * await client.patchV2VaultsId(undefined)
+ */
+ public patchV2VaultsId(
+ vaultId: string | undefined,
+ request: Skyflow.PatchV2VaultsIdRequest = {},
+ requestOptions?: SkyflowClient.RequestOptions,
+ ): core.HttpResponsePromise {
+ return core.HttpResponsePromise.fromPromise(this.__patchV2VaultsId(vaultId, request, requestOptions));
+ }
+
+ private async __patchV2VaultsId(
+ vaultId: string | undefined,
+ request: Skyflow.PatchV2VaultsIdRequest = {},
+ requestOptions?: SkyflowClient.RequestOptions,
+ ): Promise> {
+ const _response = await (this._options.fetcher ?? core.fetcher)({
+ url: urlJoin(
+ (await core.Supplier.get(this._options.baseUrl)) ??
+ (await core.Supplier.get(this._options.environment)) ??
+ environments.SkyflowEnvironment.Production,
+ `v2/vaults/${encodeURIComponent(vaultId)}`,
+ ),
+ method: "PATCH",
+ headers: {
+ Authorization: await this._getAuthorizationHeader(),
+ "X-Fern-Language": "JavaScript",
+ "X-Fern-SDK-Name": "skyflow",
+ "X-Fern-SDK-Version": "1.0.21",
+ "User-Agent": "skyflow/1.0.21",
+ "X-Fern-Runtime": core.RUNTIME.type,
+ "X-Fern-Runtime-Version": core.RUNTIME.version,
+ ...requestOptions?.headers,
+ },
+ contentType: "application/json",
+ requestType: "json",
+ timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000,
+ maxRetries: requestOptions?.maxRetries,
+ abortSignal: requestOptions?.abortSignal,
+ });
+ if (_response.ok) {
+ return { data: undefined, rawResponse: _response.rawResponse };
+ }
+
+ if (_response.error.reason === "status-code") {
+ throw new errors.SkyflowError({
+ statusCode: _response.error.statusCode,
+ body: _response.error.body,
+ rawResponse: _response.rawResponse,
+ });
+ }
+
+ switch (_response.error.reason) {
+ case "non-json":
+ throw new errors.SkyflowError({
+ statusCode: _response.error.statusCode,
+ body: _response.error.rawBody,
+ rawResponse: _response.rawResponse,
+ });
+ case "timeout":
+ throw new errors.SkyflowTimeoutError("Timeout exceeded when calling PATCH /v2/vaults/{vaultID}.");
+ case "unknown":
+ throw new errors.SkyflowError({
+ message: _response.error.errorMessage,
+ rawResponse: _response.rawResponse,
+ });
+ }
+ }
+
+ protected async _getAuthorizationHeader(): Promise {
+ return `Bearer ${await core.Supplier.get(this._options.token)}`;
+ }
+}
diff --git a/src/ _generated_/rest/api/resources/audit/client/index.ts b/common/src/_generated_/rest/api/client/index.ts
similarity index 100%
rename from src/ _generated_/rest/api/resources/audit/client/index.ts
rename to common/src/_generated_/rest/api/client/index.ts
diff --git a/common/src/_generated_/rest/api/client/requests/PatchV2VaultsIdRequest.ts b/common/src/_generated_/rest/api/client/requests/PatchV2VaultsIdRequest.ts
new file mode 100644
index 00000000..a2929a18
--- /dev/null
+++ b/common/src/_generated_/rest/api/client/requests/PatchV2VaultsIdRequest.ts
@@ -0,0 +1,9 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+/**
+ * @example
+ * {}
+ */
+export interface PatchV2VaultsIdRequest {}
diff --git a/common/src/_generated_/rest/api/client/requests/index.ts b/common/src/_generated_/rest/api/client/requests/index.ts
new file mode 100644
index 00000000..1a0e15b2
--- /dev/null
+++ b/common/src/_generated_/rest/api/client/requests/index.ts
@@ -0,0 +1 @@
+export { type PatchV2VaultsIdRequest } from "./PatchV2VaultsIdRequest";
diff --git a/common/src/_generated_/rest/api/errors/BadRequestError.ts b/common/src/_generated_/rest/api/errors/BadRequestError.ts
new file mode 100644
index 00000000..e19131a1
--- /dev/null
+++ b/common/src/_generated_/rest/api/errors/BadRequestError.ts
@@ -0,0 +1,18 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+import * as errors from "../../errors/index";
+import * as core from "../../core";
+
+export class BadRequestError extends errors.SkyflowError {
+ constructor(body: Record, rawResponse?: core.RawResponse) {
+ super({
+ message: "BadRequestError",
+ statusCode: 400,
+ body: body,
+ rawResponse: rawResponse,
+ });
+ Object.setPrototypeOf(this, BadRequestError.prototype);
+ }
+}
diff --git a/common/src/_generated_/rest/api/errors/NotFoundError.ts b/common/src/_generated_/rest/api/errors/NotFoundError.ts
new file mode 100644
index 00000000..ff59e84a
--- /dev/null
+++ b/common/src/_generated_/rest/api/errors/NotFoundError.ts
@@ -0,0 +1,18 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+import * as errors from "../../errors/index";
+import * as core from "../../core";
+
+export class NotFoundError extends errors.SkyflowError {
+ constructor(body: Record, rawResponse?: core.RawResponse) {
+ super({
+ message: "NotFoundError",
+ statusCode: 404,
+ body: body,
+ rawResponse: rawResponse,
+ });
+ Object.setPrototypeOf(this, NotFoundError.prototype);
+ }
+}
diff --git a/common/src/_generated_/rest/api/errors/UnauthorizedError.ts b/common/src/_generated_/rest/api/errors/UnauthorizedError.ts
new file mode 100644
index 00000000..73c2b114
--- /dev/null
+++ b/common/src/_generated_/rest/api/errors/UnauthorizedError.ts
@@ -0,0 +1,18 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+import * as errors from "../../errors/index";
+import * as core from "../../core";
+
+export class UnauthorizedError extends errors.SkyflowError {
+ constructor(body: Record, rawResponse?: core.RawResponse) {
+ super({
+ message: "UnauthorizedError",
+ statusCode: 401,
+ body: body,
+ rawResponse: rawResponse,
+ });
+ Object.setPrototypeOf(this, UnauthorizedError.prototype);
+ }
+}
diff --git a/common/src/_generated_/rest/api/errors/index.ts b/common/src/_generated_/rest/api/errors/index.ts
new file mode 100644
index 00000000..91d5e383
--- /dev/null
+++ b/common/src/_generated_/rest/api/errors/index.ts
@@ -0,0 +1,3 @@
+export * from "./BadRequestError";
+export * from "./UnauthorizedError";
+export * from "./NotFoundError";
diff --git a/common/src/_generated_/rest/api/index.ts b/common/src/_generated_/rest/api/index.ts
new file mode 100644
index 00000000..dba65abb
--- /dev/null
+++ b/common/src/_generated_/rest/api/index.ts
@@ -0,0 +1,4 @@
+export * from "./types";
+export * from "./errors";
+export * from "./resources";
+export * from "./client";
diff --git a/common/src/_generated_/rest/api/resources/authentication/client/Client.ts b/common/src/_generated_/rest/api/resources/authentication/client/Client.ts
new file mode 100644
index 00000000..7c3b0039
--- /dev/null
+++ b/common/src/_generated_/rest/api/resources/authentication/client/Client.ts
@@ -0,0 +1,137 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+import * as environments from "../../../../environments";
+import * as core from "../../../../core";
+import * as Skyflow from "../../../index";
+import urlJoin from "url-join";
+import * as errors from "../../../../errors/index";
+
+export declare namespace Authentication {
+ export interface Options {
+ environment?: core.Supplier;
+ /** Specify a custom URL to connect the client to. */
+ baseUrl?: core.Supplier;
+ token: core.Supplier;
+ fetcher?: core.FetchFunction;
+ }
+
+ export interface RequestOptions {
+ /** The maximum time to wait for a response in seconds. */
+ timeoutInSeconds?: number;
+ /** The number of times to retry the request. Defaults to 2. */
+ maxRetries?: number;
+ /** A hook to abort the request. */
+ abortSignal?: AbortSignal;
+ /** Additional headers to include in the request. */
+ headers?: Record;
+ }
+}
+
+export class Authentication {
+ constructor(protected readonly _options: Authentication.Options) {}
+
+ /**
+ * Generates a Bearer Token to authenticate with Skyflow. This method doesn't require the Authorization header.
Note: For recommended ways to authenticate, see API authentication.
+ *
+ * @param {Skyflow.V1GetAuthTokenRequest} request
+ * @param {Authentication.RequestOptions} requestOptions - Request-specific configuration.
+ *
+ * @throws {@link Skyflow.BadRequestError}
+ * @throws {@link Skyflow.UnauthorizedError}
+ * @throws {@link Skyflow.NotFoundError}
+ *
+ * @example
+ * await client.authentication.authenticationServiceGetAuthToken({
+ * grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
+ * assertion: "eyLhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNzIjoiY29tcGFueSIsImV4cCI6MTYxNTE5MzgwNywiaWF0IjoxNjE1MTY1MDQwLCJhdWQiOiKzb21lYXVkaWVuY2UifQ.4pcPyMDQ9o1PSyXnrXCjTwXyr4BSezdI1AVTmud2fU3"
+ * })
+ */
+ public authenticationServiceGetAuthToken(
+ request: Skyflow.V1GetAuthTokenRequest,
+ requestOptions?: Authentication.RequestOptions,
+ ): core.HttpResponsePromise {
+ return core.HttpResponsePromise.fromPromise(this.__authenticationServiceGetAuthToken(request, requestOptions));
+ }
+
+ private async __authenticationServiceGetAuthToken(
+ request: Skyflow.V1GetAuthTokenRequest,
+ requestOptions?: Authentication.RequestOptions,
+ ): Promise> {
+ const _response = await (this._options.fetcher ?? core.fetcher)({
+ url: urlJoin(
+ (await core.Supplier.get(this._options.baseUrl)) ??
+ (await core.Supplier.get(this._options.environment)) ??
+ environments.SkyflowEnvironment.Production,
+ "v1/auth/sa/oauth/token",
+ ),
+ method: "POST",
+ headers: {
+ Authorization: await this._getAuthorizationHeader(),
+ "X-Fern-Language": "JavaScript",
+ "X-Fern-SDK-Name": "skyflow",
+ "X-Fern-SDK-Version": "1.0.21",
+ "User-Agent": "skyflow/1.0.21",
+ "X-Fern-Runtime": core.RUNTIME.type,
+ "X-Fern-Runtime-Version": core.RUNTIME.version,
+ ...requestOptions?.headers,
+ },
+ contentType: "application/json",
+ requestType: "json",
+ body: request,
+ timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000,
+ maxRetries: requestOptions?.maxRetries,
+ abortSignal: requestOptions?.abortSignal,
+ });
+ if (_response.ok) {
+ return { data: _response.body as Skyflow.V1GetAuthTokenResponse, rawResponse: _response.rawResponse };
+ }
+
+ if (_response.error.reason === "status-code") {
+ switch (_response.error.statusCode) {
+ case 400:
+ throw new Skyflow.BadRequestError(
+ _response.error.body as Record,
+ _response.rawResponse,
+ );
+ case 401:
+ throw new Skyflow.UnauthorizedError(
+ _response.error.body as Record,
+ _response.rawResponse,
+ );
+ case 404:
+ throw new Skyflow.NotFoundError(
+ _response.error.body as Record,
+ _response.rawResponse,
+ );
+ default:
+ throw new errors.SkyflowError({
+ statusCode: _response.error.statusCode,
+ body: _response.error.body,
+ rawResponse: _response.rawResponse,
+ });
+ }
+ }
+
+ switch (_response.error.reason) {
+ case "non-json":
+ throw new errors.SkyflowError({
+ statusCode: _response.error.statusCode,
+ body: _response.error.rawBody,
+ rawResponse: _response.rawResponse,
+ });
+ case "timeout":
+ throw new errors.SkyflowTimeoutError("Timeout exceeded when calling POST /v1/auth/sa/oauth/token.");
+ case "unknown":
+ throw new errors.SkyflowError({
+ message: _response.error.errorMessage,
+ rawResponse: _response.rawResponse,
+ });
+ }
+ }
+
+ protected async _getAuthorizationHeader(): Promise {
+ return `Bearer ${await core.Supplier.get(this._options.token)}`;
+ }
+}
diff --git a/src/ _generated_/rest/api/resources/authentication/client/index.ts b/common/src/_generated_/rest/api/resources/authentication/client/index.ts
similarity index 100%
rename from src/ _generated_/rest/api/resources/authentication/client/index.ts
rename to common/src/_generated_/rest/api/resources/authentication/client/index.ts
diff --git a/common/src/_generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts b/common/src/_generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts
new file mode 100644
index 00000000..a1ef4fba
--- /dev/null
+++ b/common/src/_generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts
@@ -0,0 +1,25 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+/**
+ * @example
+ * {
+ * grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
+ * assertion: "eyLhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNzIjoiY29tcGFueSIsImV4cCI6MTYxNTE5MzgwNywiaWF0IjoxNjE1MTY1MDQwLCJhdWQiOiKzb21lYXVkaWVuY2UifQ.4pcPyMDQ9o1PSyXnrXCjTwXyr4BSezdI1AVTmud2fU3"
+ * }
+ */
+export interface V1GetAuthTokenRequest {
+ /** Grant type of the request. Set this to `urn:ietf:params:oauth:grant-type:jwt-bearer`. */
+ grant_type: string;
+ /** User-signed JWT token that contains the following fields:
iss: Issuer of the JWT.key: Unique identifier for the key.aud: Recipient the JWT is intended for.exp: Time the JWT expires.sub: Subject of the JWT.ctx: (Optional) Value for Context-aware authorization.
*/
+ assertion: string;
+ /** Subject token. */
+ subject_token?: string;
+ /** Subject token type. */
+ subject_token_type?: string;
+ /** Token use type. Either `delegation` or `impersonation`. */
+ requested_token_use?: string;
+ /** Subset of available roles to associate with the requested token. Uses the format "role:\ role:\". */
+ scope?: string;
+}
diff --git a/src/ _generated_/rest/api/resources/authentication/client/requests/index.ts b/common/src/_generated_/rest/api/resources/authentication/client/requests/index.ts
similarity index 100%
rename from src/ _generated_/rest/api/resources/authentication/client/requests/index.ts
rename to common/src/_generated_/rest/api/resources/authentication/client/requests/index.ts
diff --git a/src/ _generated_/rest/api/resources/authentication/index.ts b/common/src/_generated_/rest/api/resources/authentication/index.ts
similarity index 100%
rename from src/ _generated_/rest/api/resources/authentication/index.ts
rename to common/src/_generated_/rest/api/resources/authentication/index.ts
diff --git a/common/src/_generated_/rest/api/resources/index.ts b/common/src/_generated_/rest/api/resources/index.ts
new file mode 100644
index 00000000..d933c827
--- /dev/null
+++ b/common/src/_generated_/rest/api/resources/index.ts
@@ -0,0 +1,2 @@
+export * as authentication from "./authentication";
+export * from "./authentication/client/requests";
diff --git a/src/ _generated_/rest/api/types/GooglerpcStatus.ts b/common/src/_generated_/rest/api/types/GooglerpcStatus.ts
similarity index 100%
rename from src/ _generated_/rest/api/types/GooglerpcStatus.ts
rename to common/src/_generated_/rest/api/types/GooglerpcStatus.ts
diff --git a/src/ _generated_/rest/api/types/ProtobufAny.ts b/common/src/_generated_/rest/api/types/ProtobufAny.ts
similarity index 100%
rename from src/ _generated_/rest/api/types/ProtobufAny.ts
rename to common/src/_generated_/rest/api/types/ProtobufAny.ts
diff --git a/src/ _generated_/rest/api/types/V1GetAuthTokenResponse.ts b/common/src/_generated_/rest/api/types/V1GetAuthTokenResponse.ts
similarity index 100%
rename from src/ _generated_/rest/api/types/V1GetAuthTokenResponse.ts
rename to common/src/_generated_/rest/api/types/V1GetAuthTokenResponse.ts
diff --git a/common/src/_generated_/rest/api/types/index.ts b/common/src/_generated_/rest/api/types/index.ts
new file mode 100644
index 00000000..63cf5e7a
--- /dev/null
+++ b/common/src/_generated_/rest/api/types/index.ts
@@ -0,0 +1,3 @@
+export * from "./V1GetAuthTokenResponse";
+export * from "./GooglerpcStatus";
+export * from "./ProtobufAny";
diff --git a/src/ _generated_/rest/core/auth/BasicAuth.ts b/common/src/_generated_/rest/core/auth/BasicAuth.ts
similarity index 100%
rename from src/ _generated_/rest/core/auth/BasicAuth.ts
rename to common/src/_generated_/rest/core/auth/BasicAuth.ts
diff --git a/src/ _generated_/rest/core/auth/BearerToken.ts b/common/src/_generated_/rest/core/auth/BearerToken.ts
similarity index 100%
rename from src/ _generated_/rest/core/auth/BearerToken.ts
rename to common/src/_generated_/rest/core/auth/BearerToken.ts
diff --git a/src/ _generated_/rest/core/auth/index.ts b/common/src/_generated_/rest/core/auth/index.ts
similarity index 100%
rename from src/ _generated_/rest/core/auth/index.ts
rename to common/src/_generated_/rest/core/auth/index.ts
diff --git a/src/ _generated_/rest/core/fetcher/APIResponse.ts b/common/src/_generated_/rest/core/fetcher/APIResponse.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/APIResponse.ts
rename to common/src/_generated_/rest/core/fetcher/APIResponse.ts
diff --git a/src/ _generated_/rest/core/fetcher/Fetcher.ts b/common/src/_generated_/rest/core/fetcher/Fetcher.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/Fetcher.ts
rename to common/src/_generated_/rest/core/fetcher/Fetcher.ts
diff --git a/src/ _generated_/rest/core/fetcher/HttpResponsePromise.ts b/common/src/_generated_/rest/core/fetcher/HttpResponsePromise.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/HttpResponsePromise.ts
rename to common/src/_generated_/rest/core/fetcher/HttpResponsePromise.ts
diff --git a/src/ _generated_/rest/core/fetcher/RawResponse.ts b/common/src/_generated_/rest/core/fetcher/RawResponse.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/RawResponse.ts
rename to common/src/_generated_/rest/core/fetcher/RawResponse.ts
diff --git a/src/ _generated_/rest/core/fetcher/Supplier.ts b/common/src/_generated_/rest/core/fetcher/Supplier.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/Supplier.ts
rename to common/src/_generated_/rest/core/fetcher/Supplier.ts
diff --git a/src/ _generated_/rest/core/fetcher/createRequestUrl.ts b/common/src/_generated_/rest/core/fetcher/createRequestUrl.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/createRequestUrl.ts
rename to common/src/_generated_/rest/core/fetcher/createRequestUrl.ts
diff --git a/src/ _generated_/rest/core/fetcher/getFetchFn.ts b/common/src/_generated_/rest/core/fetcher/getFetchFn.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/getFetchFn.ts
rename to common/src/_generated_/rest/core/fetcher/getFetchFn.ts
diff --git a/src/ _generated_/rest/core/fetcher/getHeader.ts b/common/src/_generated_/rest/core/fetcher/getHeader.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/getHeader.ts
rename to common/src/_generated_/rest/core/fetcher/getHeader.ts
diff --git a/src/ _generated_/rest/core/fetcher/getRequestBody.ts b/common/src/_generated_/rest/core/fetcher/getRequestBody.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/getRequestBody.ts
rename to common/src/_generated_/rest/core/fetcher/getRequestBody.ts
diff --git a/src/ _generated_/rest/core/fetcher/getResponseBody.ts b/common/src/_generated_/rest/core/fetcher/getResponseBody.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/getResponseBody.ts
rename to common/src/_generated_/rest/core/fetcher/getResponseBody.ts
diff --git a/src/ _generated_/rest/core/fetcher/index.ts b/common/src/_generated_/rest/core/fetcher/index.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/index.ts
rename to common/src/_generated_/rest/core/fetcher/index.ts
diff --git a/src/ _generated_/rest/core/fetcher/makeRequest.ts b/common/src/_generated_/rest/core/fetcher/makeRequest.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/makeRequest.ts
rename to common/src/_generated_/rest/core/fetcher/makeRequest.ts
diff --git a/src/ _generated_/rest/core/fetcher/requestWithRetries.ts b/common/src/_generated_/rest/core/fetcher/requestWithRetries.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/requestWithRetries.ts
rename to common/src/_generated_/rest/core/fetcher/requestWithRetries.ts
diff --git a/src/ _generated_/rest/core/fetcher/signals.ts b/common/src/_generated_/rest/core/fetcher/signals.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/signals.ts
rename to common/src/_generated_/rest/core/fetcher/signals.ts
diff --git a/common/src/_generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts b/common/src/_generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts
new file mode 100644
index 00000000..7d83aef2
--- /dev/null
+++ b/common/src/_generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts
@@ -0,0 +1,259 @@
+import type { Writable } from "readable-stream";
+
+import { EventCallback, StreamWrapper } from "./chooseStreamWrapper";
+
+export class Node18UniversalStreamWrapper<
+ ReadFormat extends Uint8Array | Uint16Array | Uint32Array,
+> implements StreamWrapper<
+ Node18UniversalStreamWrapper | Writable | WritableStream,
+ ReadFormat
+> {
+ private readableStream: ReadableStream;
+ private reader: ReadableStreamDefaultReader;
+ private events: Record;
+ private paused: boolean;
+ private resumeCallback: ((value?: unknown) => void) | null;
+ private encoding: string | null;
+
+ constructor(readableStream: ReadableStream) {
+ this.readableStream = readableStream;
+ this.reader = this.readableStream.getReader();
+ this.events = {
+ data: [],
+ end: [],
+ error: [],
+ readable: [],
+ close: [],
+ pause: [],
+ resume: [],
+ };
+ this.paused = false;
+ this.resumeCallback = null;
+ this.encoding = null;
+ }
+
+ public on(event: string, callback: EventCallback): void {
+ this.events[event]?.push(callback);
+ }
+
+ public off(event: string, callback: EventCallback): void {
+ this.events[event] = this.events[event]?.filter((cb) => cb !== callback);
+ }
+
+ public pipe(
+ dest: Node18UniversalStreamWrapper | Writable | WritableStream,
+ ): Node18UniversalStreamWrapper | Writable | WritableStream {
+ this.on("data", async (chunk) => {
+ if (dest instanceof Node18UniversalStreamWrapper) {
+ dest._write(chunk);
+ } else if (dest instanceof WritableStream) {
+ const writer = dest.getWriter();
+ writer.write(chunk).then(() => writer.releaseLock());
+ } else {
+ dest.write(chunk);
+ }
+ });
+
+ this.on("end", async () => {
+ if (dest instanceof Node18UniversalStreamWrapper) {
+ dest._end();
+ } else if (dest instanceof WritableStream) {
+ const writer = dest.getWriter();
+ writer.close();
+ } else {
+ dest.end();
+ }
+ });
+
+ this.on("error", async (error) => {
+ if (dest instanceof Node18UniversalStreamWrapper) {
+ dest._error(error);
+ } else if (dest instanceof WritableStream) {
+ const writer = dest.getWriter();
+ writer.abort(error);
+ } else {
+ dest.destroy(error);
+ }
+ });
+
+ this._startReading();
+
+ return dest;
+ }
+
+ public pipeTo(
+ dest: Node18UniversalStreamWrapper | Writable | WritableStream,
+ ): Node18UniversalStreamWrapper | Writable | WritableStream {
+ return this.pipe(dest);
+ }
+
+ public unpipe(dest: Node18UniversalStreamWrapper | Writable | WritableStream): void {
+ this.off("data", async (chunk) => {
+ if (dest instanceof Node18UniversalStreamWrapper) {
+ dest._write(chunk);
+ } else if (dest instanceof WritableStream) {
+ const writer = dest.getWriter();
+ writer.write(chunk).then(() => writer.releaseLock());
+ } else {
+ dest.write(chunk);
+ }
+ });
+
+ this.off("end", async () => {
+ if (dest instanceof Node18UniversalStreamWrapper) {
+ dest._end();
+ } else if (dest instanceof WritableStream) {
+ const writer = dest.getWriter();
+ writer.close();
+ } else {
+ dest.end();
+ }
+ });
+
+ this.off("error", async (error) => {
+ if (dest instanceof Node18UniversalStreamWrapper) {
+ dest._error(error);
+ } else if (dest instanceof WritableStream) {
+ const writer = dest.getWriter();
+ writer.abort(error);
+ } else {
+ dest.destroy(error);
+ }
+ });
+ }
+
+ public destroy(error?: Error): void {
+ this.reader
+ .cancel(error)
+ .then(() => {
+ this._emit("close");
+ })
+ .catch((err) => {
+ this._emit("error", err);
+ });
+ }
+
+ public pause(): void {
+ this.paused = true;
+ this._emit("pause");
+ }
+
+ public resume(): void {
+ if (this.paused) {
+ this.paused = false;
+ this._emit("resume");
+ if (this.resumeCallback) {
+ this.resumeCallback();
+ this.resumeCallback = null;
+ }
+ }
+ }
+
+ public get isPaused(): boolean {
+ return this.paused;
+ }
+
+ public async read(): Promise {
+ if (this.paused) {
+ await new Promise((resolve) => {
+ this.resumeCallback = resolve;
+ });
+ }
+ const { done, value } = await this.reader.read();
+
+ if (done) {
+ return undefined;
+ }
+ return value;
+ }
+
+ public setEncoding(encoding: string): void {
+ this.encoding = encoding;
+ }
+
+ public async text(): Promise {
+ const chunks: ReadFormat[] = [];
+
+ while (true) {
+ const { done, value } = await this.reader.read();
+ if (done) {
+ break;
+ }
+ if (value) {
+ chunks.push(value);
+ }
+ }
+
+ const decoder = new TextDecoder(this.encoding || "utf-8");
+ return decoder.decode(await new Blob(chunks).arrayBuffer());
+ }
+
+ public async json(): Promise {
+ const text = await this.text();
+ return JSON.parse(text);
+ }
+
+ private _write(chunk: ReadFormat): void {
+ this._emit("data", chunk);
+ }
+
+ private _end(): void {
+ this._emit("end");
+ }
+
+ private _error(error: any): void {
+ this._emit("error", error);
+ }
+
+ private _emit(event: string, data?: any): void {
+ if (this.events[event]) {
+ for (const callback of this.events[event] || []) {
+ callback(data);
+ }
+ }
+ }
+
+ private async _startReading(): Promise {
+ try {
+ this._emit("readable");
+ while (true) {
+ if (this.paused) {
+ await new Promise((resolve) => {
+ this.resumeCallback = resolve;
+ });
+ }
+ const { done, value } = await this.reader.read();
+ if (done) {
+ this._emit("end");
+ this._emit("close");
+ break;
+ }
+ if (value) {
+ this._emit("data", value);
+ }
+ }
+ } catch (error) {
+ this._emit("error", error);
+ }
+ }
+
+ [Symbol.asyncIterator](): AsyncIterableIterator {
+ return {
+ next: async () => {
+ if (this.paused) {
+ await new Promise((resolve) => {
+ this.resumeCallback = resolve;
+ });
+ }
+ const { done, value } = await this.reader.read();
+ if (done) {
+ return { done: true, value: undefined };
+ }
+ return { done: false, value };
+ },
+ [Symbol.asyncIterator]() {
+ return this;
+ },
+ };
+ }
+}
diff --git a/src/ _generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts b/common/src/_generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts
rename to common/src/_generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts
diff --git a/common/src/_generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts b/common/src/_generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts
new file mode 100644
index 00000000..e5e8b680
--- /dev/null
+++ b/common/src/_generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts
@@ -0,0 +1,244 @@
+import { StreamWrapper } from "./chooseStreamWrapper";
+
+type EventCallback = (data?: any) => void;
+
+export class UndiciStreamWrapper implements StreamWrapper<
+ UndiciStreamWrapper | WritableStream,
+ ReadFormat
+> {
+ private readableStream: ReadableStream;
+ private reader: ReadableStreamDefaultReader;
+ private events: Record;
+ private paused: boolean;
+ private resumeCallback: ((value?: unknown) => void) | null;
+ private encoding: string | null;
+
+ constructor(readableStream: ReadableStream) {
+ this.readableStream = readableStream;
+ this.reader = this.readableStream.getReader();
+ this.events = {
+ data: [],
+ end: [],
+ error: [],
+ readable: [],
+ close: [],
+ pause: [],
+ resume: [],
+ };
+ this.paused = false;
+ this.resumeCallback = null;
+ this.encoding = null;
+ }
+
+ public on(event: string, callback: EventCallback): void {
+ this.events[event]?.push(callback);
+ }
+
+ public off(event: string, callback: EventCallback): void {
+ this.events[event] = this.events[event]?.filter((cb) => cb !== callback);
+ }
+
+ public pipe(
+ dest: UndiciStreamWrapper | WritableStream,
+ ): UndiciStreamWrapper | WritableStream {
+ this.on("data", (chunk) => {
+ if (dest instanceof UndiciStreamWrapper) {
+ dest._write(chunk);
+ } else {
+ const writer = dest.getWriter();
+ writer.write(chunk).then(() => writer.releaseLock());
+ }
+ });
+
+ this.on("end", () => {
+ if (dest instanceof UndiciStreamWrapper) {
+ dest._end();
+ } else {
+ const writer = dest.getWriter();
+ writer.close();
+ }
+ });
+
+ this.on("error", (error) => {
+ if (dest instanceof UndiciStreamWrapper) {
+ dest._error(error);
+ } else {
+ const writer = dest.getWriter();
+ writer.abort(error);
+ }
+ });
+
+ this._startReading();
+
+ return dest;
+ }
+
+ public pipeTo(
+ dest: UndiciStreamWrapper | WritableStream,
+ ): UndiciStreamWrapper | WritableStream {
+ return this.pipe(dest);
+ }
+
+ public unpipe(dest: UndiciStreamWrapper | WritableStream): void {
+ this.off("data", (chunk) => {
+ if (dest instanceof UndiciStreamWrapper) {
+ dest._write(chunk);
+ } else {
+ const writer = dest.getWriter();
+ writer.write(chunk).then(() => writer.releaseLock());
+ }
+ });
+
+ this.off("end", () => {
+ if (dest instanceof UndiciStreamWrapper) {
+ dest._end();
+ } else {
+ const writer = dest.getWriter();
+ writer.close();
+ }
+ });
+
+ this.off("error", (error) => {
+ if (dest instanceof UndiciStreamWrapper) {
+ dest._error(error);
+ } else {
+ const writer = dest.getWriter();
+ writer.abort(error);
+ }
+ });
+ }
+
+ public destroy(error?: Error): void {
+ this.reader
+ .cancel(error)
+ .then(() => {
+ this._emit("close");
+ })
+ .catch((err) => {
+ this._emit("error", err);
+ });
+ }
+
+ public pause(): void {
+ this.paused = true;
+ this._emit("pause");
+ }
+
+ public resume(): void {
+ if (this.paused) {
+ this.paused = false;
+ this._emit("resume");
+ if (this.resumeCallback) {
+ this.resumeCallback();
+ this.resumeCallback = null;
+ }
+ }
+ }
+
+ public get isPaused(): boolean {
+ return this.paused;
+ }
+
+ public async read(): Promise {
+ if (this.paused) {
+ await new Promise((resolve) => {
+ this.resumeCallback = resolve;
+ });
+ }
+ const { done, value } = await this.reader.read();
+ if (done) {
+ return undefined;
+ }
+ return value;
+ }
+
+ public setEncoding(encoding: string): void {
+ this.encoding = encoding;
+ }
+
+ public async text(): Promise {
+ const chunks: BlobPart[] = [];
+
+ while (true) {
+ const { done, value } = await this.reader.read();
+ if (done) {
+ break;
+ }
+ if (value) {
+ chunks.push(value);
+ }
+ }
+
+ const decoder = new TextDecoder(this.encoding || "utf-8");
+ return decoder.decode(await new Blob(chunks).arrayBuffer());
+ }
+
+ public async json(): Promise {
+ const text = await this.text();
+ return JSON.parse(text);
+ }
+
+ private _write(chunk: ReadFormat): void {
+ this._emit("data", chunk);
+ }
+
+ private _end(): void {
+ this._emit("end");
+ }
+
+ private _error(error: any): void {
+ this._emit("error", error);
+ }
+
+ private _emit(event: string, data?: any): void {
+ if (this.events[event]) {
+ for (const callback of this.events[event] || []) {
+ callback(data);
+ }
+ }
+ }
+
+ private async _startReading(): Promise {
+ try {
+ this._emit("readable");
+ while (true) {
+ if (this.paused) {
+ await new Promise((resolve) => {
+ this.resumeCallback = resolve;
+ });
+ }
+ const { done, value } = await this.reader.read();
+ if (done) {
+ this._emit("end");
+ this._emit("close");
+ break;
+ }
+ if (value) {
+ this._emit("data", value);
+ }
+ }
+ } catch (error) {
+ this._emit("error", error);
+ }
+ }
+
+ [Symbol.asyncIterator](): AsyncIterableIterator {
+ return {
+ next: async () => {
+ if (this.paused) {
+ await new Promise((resolve) => {
+ this.resumeCallback = resolve;
+ });
+ }
+ const { done, value } = await this.reader.read();
+ if (done) {
+ return { done: true, value: undefined };
+ }
+ return { done: false, value };
+ },
+ [Symbol.asyncIterator]() {
+ return this;
+ },
+ };
+ }
+}
diff --git a/src/ _generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts b/common/src/_generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts
similarity index 100%
rename from src/ _generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts
rename to common/src/_generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts
diff --git a/common/src/_generated_/rest/core/index.ts b/common/src/_generated_/rest/core/index.ts
new file mode 100644
index 00000000..c752c6a6
--- /dev/null
+++ b/common/src/_generated_/rest/core/index.ts
@@ -0,0 +1,3 @@
+export * from "./fetcher";
+export * from "./auth";
+export * from "./runtime";
diff --git a/src/ _generated_/rest/core/json.ts b/common/src/_generated_/rest/core/json.ts
similarity index 100%
rename from src/ _generated_/rest/core/json.ts
rename to common/src/_generated_/rest/core/json.ts
diff --git a/src/ _generated_/rest/core/runtime/index.ts b/common/src/_generated_/rest/core/runtime/index.ts
similarity index 100%
rename from src/ _generated_/rest/core/runtime/index.ts
rename to common/src/_generated_/rest/core/runtime/index.ts
diff --git a/src/ _generated_/rest/core/runtime/runtime.ts b/common/src/_generated_/rest/core/runtime/runtime.ts
similarity index 100%
rename from src/ _generated_/rest/core/runtime/runtime.ts
rename to common/src/_generated_/rest/core/runtime/runtime.ts
diff --git a/common/src/_generated_/rest/environments.ts b/common/src/_generated_/rest/environments.ts
new file mode 100644
index 00000000..4a1edc91
--- /dev/null
+++ b/common/src/_generated_/rest/environments.ts
@@ -0,0 +1,10 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+export const SkyflowEnvironment = {
+ Production: "https://manage.skyflowapis.com",
+ Sandbox: "https://manage.skyflowapis-preview.com",
+} as const;
+
+export type SkyflowEnvironment = typeof SkyflowEnvironment.Production | typeof SkyflowEnvironment.Sandbox;
diff --git a/src/ _generated_/rest/errors/SkyflowError.ts b/common/src/_generated_/rest/errors/SkyflowError.ts
similarity index 100%
rename from src/ _generated_/rest/errors/SkyflowError.ts
rename to common/src/_generated_/rest/errors/SkyflowError.ts
diff --git a/src/ _generated_/rest/errors/SkyflowTimeoutError.ts b/common/src/_generated_/rest/errors/SkyflowTimeoutError.ts
similarity index 100%
rename from src/ _generated_/rest/errors/SkyflowTimeoutError.ts
rename to common/src/_generated_/rest/errors/SkyflowTimeoutError.ts
diff --git a/src/ _generated_/rest/errors/index.ts b/common/src/_generated_/rest/errors/index.ts
similarity index 100%
rename from src/ _generated_/rest/errors/index.ts
rename to common/src/_generated_/rest/errors/index.ts
diff --git a/src/ _generated_/rest/index.ts b/common/src/_generated_/rest/index.ts
similarity index 100%
rename from src/ _generated_/rest/index.ts
rename to common/src/_generated_/rest/index.ts
diff --git a/src/ _generated_/rest/version.ts b/common/src/_generated_/rest/version.ts
similarity index 100%
rename from src/ _generated_/rest/version.ts
rename to common/src/_generated_/rest/version.ts
diff --git a/common/src/base/options.ts b/common/src/base/options.ts
new file mode 100644
index 00000000..dc7e1ba5
--- /dev/null
+++ b/common/src/base/options.ts
@@ -0,0 +1,11 @@
+export class BaseOptions {
+ private _requestHeaders?: Record;
+
+ setRequestHeaders(h: Record): void {
+ this._requestHeaders = h;
+ }
+
+ getRequestHeaders(): Record | undefined {
+ return this._requestHeaders;
+ }
+}
diff --git a/common/src/base/request.ts b/common/src/base/request.ts
new file mode 100644
index 00000000..1af86d47
--- /dev/null
+++ b/common/src/base/request.ts
@@ -0,0 +1,14 @@
+// Contract-agnostic marker base classes for request models.
+// Layers (skyvault/flowvault) extend these for their own contract-specific requests.
+export class BaseInsertRequest {}
+export class BaseGetRequest {}
+export class BaseDeleteRequest {}
+export class BaseQueryRequest {}
+export class BaseDetokenizeRequest {}
+export class BaseUpdateRequest {}
+export class BaseTokenizeRequest {}
+export class BaseFileUploadRequest {}
+export class BaseInvokeConnectionRequest {}
+export class BaseGetTokensRequest {}
+export class BaseUploadFilesRequest {}
+export class BaseDeleteFilesRequest {}
diff --git a/common/src/base/response.ts b/common/src/base/response.ts
new file mode 100644
index 00000000..9fcc24b4
--- /dev/null
+++ b/common/src/base/response.ts
@@ -0,0 +1,14 @@
+// Contract-agnostic marker base classes for response models.
+// Layers (skyvault/flowvault) extend these for their own contract-specific responses.
+export class BaseInsertResponse {}
+export class BaseGetResponse {}
+export class BaseDeleteResponse {}
+export class BaseQueryResponse {}
+export class BaseDetokenizeResponse {}
+export class BaseUpdateResponse {}
+export class BaseTokenizeResponse {}
+export class BaseFileUploadResponse {}
+export class BaseInvokeConnectionResponse {}
+export class BaseGetTokensResponse {}
+export class BaseUploadFilesResponse {}
+export class BaseDeleteFilesResponse {}
diff --git a/src/vault/config/connection/index.ts b/common/src/config/connection/index.ts
similarity index 100%
rename from src/vault/config/connection/index.ts
rename to common/src/config/connection/index.ts
diff --git a/src/vault/config/credentials/index.ts b/common/src/config/credentials/index.ts
similarity index 100%
rename from src/vault/config/credentials/index.ts
rename to common/src/config/credentials/index.ts
diff --git a/src/vault/config/vault/index.ts b/common/src/config/vault/index.ts
similarity index 83%
rename from src/vault/config/vault/index.ts
rename to common/src/config/vault/index.ts
index f33610d2..cda48789 100644
--- a/src/vault/config/vault/index.ts
+++ b/common/src/config/vault/index.ts
@@ -1,5 +1,5 @@
// imports
-import { Env } from "../../../utils";
+import { Env } from "../../utils";
import Credentials from "../credentials";
interface VaultConfig {
diff --git a/src/error/codes/index.ts b/common/src/error/codes/index.ts
similarity index 95%
rename from src/error/codes/index.ts
rename to common/src/error/codes/index.ts
index 672bd33a..141bafd9 100644
--- a/src/error/codes/index.ts
+++ b/common/src/error/codes/index.ts
@@ -1,6 +1,6 @@
import errorMessages from "../messages";
-const SKYFLOW_ERROR_CODE = {
+const _SKYFLOW_ERROR_CODE = {
CONFIG_MISSING: { httpCode: 400, message: errorMessages.CONFIG_MISSING },
INVALID_TYPE_FOR_CONFIG: { httpCode: 400, message: errorMessages.INVALID_TYPE_FOR_CONFIG },
EMPTY_VAULT_CONFIG: { httpCode: 400, message: errorMessages.EMPTY_VAULT_CONFIG },
@@ -236,4 +236,24 @@ const SKYFLOW_ERROR_CODE = {
INVALID_XML_FORMAT: { httpCode: 400, message: errorMessages.INVALID_XML_FORMAT },
};
+// Each access returns an entry whose `message` is resolved lazily from the
+// identity-aware `errorMessages`, so the SDK version/name in error text reflects
+// the live SDK identity (set per layer via setSdkIdentity) at throw time rather
+// than being frozen at module-load. `httpCode` comes from the literal above.
+const SKYFLOW_ERROR_CODE: typeof _SKYFLOW_ERROR_CODE = new Proxy(_SKYFLOW_ERROR_CODE, {
+ get(target, prop) {
+ if (typeof prop !== "string" || !(prop in target)) {
+ return (target as Record)[prop as string];
+ }
+ const key = prop as keyof typeof _SKYFLOW_ERROR_CODE;
+ const httpCode = target[key].httpCode;
+ return {
+ httpCode,
+ get message(): string {
+ return (errorMessages as Record)[key as string];
+ },
+ };
+ },
+});
+
export default SKYFLOW_ERROR_CODE;
\ No newline at end of file
diff --git a/src/error/index.ts b/common/src/error/index.ts
similarity index 100%
rename from src/error/index.ts
rename to common/src/error/index.ts
diff --git a/common/src/error/messages/index.ts b/common/src/error/messages/index.ts
new file mode 100644
index 00000000..b5c193ca
--- /dev/null
+++ b/common/src/error/messages/index.ts
@@ -0,0 +1,252 @@
+import { getSdkIdentity } from "../../identity";
+
+const errorPrefix = (): string => `Skyflow Node SDK v${getSdkIdentity().version}`;
+
+const errorMessages = {
+ get CONFIG_MISSING(): string { return `${errorPrefix()} Initialization failed. Skyflow config cannot be empty. Specify a valid skyflow config.`; },
+ get INVALID_SKYFLOW_CONFIG(): string { return `${errorPrefix()} Initialization failed. Invalid skyflow config. Vault/Connection config key missing in skyflow config.`; },
+ get INVALID_TYPE_FOR_CONFIG(): string { return `${errorPrefix()} Initialization failed. Invalid %s1 config. Specify a valid %s1 config.`; },
+ get EMPTY_VAULT_CONFIG(): string { return `${errorPrefix()} Initialization failed. Vault config cannot be empty. Specify a valid vault config.`; },
+ get EMPTY_CONNECTION_CONFIG(): string { return `${errorPrefix()} Initialization failed. Connection config cannot be empty. Specify a valid connection config.`; },
+
+ get EMPTY_VAULT_ID(): string { return `${errorPrefix()} Initialization failed. Invalid vault ID. Specify a valid vault Id.`; },
+ get EMPTY_VAULT_ID_VALIDATION(): string { return `${errorPrefix()} Validation error. Invalid vault ID. Specify a valid vault Id.`; },
+ get INVALID_VAULT_ID(): string { return `${errorPrefix()} Initialization failed. Invalid vault ID. Specify a valid vault Id as a string.`; },
+ get EMPTY_CLUSTER_ID(): string { return `${errorPrefix()} Initialization failed. Invalid cluster ID. Specify a valid cluster Id for vault with vaultId %s1 .`; },
+ get INVALID_CLUSTER_ID(): string { return `${errorPrefix()} Initialization failed. Invalid cluster ID. Specify cluster Id as a string for vault with vaultId %s1.`; },
+ get INVALID_TOKEN(): string { return `${errorPrefix()} Validation error. Invalid token. Specify a valid token.`; },
+ get TOKEN_EXPIRED(): string { return `${errorPrefix()} Validation error. Token provided is either invalid or has expired. Specify a valid token.`; },
+ get INVALID_ENV(): string { return `${errorPrefix()} Initialization failed. Invalid env. Specify a valid env for vault with vaultId %s1.`; },
+ get INVALID_LOG_LEVEL(): string { return `${errorPrefix()} Initialization failed. Invalid log level. Specify a valid log level.`; },
+ get EMPTY_CREDENTIAL_FILE_PATH(): string { return `${errorPrefix()} Initialization failed. Invalid credentials. Specify a valid file path.`; },
+ get INVALID_CREDENTIAL_FILE_PATH(): string { return `${errorPrefix()} Initialization failed. Invalid credentials. Expected file path to be a string.`; },
+
+ get INVALID_CREDENTIALS_FILE_PATH(): string { return `${errorPrefix()} Initialization failed. Invalid skyflow credentials. Expected file path to exists.`; },
+ get INVALID_TOKEN_URI(): string { return `${errorPrefix()} Initialization failed. Invalid Skyflow credentials. The token URI must be a string and a valid URL.`; },
+ get INVALID_TOKEN_URI_WITH_ID(): string { return `${errorPrefix()} Initialization failed. Invalid Skyflow credentials. The token URI must be a string and a valid URL for %s1 with %s2 %s3.`; },
+ get INVALID_KEY(): string { return `${errorPrefix()} Initialization failed. Invalid skyflow credentials. Specify a valid api key.`; },
+ get INVALID_PARSED_CREDENTIALS_STRING(): string { return `${errorPrefix()} Initialization failed. Invalid skyflow credentials. Specify a valid credentials string.`; },
+ get INVALID_BEARER_TOKEN(): string { return `${errorPrefix()} Initialization failed. Invalid skyflow credentials. Bearer token is invalid or expired. Specify a valid token.`; },
+
+ get INVALID_FILE_PATH_WITH_ID(): string { return `${errorPrefix()} Initialization failed. Invalid credentials. Expected file path to exists for %s1 with %s2 %s3.`; },
+ get INVALID_KEY_WITH_ID(): string { return `${errorPrefix()} Initialization failed. Invalid credentials. Specify a valid api key for %s1 with %s2 %s3.`; },
+ get INVALID_PARSED_CREDENTIALS_STRING_WITH_ID(): string { return `${errorPrefix()} Initialization failed. Invalid credentials. Specify a valid credentials string for %s1 with %s2 %s3.`; },
+ get INVALID_BEARER_TOKEN_WITH_ID(): string { return `${errorPrefix()} Initialization failed. Invalid credentials. Bearer token is invalid or expired. Specify a valid token for %s1 with %s2 %s3.`; },
+
+ get EMPTY_CONNECTION_ID_VALIDATION(): string { return `${errorPrefix()} Validation error. Invalid connection ID. Specify a valid connection Id.`; },
+ get EMPTY_CONNECTION_ID(): string { return `${errorPrefix()} Initialization failed. Invalid connection ID. Specify a valid connection Id.`; },
+ get INVALID_CONNECTION_ID(): string { return `${errorPrefix()} Initialization failed. Invalid connection ID. Specify connection Id as a string.`; },
+ get EMPTY_CONNECTION_URL(): string { return `${errorPrefix()} Initialization failed. Invalid connection URL. Specify a valid connection Url.`; },
+ get INVALID_CONNECTION_URL(): string { return `${errorPrefix()} Initialization failed. Invalid connection URL. Specify connection Url as a valid url.`; },
+
+ get VAULT_ID_EXITS_IN_CONFIG_LIST(): string { return `${errorPrefix()} Validation error. %s1 already exists in the config list. Specify a new vaultId.`; },
+ get CONNECTION_ID_EXITS_IN_CONFIG_LIST(): string { return `${errorPrefix()} Validation error. %s1 already exists in the config list. Specify a new vaultId.`; },
+ get VAULT_ID_NOT_IN_CONFIG_LIST(): string { return `${errorPrefix()} Validation error. %s1 is missing from the config. Specify the vaultId's from config.`; },
+ get CONNECTION_ID_NOT_IN_CONFIG_LIST(): string { return `${errorPrefix()} Validation error. %s1 is missing from the config. Specify the connectionIds from config.`; },
+
+ get EMPTY_CREDENTIALS(): string { return `${errorPrefix()} Validation error. Invalid credentials. Credentials must not be empty.`; },
+ get INVALID_CREDENTIALS(): string { return `${errorPrefix()} Validation error. Invalid credentials. Specify a valid credentials.`; },
+ get CREDENTIALS_WITH_NO_VALID_KEY(): string { return `${errorPrefix()} Validation error. Invalid credentials. Credentials must include one of the following: { apiKey, token, credentials, path }.`; },
+ get MULTIPLE_CREDENTIALS_PASSED(): string { return `${errorPrefix()} Validation error. Multiple credentials provided. Specify only one of the following: { apiKey, token, credentials, path }.`; },
+ get INVALID_CREDENTIALS_WITH_ID(): string { return `${errorPrefix()} Validation error. Invalid credentials. Credentials must include one of the following: { apiKey, token, credentials, path } for %s1 with %s2 %s3.`; },
+ get MULTIPLE_CREDENTIALS_PASSED_WITH_ID(): string { return `${errorPrefix()} Validation error. Invalid credentials.Specify only one of the following: { apiKey, token, credentials, path } for %s1 with %s2 %s3.`; },
+
+ get FILE_NOT_FOUND(): string { return `${errorPrefix()} Validation error. Credential file not found at %s1. Verify the file path.`; },
+ get INVALID_JSON_FILE(): string { return `${errorPrefix()} Validation error. File at %s1 is not in valid JSON format. Verify the file contents.`; },
+
+ get EMPTY_CREDENTIALS_STRING(): string { return `${errorPrefix()} Validation error. Invalid credentials. Specify a valid credentials.`; },
+ get INVALID_CREDENTIALS_STRING(): string { return `${errorPrefix()} Validation error. Invalid credentials. Specify credentials as a string.`; },
+
+ get MISSING_PRIVATE_KEY(): string { return `${errorPrefix()} Validation error. Unable to read private key in credentials. Verify your private key.`; },
+ get MISSING_CLIENT_ID(): string { return `${errorPrefix()} Validation error. Unable to read client ID in credentials. Verify your client ID.`; },
+ get MISSING_KEY_ID(): string { return `${errorPrefix()} Validation error. Unable to read key ID in credentials. Verify your key ID.`; },
+ get MISSING_TOKEN_URI(): string { return `${errorPrefix()} Validation error. Unable to read token URI in credentials. Verify your token URI.`; },
+
+ get INVALID_ROLES_KEY_TYPE(): string { return `${errorPrefix()} Validation error. Invalid roles. Specify roles as an array.`; },
+ get INVALID_CONTEXT(): string { return `${errorPrefix()} Validation error. Invalid context. Specify context as a string.`; },
+ get EMPTY_ROLES(): string { return `${errorPrefix()} Validation error. Invalid roles. Specify at least one role.`; },
+
+ get INVALID_JSON_FORMAT(): string { return `${errorPrefix()} Validation error. Credentials is not in valid JSON format. Verify the credentials.`; },
+
+ get EMPTY_DATA_TOKENS(): string { return `${errorPrefix()} Validation error. Invalid data tokens. Specify valid data tokens.`; },
+ get DATA_TOKEN_KEY_TYPE(): string { return `${errorPrefix()} Validation error. Invalid data tokens. Specify data token as an string array.`; },
+ get TIME_TO_LIVE_KET_TYPE(): string { return `${errorPrefix()} Validation error. Invalid time to live. Specify time to live parameter as an string.`; },
+
+ get INVALID_DELETE_IDS_INPUT(): string { return `${errorPrefix()} Validation error. Invalid delete ids type in delete request. Specify delete ids as a string array.`; },
+ get EMPTY_DELETE_IDS(): string { return `${errorPrefix()} Validation error. Delete ids array cannot be empty. Specify id's in delete request.`; },
+ get INVALID_ID_IN_DELETE(): string { return `${errorPrefix()} Validation error. Invalid type of id passed in delete request. Id must be of type string at index %s1.`; },
+ get INVALID_DELETE_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid delete request. Specify a valid delete request.`; },
+ get EMPTY_ID_IN_DELETE(): string { return `${errorPrefix()} Validation error. Id cannot be empty in delete request. Specify a valid id.`; },
+
+ get MISSING_REDACTION_TYPE_IN_DETOKENIZE(): string { return `${errorPrefix()} Validation error. Redaction type cannot be empty in detokenize request. Specify the redaction type.`; },
+ get INVALID_REDACTION_TYPE_IN_DETOKENIZE(): string { return `${errorPrefix()} Validation error. Invalid redaction type in detokenize request. Specify a redaction type.`; },
+ get INVALID_TOKENS_TYPE_IN_DETOKENIZE(): string { return `${errorPrefix()} Validation error. Invalid tokens type in detokenize request. Specify tokens as a string array.`; },
+ get EMPTY_TOKENS_IN_DETOKENIZE(): string { return `${errorPrefix()} Validation error. Tokens array cannot be empty. Specify token's in detokenize request.`; },
+ get EMPTY_TOKEN_IN_DETOKENIZE(): string { return `${errorPrefix()} Validation error. Token cannot be empty in detokenize request. Specify a valid token at index %s1.`; },
+ get INVALID_TOKEN_IN_DETOKENIZE(): string { return `${errorPrefix()} Validation error. Invalid type of token passed in detokenize request. token must be of type string at index %s1.`; },
+ get INVALID_DETOKENIZE_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid detokenize request. Specify a valid detokenize request.`; },
+
+ get MISSING_VALUES_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Values cannot be empty in tokenize request. Specify valid values.`; },
+ get INVALID_VALUES_TYPE_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Invalid values type in tokenize request. Specify valid values of type array.`; },
+ get EMPTY_VALUES_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Values array cannot be empty. Specify value's in tokenize request.`; },
+ get EMPTY_DATA_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Data cannot be empty in tokenize request. Specify a valid data at index %s1.`; },
+ get INVALID_DATA_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Invalid Data. Specify a valid data at index %s1.`; },
+ get INVALID_COLUMN_GROUP_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Invalid type of column group passed in tokenize request. Column group must be of type string at index %s1.`; },
+ get INVALID_VALUE_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Invalid type of value passed in tokenize request. Value must be of type string at index %s1.`; },
+ get INVALID_TOKENIZE_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid tokenize request. Specify a valid tokenize request.`; },
+ get EMPTY_COLUMN_GROUP_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Column group cannot be empty in tokenize request. Specify a valid column group at index %s1.`; },
+ get EMPTY_VALUE_IN_TOKENIZE(): string { return `${errorPrefix()} Validation error. Value cannot be empty in tokenize request. Specify a valid value at index %s1.`; },
+
+ get INVALID_RECORD_IN_INSERT(): string { return `${errorPrefix()} Validation error. Invalid data in insert request. data must be of type object at index %s1.`; },
+ get INVALID_RECORD_IN_UPDATE(): string { return `${errorPrefix()} Validation error. Invalid data in update request. data must be of type object.`; },
+ get EMPTY_RECORD_IN_INSERT(): string { return `${errorPrefix()} Validation error. Data cannot be empty in insert request. Specify valid data at index %s1.`; },
+ get INVALID_INSERT_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid insert request. Specify a valid insert request.`; },
+ get INVALID_TYPE_OF_DATA_IN_INSERT(): string { return `${errorPrefix()} Validation error. Invalid type of data in insert request. Specify data as a object array.`; },
+ get EMPTY_DATA_IN_INSERT(): string { return `${errorPrefix()} Validation error. Data array cannot be empty. Specify data in insert request.`; },
+
+ get INVALID_QUERY_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid query request. Specify a valid query request.`; },
+ get EMPTY_QUERY(): string { return `${errorPrefix()} Validation error. Query cannot be empty in query request. Specify a valid query.`; },
+ get INVALID_QUERY(): string { return `${errorPrefix()} Validation error. Invalid query in query request. Specify a valid query.`; },
+
+ get INVALID_FILE_UPLOAD_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid file upload request. Specify a valid file upload request.`; },
+ get MISSING_TABLE_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Table name cannot be empty in file upload request. Specify table name as a string.`; },
+ get INVALID_TABLE_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Invalid table name in file upload request. Specify a valid table name.`; },
+ get MISSING_SKYFLOW_ID_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Skyflow id cannot be empty in file upload request. Specify a valid skyflow Id as string.`; },
+ get INVALID_SKYFLOW_ID_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Invalid skyflow Id in file upload request. Specify a valid skyflow Id.`; },
+ get MISSING_COLUMN_NAME_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Column name cannot be empty in file upload request. Specify a valid column name as string.`; },
+ get INVALID_COLUMN_NAME_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Invalid column name in file upload request. Specify a valid column name.`; },
+ get MISSING_FILE_PATH_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. File path cannot be empty in file upload request. Specify a valid file path as string.`; },
+ get INVALID_FILE_PATH_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Invalid file path in file upload request. Specify a valid file path.`; },
+ get MISSING_FILE_SOURCE_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Provide exactly one of filePath, base64, or fileObject.`; },
+ get MISSING_FILE_NAME_FOR_BASE64(): string { return `${errorPrefix()} Validation error. File name is required when providing a base64 string`; },
+ get INVALID_FILE_OBJECT_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Invalid file object in file upload request. Specify a valid file object.`; },
+ get MISSING_FILE_NAME_IN_FILE_OBJECT(): string { return `${errorPrefix()} Validation error. File name is required when providing a file object. Specify a valid file name.`; },
+ get INVALID_BASE64_IN_UPLOAD_FILE(): string { return `${errorPrefix()} Validation error. Invalid base64 string in file upload request. Specify a valid base64 string.`; },
+
+ get MISSING_SKYFLOW_ID_IN_UPDATE(): string { return `${errorPrefix()} Validation error. Skyflow id name cannot be empty in update request. Specify a skyflow Id name as string.`; },
+ get INVALID_SKYFLOW_ID_IN_UPDATE(): string { return `${errorPrefix()} Validation error. Invalid skyflow Id in update request. Specify a valid skyflow Id.`; },
+ get INVALID_TYPE_OF_UPDATE_DATA(): string { return `${errorPrefix()} Validation error. Invalid update data in update request. Specify a valid update data as array of object.`; },
+ get EMPTY_UPDATE_DATA(): string { return `${errorPrefix()} Validation error. Update data cannot be empty in update request. Specify a valid update data.`; },
+ get INVALID_UPDATE_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid update request. Specify a valid update request.`; },
+ get INVALID_DATA_IN_UPDATE(): string { return `${errorPrefix()} Validation error. Invalid data in update request. data must be of type object at index %s1.`; },
+ get EMPTY_DATA_IN_UPDATE(): string { return `${errorPrefix()} Validation error. Data cannot be empty in update request. Specify a valid data at index %s1.`; },
+ get INVALID_UPDATE_TOKENS(): string { return `${errorPrefix()} Validation error. Invalid tokens. Specify valid tokens as object.`; },
+ get INVALID_TOKEN_IN_UPDATE(): string { return `${errorPrefix()} Validation error. Invalid tokens. Specify valid tokens as key value pairs.`; },
+
+ get EMPTY_TABLE_NAME(): string { return `${errorPrefix()} Validation error. Table name cannot be empty. Specify a valid table name.`; },
+ get INVALID_TABLE_NAME(): string { return `${errorPrefix()} Validation error. Invalid table name. Specify a valid table name as string.`; },
+ get EMPTY_REDACTION_TYPE(): string { return `${errorPrefix()} Validation error. Redaction type cannot be empty. Specify a valid redaction type.`; },
+ get INVALID_REDACTION_TYPE(): string { return `${errorPrefix()} Validation error. Invalid redaction type. Specify a valid redaction type.`; },
+
+ get INVALID_TYPE_OF_IDS(): string { return `${errorPrefix()} Validation error. Invalid ids passed in get request. Specify valid ids as array of string.`; },
+ get EMPTY_IDS_IN_GET(): string { return `${errorPrefix()} Validation error. Ids cannot be empty in get request. Specify valid ids.`; },
+ get EMPTY_ID_IN_GET(): string { return `${errorPrefix()} Validation error. Id cannot be empty. Specify a valid Id at index %s1.`; },
+ get INVALID_ID_IN_GET(): string { return `${errorPrefix()} Validation error. Invalid Id. Specify a valid Id at index %s1 as string.`; },
+ get INVALID_GET_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid get request. Specify a valid get request.`; },
+
+ get EMPTY_COLUMN_NAME(): string { return `${errorPrefix()} Validation error. Column name cannot be empty. Specify a valid column name.`; },
+ get INVALID_COLUMN_NAME(): string { return `${errorPrefix()} Validation error. Invalid column name. Specify a valid column name as string.`; },
+ get INVALID_COLUMN_VALUES(): string { return `${errorPrefix()} Validation error. Invalid column values. Specify valid column values as string array.`; },
+ get EMPTY_COLUMN_VALUES(): string { return `${errorPrefix()} Validation error. Column values cannot be empty. Specify valid column values.`; },
+ get EMPTY_COLUMN_VALUE(): string { return `${errorPrefix()} Validation error. Column value cannot be empty. Specify a valid column value at index %s1.`; },
+ get INVALID_COLUMN_VALUE(): string { return `${errorPrefix()} Validation error. Invalid column value. Specify a valid column value at index %s1 as string.`; },
+ get INVALID_GET_COLUMN_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid get column request. Specify a valid get column request.`; },
+
+ get EMPTY_URL(): string { return `${errorPrefix()} Validation error. Url cannot be empty. Specify a valid url.`; },
+ get INVALID_URL(): string { return `${errorPrefix()} Validation error. Invalid url. Specify a valid url as a string.`; },
+ get EMPTY_METHOD_NAME(): string { return `${errorPrefix()} Validation error. Method name cannot be empty. Specify a valid method name.`; },
+ get INVALID_METHOD_NAME(): string { return `${errorPrefix()} Validation error. Invalid method name. Specify a valid method name as a string.`; },
+
+ get EMPTY_PATH_PARAMS(): string { return `${errorPrefix()} Validation error. Path params cannot be empty. Specify valid path params.`; },
+ get INVALID_PATH_PARAMS(): string { return `${errorPrefix()} Validation error. Invalid path params. Specify valid path params.`; },
+ get EMPTY_QUERY_PARAMS(): string { return `${errorPrefix()} Validation error. Query params cannot be empty. Specify valid query params.`; },
+ get INVALID_QUERY_PARAMS(): string { return `${errorPrefix()} Validation error. Invalid query params. Specify valid query params.`; },
+ get EMPTY_BODY(): string { return `${errorPrefix()} Validation error. Body cannot be empty. Specify a valid body.`; },
+ get INVALID_BODY(): string { return `${errorPrefix()} Validation error. Invalid body. Specify a valid body.`; },
+ get EMPTY_HEADERS(): string { return `${errorPrefix()} Validation error. Headers cannot be empty. Specify valid headers.`; },
+ get INVALID_HEADERS(): string { return `${errorPrefix()} Validation error. Invalid headers. Specify valid headers.`; },
+
+ get INVALID_INVOKE_CONNECTION_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid invoke connection request. Specify a valid get invoke connection request.`; },
+ get ERROR_OCCURRED(): string { return `${errorPrefix()} API error. Error occurred.`; },
+
+ get EMPTY_INSERT_TOKEN(): string { return `${errorPrefix()} Validation error. Tokens object cannot be empty. Specify a valid tokens object at index %s1.`; },
+ get INVALID_INSERT_TOKEN(): string { return `${errorPrefix()} Validation error. Invalid tokens object. Specify a valid tokens object at index %s1.`; },
+ get INVALID_INSERT_TOKENS(): string { return `${errorPrefix()} Validation error. Invalid tokens. Specify valid tokens as object array.`; },
+ get INVALID_TOKEN_MODE(): string { return `${errorPrefix()} Validation error. The token mode key has a value of type %s1. Specify token as type of TokenMode.`; },
+ get INVALID_HOMOGENEOUS(): string { return `${errorPrefix()} Validation error. The homogeneous key has a value of type %s1. Specify homogeneous as boolean.`; },
+ get INVALID_TOKEN_STRICT(): string { return `${errorPrefix()} Validation error. The tokenStrict key has a value of type %s1. Specify tokenStrict as boolean.`; },
+ get INVALID_CONTINUE_ON_ERROR(): string { return `${errorPrefix()} Validation error. The continueOnError key has a value of type %s1. Specify continueOnError as boolean.`; },
+ get INVALID_UPSERT(): string { return `${errorPrefix()} Validation error. The upsert key has a value of type %s1. Specify upsert as string.`; },
+ get INVALID_RETURN_TOKEN(): string { return `${errorPrefix()} Validation error. The returnToken key has a value of type %s1. Specify returnToken as boolean.`; },
+
+ get NO_TOKENS_WITH_TOKEN_MODE(): string { return `${errorPrefix()} Validation error. Tokens weren't specified for records while 'tokenMode' was ENABLE or ENABLE_STRICT`; },
+ get INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT(): string { return `${errorPrefix()} Validation error. 'tokenMode' is set to 'ENABLE_STRICT', but some fields are missing tokens. Specify tokens for all fields.`; },
+
+ get INVALID_DOWNLOAD_URL(): string { return `${errorPrefix()} Validation error. The downloadURL key has a value of type %s1. Specify downloadURL as string.`; },
+
+ get EMPTY_FIELD(): string { return `${errorPrefix()} Validation error. Filed value cannot be empty. Specify a valid filed value at index %s1.`; },
+ get INVALID_FIELD(): string { return `${errorPrefix()} Validation error. Invalid filed value. Specify a valid filed value at index %s1 as string.`; },
+
+ get INVALID_OFFSET(): string { return `${errorPrefix()} Validation error. The offset key has a value of type %s1. Specify offset as string.`; },
+ get INVALID_LIMIT(): string { return `${errorPrefix()} Validation error. The limit key has a value of type %s1. Specify limit as string.`; },
+
+ get INVALID_ORDER_BY(): string { return `${errorPrefix()} Validation error. The orderBy key has a value of type %s1. Specify orderBy as string.`; },
+ get INVALID_FIELDS(): string { return `${errorPrefix()} Validation error. The fields key has a value of type %s1. Specify fields as array of strings.`; },
+
+ get INVAILD_JSON_RESPONSE(): string { return `${errorPrefix()} Validation error. The invalid json response. Please reach out to skyflow using requestId - %s1.`; },
+
+ get EMPTY_VAULT_CLIENTS(): string { return `${errorPrefix()} Validation error. No vault config found. Please add a vault config`; },
+ get EMPTY_CONNECTION_CLIENTS(): string { return `${errorPrefix()} Validation error. No connection config found. Please add a connection config`; },
+
+ get GENERIC_API_ERROR(): string { return `${errorPrefix()} Validation error. Invalid configuration. Please add a valid vault configuration`; },
+ get INVALID_TEXT_IN_DEIDENTIFY(): string { return `${errorPrefix()} Validation error. The text field is required and must be a non-empty string. Specify a valid text.`; },
+ get INVALID_ENTITIES_IN_DEIDENTIFY(): string { return `${errorPrefix()} Validation error. The entities field must be an array of DetectEntities enums. Specify a valid entities.`; },
+ get INVALID_ALLOW_REGEX_LIST(): string { return `${errorPrefix()} Validation error. The allowRegexList field must be an array of strings. Specify a valid allowRegexList.`; },
+ get INVALID_RESTRICT_REGEX_LIST(): string { return `${errorPrefix()} Validation error. The restrictRegexList field must be an array of strings. Specify a valid restrictRegexList.`; },
+ get INVALID_TOKEN_FORMAT(): string { return `${errorPrefix()} Validation error. The tokenFormat key must be an instance of TokenFormat. Specify a valid token format.`; },
+ get INVALID_TRANSFORMATIONS(): string { return `${errorPrefix()} Validation error. The transformations key must be an instance of Transformations. Specify a valid transformations.`; },
+ get VAULT_TOKEN_FORMAT_NOT_ALLOWED_FOR_DEIDENTIFY_FILES(): string { return `${errorPrefix()} Validation error. Vault token format is not allowed for deidentify file request.`; },
+
+ get INVALID_TEXT_IN_REIDENTIFY(): string { return `${errorPrefix()} Validation error. The text field is required and must be a non-empty string. Specify a valid text.`; },
+ get INVALID_REDACTED_ENTITIES_IN_REIDENTIFY(): string { return `${errorPrefix()} Validation error. The redactedEntities field must be an array of DetectEntities enums. Specify a valid redactedEntities.`; },
+ get INVALID_MASKED_ENTITIES_IN_REIDENTIFY(): string { return `${errorPrefix()} Validation error. The maskedEntities field must be an array of DetectEntities enums. Specify a valid maskedEntities.`; },
+ get INVALID_PLAIN_TEXT_ENTITIES_IN_REIDENTIFY(): string { return `${errorPrefix()} Validation error. The plainTextEntities field must be an array of DetectEntities enums. Specify a valid plainTextEntities.`; },
+
+ get INVALID_DEIDENTIFY_FILE_REQUEST(): string { return `${errorPrefix()} Validation error. Invalid deidentify file request. Specify a valid deidentify file request.`; },
+ get INVALID_DEIDENTIFY_FILE_INPUT(): string { return `${errorPrefix()} Validation error. Invalid deidentify file input. Please provide either a file or a file path.`; },
+ get EMPTY_FILE_OBJECT(): string { return `${errorPrefix()} Validation error. File object cannot be empty. Specify a valid file object.`; },
+ get INVALID_FILE_FORMAT(): string { return `${errorPrefix()} Validation error. Invalid file format. Specify a valid file format.`; },
+ get MISSING_FILE_SOURCE(): string { return `${errorPrefix()} Validation error. Provide exactly one of filePath, base64, or fileObject.`; },
+ get INVALID_FILE_OBJECT(): string { return `${errorPrefix()} Validation error. Invalid file object. Specify a valid file object.`; },
+ get INVALID_BASE64_STRING(): string { return `${errorPrefix()} Validation error. Invalid base64 string. Specify a valid base64 string.`; },
+ get INVALID_DEIDENTIFY_FILE_OPTIONS(): string { return `${errorPrefix()} Validation error. Invalid deidentify file options. Specify a valid deidentify file options.`; },
+ get INVALID_ENTITIES(): string { return `${errorPrefix()} Validation error. Invalid entities. Specify valid entities as string array.`; },
+ get EMPTY_ENTITIES(): string { return `${errorPrefix()} Validation error. Entities cannot be empty. Specify valid entities.`; },
+ get EMPTY_ALLOW_REGEX_LIST(): string { return `${errorPrefix()} Validation error. Allow regex list cannot be empty. Specify valid allow regex list.`; },
+ get INVALID_ALLOW_REGEX(): string { return `${errorPrefix()} Validation error. Invalid allow regex. Specify valid allow regex at index %s1.`; },
+ get EMPTY_RESTRICT_REGEX_LIST(): string { return `${errorPrefix()} Validation error. Restrict regex list cannot be empty. Specify valid restrict regex list.`; },
+ get INVALID_RESTRICT_REGEX(): string { return `${errorPrefix()} Validation error. Invalid restrict regex. Specify valid restrict regex at index %s1.`; },
+ get INVALID_OUTPUT_PROCESSED_IMAGE(): string { return `${errorPrefix()} Validation error. Invalid output processed image. Specify valid output processed image as string.`; },
+ get INVALID_OUTPUT_OCR_TEXT(): string { return `${errorPrefix()} Validation error. Invalid output ocr text. Specify valid output ocr text as string.`; },
+ get INVALID_MASKING_METHOD(): string { return `${errorPrefix()} Validation error. Invalid masking method. Specify valid masking method as string.`; },
+ get INVALID_PIXEL_DENSITY(): string { return `${errorPrefix()} Validation error. Invalid pixel density. Specify valid pixel density as string.`; },
+ get INVALID_OUTPUT_TRANSCRIPTION(): string { return `${errorPrefix()} Validation error. Invalid output transcription. Specify valid output transcription as string.`; },
+ get INVALID_OUTPUT_PROCESSED_AUDIO(): string { return `${errorPrefix()} Validation error. Invalid output processed audio. Specify valid output processed audio as string.`; },
+ get INVALID_MAX_RESOLUTION(): string { return `${errorPrefix()} Validation error. Invalid max resolution. Specify valid max resolution as string.`; },
+ get INVALID_BLEEP(): string { return `${errorPrefix()} Validation error. Invalid bleep. Specify valid bleep as object.`; },
+ get INVALID_FILE_OR_ENCODED_FILE(): string { return `${errorPrefix()} . Error while decoding base64 and saving file`; },
+ get INVALID_FILE_TYPE(): string { return `${errorPrefix()} Validation error. Invalid file type. Specify a valid file type.`; },
+ get INVALID_DEIDENTIFY_FILE_PATH(): string { return `${errorPrefix()} Validation error. Invalid file path. Specify a valid file path.`; },
+ get INVALID_FILE_NAME(): string { return `${errorPrefix()} Validation error. Invalid file name. Specify a valid file name.`; },
+ get FILE_READ_ERROR(): string { return `${errorPrefix()} Validation error. Unable to read file. Verify the file path.`; },
+ get INVALID_BASE64_HEADER(): string { return `${errorPrefix()} Validation error. Invalid base64 header. Specify a valid base64 header.`; },
+ get INVALID_WAIT_TIME(): string { return `${errorPrefix()} Validation error. Invalid wait time. Specify a valid wait time as number and should not be greater than 20 secs.`; },
+ get INVALID_OUTPUT_DIRECTORY(): string { return `${errorPrefix()} Validation error. Invalid output directory. Specify a valid output directory as string.`; },
+ get INVALID_OUTPUT_DIRECTORY_PATH(): string { return `${errorPrefix()} Validation error. Invalid output directory path. Specify a valid output directory path as string.`; },
+ get EMPTY_RUN_ID(): string { return `${errorPrefix()} Validation error. Run id cannot be empty. Specify a valid run id.`; },
+ get INVALID_RUN_ID(): string { return `${errorPrefix()} Validation error. Invalid run id. Specify a valid run id as string.`; },
+ get INTERNAL_SERVER_ERROR(): string { return `${errorPrefix()}. Internal server error. %s1.`; },
+ get INVALID_XML_FORMAT(): string { return `${errorPrefix()} Validation error. Invalid XML format. Specify a valid XML format as string.`; },
+};
+
+export default errorMessages;
\ No newline at end of file
diff --git a/common/src/identity.ts b/common/src/identity.ts
new file mode 100644
index 00000000..a9453317
--- /dev/null
+++ b/common/src/identity.ts
@@ -0,0 +1,15 @@
+export interface SdkIdentity {
+ name: string;
+ version: string;
+}
+
+// Default identity = legacy product. Layers (skyvault/flowvault) override via setSdkIdentity().
+let current: SdkIdentity = { name: 'skyflow-node', version: '2.1.2' };
+
+export function setSdkIdentity(id: SdkIdentity): void {
+ current = { ...id };
+}
+
+export function getSdkIdentity(): SdkIdentity {
+ return { ...current };
+}
diff --git a/common/src/index.ts b/common/src/index.ts
new file mode 100644
index 00000000..7f8065e3
--- /dev/null
+++ b/common/src/index.ts
@@ -0,0 +1,82 @@
+// =============================================================================
+// skyflow-common: contract-agnostic shared core for the Skyflow Node SDKs.
+// Layers (skyvault/flowvault) consume this surface via `import { ... } from 'skyflow-common'`.
+// =============================================================================
+
+// --- Utils: enums, consts, helpers and shared interfaces ---------------------
+// Exposes Env, LogLevel, RequestMethod, RedactionType, OrderByEnum, TokenMode,
+// TokenType, MaskingMethod, DetectOutputTranscription, DetectEntities, MessageType,
+// AuthType, CONFIG, CONTROLLER_TYPES, TYPES, SKYFLOW, SDK, HTTP_STATUS_CODE,
+// CONTENT_TYPE, HTTP_HEADER, REQUEST (and other consts); printLog, parameterizedString,
+// generateSDKMetrics, getVaultURL, getConnectionBaseURL, getBearerToken, getToken,
+// validateToken, removeSDKVersion, getBaseUrl, fillUrlWithPathAndQueryParams,
+// isValidURL, objectToXML; plus the ISkyflowError, SkyflowRecordError, AuthInfo types.
+export * from "./utils";
+
+// --- Errors ------------------------------------------------------------------
+export { default as SkyflowError } from "./error";
+export { default as SKYFLOW_ERROR_CODE } from "./error/codes";
+export { default as errorMessages } from "./error/messages";
+export { default as logs } from "./utils/logs";
+
+// --- JWT helpers -------------------------------------------------------------
+export { isExpired, isTokenValid } from "./utils/jwt-utils";
+
+// --- Credentials & config ----------------------------------------------------
+export { default as Credentials } from "./config/credentials";
+export type {
+ ApiKeyCredentials,
+ TokenCredentials,
+ PathCredentials,
+ StringCredentials,
+} from "./config/credentials";
+export { default as VaultConfig } from "./config/vault";
+export { default as ConnectionConfig } from "./config/connection";
+
+// --- Shared, contract-agnostic types -----------------------------------------
+// SkyflowConfig, StringKeyValueMapType, FileInput, Filepath, FileObject,
+// InsertResponseType, GetResponseData, QueryResponseType, TokenizeRequestType,
+// IndexRange, ServiceAccountResponseError.
+export * from "./types";
+
+// --- Service account (token generation) --------------------------------------
+export {
+ generateBearerToken,
+ generateBearerTokenFromCreds,
+ generateSignedDataTokens,
+ generateSignedDataTokensFromCreds,
+} from "./service-account";
+export type {
+ BearerTokenOptions,
+ SignedDataTokensOptions,
+ GenerateTokenOptions,
+ SignedDataTokensResponse,
+ TokenResponse,
+} from "./service-account";
+
+// --- Generic validators ------------------------------------------------------
+export {
+ validateSkyflowConfig,
+ validateVaultConfig,
+ validateConnectionConfig,
+ validateUpdateVaultConfig,
+ validateUpdateConnectionConfig,
+ validateSkyflowCredentials,
+ validateCredentialsWithId,
+ isValidAPIKey,
+ isLogLevel,
+ isEnv,
+} from "./validations";
+
+// --- SDK identity ------------------------------------------------------------
+export { setSdkIdentity, getSdkIdentity } from "./identity";
+export type { SdkIdentity } from "./identity";
+
+// --- Base (contract-agnostic) model classes ----------------------------------
+export { BaseOptions } from "./base/options";
+export * from "./base/request";
+export * from "./base/response";
+
+// --- Facade base (shared Skyflow client scaffolding) -------------------------
+export { BaseSkyflow } from "./skyflow/base";
+export type { BaseVaultEntry, BaseVaultClientLike } from "./skyflow/base";
diff --git a/src/service-account/client/index.ts b/common/src/service-account/client/index.ts
similarity index 69%
rename from src/service-account/client/index.ts
rename to common/src/service-account/client/index.ts
index 834fca56..2c16cfdc 100644
--- a/src/service-account/client/index.ts
+++ b/common/src/service-account/client/index.ts
@@ -1,5 +1,5 @@
// imports
-import { Authentication } from "../../ _generated_/rest/api/resources/authentication/client/Client";
+import { Authentication } from "../../_generated_/rest/api/resources/authentication/client/Client";
class Client {
diff --git a/src/service-account/index.ts b/common/src/service-account/index.ts
similarity index 99%
rename from src/service-account/index.ts
rename to common/src/service-account/index.ts
index a68903e0..320884b8 100644
--- a/src/service-account/index.ts
+++ b/common/src/service-account/index.ts
@@ -1,13 +1,13 @@
import * as fs from 'fs';
import jwt from "jsonwebtoken";
-import { V1GetAuthTokenRequest, V1GetAuthTokenResponse } from '../ _generated_/rest/api';
+import { V1GetAuthTokenRequest, V1GetAuthTokenResponse } from '../_generated_/rest/api';
import { getBaseUrl, LogLevel, isValidURL, MessageType, parameterizedString, printLog, HTTP_HEADER, CONTENT_TYPE, HTTP_STATUS_CODE, JWT, ENCODING_TYPE } from '../utils';
import Client from './client';
import logs from '../utils/logs';
import SkyflowError from '../error';
import SKYFLOW_ERROR_CODE from '../error/codes';
-import { ServiceAccountResponseError } from '../vault/types';
-import { WithRawResponse } from '../ _generated_/rest/core';
+import { ServiceAccountResponseError } from '../types';
+import { WithRawResponse } from '../_generated_/rest/core';
function normalizeTokenOptions(options?: BearerTokenOptions): BearerTokenOptions | undefined {
if (!options) return options;
diff --git a/common/src/skyflow/base.ts b/common/src/skyflow/base.ts
new file mode 100644
index 00000000..6d899164
--- /dev/null
+++ b/common/src/skyflow/base.ts
@@ -0,0 +1,219 @@
+// =============================================================================
+// BaseSkyflow — the contract-agnostic facade base shared by every Skyflow SDK
+// (skyvault / flowvault), analogous to Java's `common` BaseSkyflow.
+//
+// It owns the vault-client registry, common credentials, log level, and the
+// vault-config / credential / log-level management methods. Each layer extends
+// it, implements `buildVaultEntry` to construct its own VaultClient + the
+// controllers it exposes, and adds its layer-specific accessors (vault(),
+// and for skyvault also detect()/connection()).
+// =============================================================================
+import {
+ CONFIG,
+ Env,
+ LogLevel,
+ MessageType,
+ parameterizedString,
+ printLog,
+ getVaultURL,
+} from "../utils";
+import {
+ isLogLevel,
+ validateSkyflowConfig,
+ validateVaultConfig,
+ validateUpdateVaultConfig,
+ validateSkyflowCredentials,
+} from "../validations";
+import SkyflowError from "../error";
+import SKYFLOW_ERROR_CODE from "../error/codes";
+import logs from "../utils/logs";
+import type Credentials from "../config/credentials";
+import type VaultConfig from "../config/vault";
+import type { SkyflowConfig } from "../types";
+
+/** Minimal surface each layer's VaultClient must expose for base management. */
+export interface BaseVaultClientLike {
+ setLogLevel(logLevel: LogLevel): void;
+ updateSkyflowCredentials(credentials?: Credentials): void;
+ updateClientConfig(
+ url: string,
+ vaultId: string,
+ individualCredentials?: Credentials,
+ skyflowCredentials?: Credentials,
+ logLevel?: LogLevel,
+ ): void;
+}
+
+/** A registry entry for one configured vault: its config, client and controllers. */
+export interface BaseVaultEntry {
+ config: VaultConfig;
+ client: TClient;
+}
+
+export abstract class BaseSkyflow {
+ protected vaultClients: { [vaultId: string]: TEntry } = {};
+
+ protected commonCredentials?: Credentials;
+
+ protected logLevel: LogLevel = LogLevel.ERROR;
+
+ /**
+ * Build the layer-specific entry (VaultClient + the controllers that layer
+ * exposes). Implementations may read `this.commonCredentials` / `this.logLevel`.
+ */
+ protected abstract buildVaultEntry(config: VaultConfig): TEntry;
+
+ /**
+ * Common initialization: validate config, set log level + credentials, and
+ * register the provided vault configs. Call from the subclass constructor
+ * (after super()). Connection configs, if any, are handled by the subclass.
+ */
+ protected initializeSkyflow(config: SkyflowConfig): void {
+ validateSkyflowConfig(config, this.logLevel);
+ printLog(logs.infoLogs.INITIALIZE_CLIENT, MessageType.LOG, this.logLevel);
+
+ if (config?.logLevel && !isLogLevel(config.logLevel)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_LOG_LEVEL);
+ }
+ this.logLevel = config.logLevel || LogLevel.ERROR;
+ printLog(
+ parameterizedString(logs.infoLogs.CURRENT_LOG_LEVEL, [this.logLevel]),
+ MessageType.LOG,
+ this.logLevel,
+ );
+
+ if (config?.skyflowCredentials) {
+ validateSkyflowCredentials(config.skyflowCredentials);
+ }
+ this.commonCredentials = config?.skyflowCredentials;
+
+ printLog(logs.infoLogs.VALIDATING_VAULT_CONFIG, MessageType.LOG, this.logLevel);
+ config.vaultConfigs?.forEach((vaultConfig) => this.addVaultConfig(vaultConfig));
+ }
+
+ addVaultConfig(config: VaultConfig): void {
+ validateVaultConfig(config, this.logLevel);
+ this.throwErrorIfVaultIdExists(config?.vaultId);
+ this.vaultClients[config.vaultId] = this.buildVaultEntry(config);
+ printLog(logs.infoLogs.CONTROLLER_INITIALIZED, MessageType.LOG, this.logLevel);
+ }
+
+ updateVaultConfig(config: VaultConfig): void {
+ validateUpdateVaultConfig(config, this.logLevel);
+ const existing = this.vaultClients[config.vaultId];
+ if (!existing) {
+ this.throwErrorForUnknownVaultId(config.vaultId);
+ return;
+ }
+ const updatedConfig = { ...existing.config, ...config };
+ const vaultUrl = getVaultURL(updatedConfig.clusterId, updatedConfig.env || Env.PROD);
+ existing.config = updatedConfig;
+ existing.client.updateClientConfig(
+ vaultUrl,
+ updatedConfig.vaultId,
+ updatedConfig.credentials,
+ this.commonCredentials,
+ this.logLevel,
+ );
+ }
+
+ getVaultConfig(vaultId: string): VaultConfig {
+ if (!vaultId) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_VAULT_ID_VALIDATION);
+ }
+ const existing = this.vaultClients[vaultId];
+ if (!existing) {
+ this.throwErrorForUnknownVaultId(vaultId);
+ }
+ return existing.config;
+ }
+
+ removeVaultConfig(vaultId: string): void {
+ if (!this.vaultClients[vaultId]) {
+ this.throwErrorForUnknownVaultId(vaultId);
+ return;
+ }
+ delete this.vaultClients[vaultId];
+ }
+
+ setLogLevel(logLevel: LogLevel): void {
+ if (logLevel && !isLogLevel(logLevel)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_LOG_LEVEL);
+ }
+ this.logLevel = logLevel;
+ this.applyLogLevel();
+ }
+
+ /** Apply the current log level to all clients. Subclasses extend to cover
+ * additional (e.g. connection) clients, then call super.applyLogLevel(). */
+ protected applyLogLevel(): void {
+ Object.values(this.vaultClients).forEach((entry) => entry.client.setLogLevel(this.logLevel));
+ }
+
+ updateLogLevel(logLevel: LogLevel): this {
+ this.setLogLevel(logLevel);
+ return this;
+ }
+
+ getLogLevel(): LogLevel {
+ return this.logLevel;
+ }
+
+ updateSkyflowCredentials(credentials: Credentials): void {
+ if (!credentials) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CREDENTIALS);
+ }
+ validateSkyflowCredentials(credentials);
+ this.commonCredentials = credentials;
+ this.applyCredentials();
+ }
+
+ /** Apply the current common credentials to all clients. Subclasses extend. */
+ protected applyCredentials(): void {
+ Object.values(this.vaultClients).forEach((entry) =>
+ entry.client.updateSkyflowCredentials(this.commonCredentials),
+ );
+ }
+
+ getSkyflowCredentials(): Credentials | undefined {
+ return this.commonCredentials;
+ }
+
+ /** Resolve a vault entry by id (or the first registered), with the common
+ * empty-registry / unknown-id errors. Layer accessors (vault/detect) use this. */
+ protected resolveVaultEntry(vaultId?: string): TEntry {
+ if (Object.keys(this.vaultClients).length === 0) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_VAULT_CLIENTS);
+ }
+ const clientId = vaultId || Object.keys(this.vaultClients)[0];
+ const entry = this.vaultClients[clientId];
+ if (entry) {
+ return entry;
+ }
+ this.throwErrorForUnknownVaultId(clientId);
+ // Unreachable: throwErrorForUnknownVaultId always throws.
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INTERNAL_SERVER_ERROR);
+ }
+
+ protected throwErrorIfVaultIdExists(id: string): void {
+ if (Object.keys(this.vaultClients).includes(id)) {
+ printLog(
+ parameterizedString(logs.infoLogs[`${CONFIG.VAULT_ID}_CONFIG_EXISTS`], [id]),
+ MessageType.ERROR,
+ this.logLevel,
+ );
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.VAULT_ID_EXITS_IN_CONFIG_LIST, [id]);
+ }
+ }
+
+ protected throwErrorForUnknownVaultId(id: string): void {
+ printLog(
+ parameterizedString(logs.infoLogs[`${CONFIG.VAULT_ID}_CONFIG_DOES_NOT_EXIST`], [id]),
+ MessageType.ERROR,
+ this.logLevel,
+ );
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.VAULT_ID_NOT_IN_CONFIG_LIST, [id]);
+ }
+}
+
+export default BaseSkyflow;
diff --git a/common/src/types/index.ts b/common/src/types/index.ts
new file mode 100644
index 00000000..1f381db2
--- /dev/null
+++ b/common/src/types/index.ts
@@ -0,0 +1,66 @@
+import { LogLevel } from "../utils";
+import ConnectionConfig from "../config/connection";
+import VaultConfig from "../config/vault";
+import Credentials from "../config/credentials";
+
+export interface SkyflowConfig {
+ vaultConfigs?: VaultConfig[];
+ connectionConfigs?: ConnectionConfig[];
+ skyflowCredentials?: Credentials;
+ logLevel?: LogLevel;
+}
+
+export type FileInput =
+ | Filepath
+ | FileObject
+
+export interface Filepath {
+ filePath: string;
+}
+
+export interface FileObject {
+ file: File;
+}
+
+export interface InsertResponseType {
+ skyflowId: string;
+ /** @deprecated Renamed to skyflowId. Will be removed in v3. */
+ skyflow_id?: string;
+ [key: string]: unknown;
+}
+
+export interface GetResponseData {
+ [key: string]: unknown;
+}
+
+export interface QueryResponseType {
+ [key: string]: unknown;
+}
+
+export interface StringKeyValueMapType {
+ [key: string]: object | string;
+}
+
+export interface TokenizeRequestType {
+ columnGroup: string;
+ value: string;
+}
+
+export interface IndexRange {
+ start?: number;
+ end?: number;
+}
+
+export type ServiceAccountResponseError = {
+ rawResponse?: {
+ headers?: {
+ get: (header: string) => string | undefined;
+ };
+ };
+ body: any;
+ response?: {
+ status?: number | string;
+ };
+ message: string;
+ [key: string]: any;
+};
diff --git a/src/utils/index.ts b/common/src/utils/index.ts
similarity index 97%
rename from src/utils/index.ts
rename to common/src/utils/index.ts
index 983aac2e..606f3575 100644
--- a/src/utils/index.ts
+++ b/common/src/utils/index.ts
@@ -1,16 +1,16 @@
import SkyflowError from "../error";
-import * as sdkDetails from "../../package.json";
+import { getSdkIdentity } from "../identity";
import { generateBearerToken, generateBearerTokenFromCreds } from "../service-account";
import type { BearerTokenOptions } from "../service-account";
-import Credentials, { ApiKeyCredentials, PathCredentials, StringCredentials, TokenCredentials } from "../vault/config/credentials";
+import Credentials, { ApiKeyCredentials, PathCredentials, StringCredentials, TokenCredentials } from "../config/credentials";
import dotenv from "dotenv";
import logs from "./logs";
import os from 'os';
import process from "process";
import SKYFLOW_ERROR_CODE from "../error/codes";
import { isExpired } from "./jwt-utils";
-import { isValidAPIKey } from "./validations";
-import { StringKeyValueMapType } from "../vault/types";
+import { isValidAPIKey } from "../validations";
+import { StringKeyValueMapType } from "../types";
dotenv.config();
@@ -517,7 +517,7 @@ export const printLog = (message: string, messageType: MessageType, logLevel: Lo
const {
showDebugLogs, showInfoLogs, showWarnLogs, showErrorLogs,
} = LogLevelOptions[logLevel];
- const version = sdkDetails.version ? `v${sdkDetails.version}` : '';
+ const version = getSdkIdentity().version ? `v${getSdkIdentity().version}` : '';
if (messageType === MessageType.LOG && showDebugLogs) {
// eslint-disable-next-line no-console
console.log(`DEBUG: [Skyflow Node SDK ${version}] ` + message);
@@ -548,7 +548,7 @@ export const generateSDKMetrics = (logLevel?: LogLevel) => {
let clientOSDetails = "";
let runtimeDetails = "";
try {
- sdkNameVersion = `${sdkDetails.name ? `${sdkDetails.name}@` : ""}${sdkDetails.version ? sdkDetails.version : ""
+ sdkNameVersion = `${getSdkIdentity().name ? `${getSdkIdentity().name}@` : ""}${getSdkIdentity().version ? getSdkIdentity().version : ""
}`;
} catch (err) {
printLog(
diff --git a/src/utils/jwt-utils/index.ts b/common/src/utils/jwt-utils/index.ts
similarity index 100%
rename from src/utils/jwt-utils/index.ts
rename to common/src/utils/jwt-utils/index.ts
diff --git a/src/utils/logs/index.ts b/common/src/utils/logs/index.ts
similarity index 100%
rename from src/utils/logs/index.ts
rename to common/src/utils/logs/index.ts
diff --git a/common/src/validations/index.ts b/common/src/validations/index.ts
new file mode 100644
index 00000000..c3fd4498
--- /dev/null
+++ b/common/src/validations/index.ts
@@ -0,0 +1,380 @@
+import { CONFIG, Env, isValidURL, LogLevel, MessageType, printLog, API_KEY } from "../utils";
+import SkyflowError from "../error";
+import SKYFLOW_ERROR_CODE from "../error/codes";
+import ConnectionConfig from "../config/connection";
+import Credentials, { ApiKeyCredentials, PathCredentials, StringCredentials, TokenCredentials } from "../config/credentials";
+import VaultConfig from "../config/vault";
+import { SkyflowConfig } from "../types";
+import * as fs from 'fs';
+import { isExpired } from "../utils/jwt-utils";
+import logs from "../utils/logs";
+
+export function isEnv(value?: string): boolean {
+ return value !== undefined && Object.values(Env).includes(value as Env);
+}
+
+export function isLogLevel(value?: string): boolean {
+ return value !== undefined && Object.values(LogLevel).includes(value as LogLevel);
+}
+
+export function isValidAPIKey(apiKey: string) {
+ if (!apiKey) {
+ return false;
+ }
+ if (apiKey && typeof apiKey === 'string' && apiKey.startsWith(API_KEY.PREFIX)) {
+ return true;
+ }
+ return false;
+};
+
+function isValidCredentialsString(credentialsString: string) {
+ if (!credentialsString || credentialsString === null || credentialsString === undefined) {
+ return false;
+ }
+ if (credentialsString && typeof credentialsString === 'string') {
+ try {
+ let credentialsObj = JSON.parse("{}")
+ const parsed = JSON.parse(credentialsString);
+ credentialsObj = {
+ ...parsed,
+ clientId: parsed.clientId ?? parsed.clientID,
+ keyId: parsed.keyId ?? parsed.keyID,
+ tokenUri: parsed.tokenUri ?? parsed.tokenURI,
+ };
+ if (credentialsObj?.clientId == null || credentialsObj?.keyId == null) {
+ return false;
+ }
+ return true;
+ } catch (err) {
+ return false;
+ }
+ }
+ return false;
+};
+
+function isValidPath(path: string) {
+ if (!path || path === null || path === undefined) {
+ return false;
+ }
+ if (path && typeof path === 'string' && fs.existsSync(path)) {
+ return true;
+ }
+ return false;
+};
+
+export const validateSkyflowConfig = (config: SkyflowConfig, logLevel: LogLevel = LogLevel.ERROR) => {
+ if (config) {
+ if (!Object.prototype.hasOwnProperty.call(config, 'vaultConfigs')) {
+ printLog(logs.errorLogs.VAULT_CONFIG_KEY_MISSING, MessageType.ERROR, logLevel);
+ }
+
+ const { vaultConfigs, connectionConfigs } = config;
+
+ // Count how many of the fields are defined
+ const definedFields = [vaultConfigs, connectionConfigs].filter(Boolean).length;
+
+ // If none are present
+ if (definedFields === 0) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_CONFIG);
+ }
+
+ if (config?.vaultConfigs && !Array.isArray(config.vaultConfigs)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TYPE_FOR_CONFIG, [CONFIG.VAULT])
+ }
+
+ if (config?.connectionConfigs && !Array.isArray(config?.connectionConfigs)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TYPE_FOR_CONFIG, [CONFIG.CONNECTION])
+ }
+
+ } else {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.CONFIG_MISSING);
+ }
+};
+
+
+export const validateCredentialsWithId = (credentials: Credentials, type: string, typeId: string, id: string, logLevel: LogLevel = LogLevel.ERROR) => {
+ if (!credentials) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CREDENTIALS_WITH_ID, [type, typeId, id]);
+ }
+
+ const isTokenCred = 'token' in credentials;
+ const isPathCred = 'path' in credentials;
+ const isStringCred = 'credentialsString' in credentials;
+ const isApiKeyCred = 'apiKey' in credentials;
+
+ // Check if exactly one credential type is provided
+ const definedTypes = [isTokenCred, isPathCred, isStringCred, isApiKeyCred].filter(Boolean).length;
+
+ if (definedTypes === 0) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CREDENTIALS_WITH_ID, [type, typeId, id]);
+ }
+
+ if (definedTypes > 1) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.MULTIPLE_CREDENTIALS_PASSED_WITH_ID, [type, typeId, id]);
+ }
+
+ // Validate TokenCredentials
+ if (isTokenCred) {
+ const tokenCred = credentials as TokenCredentials;
+ if (typeof tokenCred.token !== 'string' || isExpired(tokenCred.token)) {
+ printLog(logs.errorLogs.EMPTY_TOKEN_VALUE, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_BEARER_TOKEN_WITH_ID, [type, typeId, id]);
+ }
+ }
+
+ // Validate PathCredentials
+ if (isPathCred) {
+ const pathCred = credentials as PathCredentials;
+ if (typeof pathCred.path !== 'string' || !isValidPath(pathCred.path)) {
+ printLog(logs.errorLogs.EMPTY_CREDENTIALS_PATH, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_FILE_PATH_WITH_ID, [type, typeId, id]);
+ }
+ if (pathCred.roles !== undefined && !Array.isArray(pathCred.roles)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE, [type, typeId, id]);
+ }
+ if (pathCred.context !== undefined && (typeof pathCred.context !== 'string' && typeof pathCred.context !== 'object')) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONTEXT, [type, typeId, id]);
+ }
+ if(Object.prototype.hasOwnProperty.call(pathCred, 'tokenUri')) {
+ if (pathCred.tokenUri === undefined || typeof pathCred.tokenUri !== 'string' || !isValidURL(pathCred.tokenUri)) {
+ printLog(logs.errorLogs.INVALID_TOKEN_URI, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TOKEN_URI_WITH_ID, [type, typeId, id]);
+ }
+ }
+ }
+
+ // Validate StringCredentials
+ if (isStringCred) {
+ const stringCred = credentials as StringCredentials;
+ if (typeof stringCred.credentialsString !== 'string' || !isValidCredentialsString(stringCred.credentialsString)) {
+ printLog(logs.errorLogs.EMPTY_CREDENTIALS_STRING, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_PARSED_CREDENTIALS_STRING_WITH_ID, [type, typeId, id]);
+ }
+ if (stringCred.roles !== undefined && !Array.isArray(stringCred.roles)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE, [type, typeId, id]);
+ }
+ if (stringCred.context !== undefined && (typeof stringCred.context !== 'string' && typeof stringCred.context !== 'object')) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONTEXT, [type, typeId, id]);
+ }
+ if (Object.prototype.hasOwnProperty.call(stringCred, 'tokenUri')) {
+ if (stringCred.tokenUri === undefined || typeof stringCred.tokenUri !== 'string' || !isValidURL(stringCred.tokenUri)) {
+ printLog(logs.errorLogs.INVALID_TOKEN_URI, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TOKEN_URI_WITH_ID, [type, typeId, id]);
+ }
+ }
+ }
+
+ // Validate ApiKeyCredentials
+ if (isApiKeyCred) {
+ const apiKeyCred = credentials as ApiKeyCredentials;
+ if (typeof apiKeyCred.apiKey !== 'string' || !isValidAPIKey(apiKeyCred.apiKey)) {
+ printLog(logs.errorLogs.INVALID_KEY, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_KEY_WITH_ID, [type, typeId, id]);
+ }
+ }
+};
+
+export const validateVaultConfig = (vaultConfig: VaultConfig, logLevel: LogLevel = LogLevel.ERROR) => {
+ if (vaultConfig) {
+ if (!Object.prototype.hasOwnProperty.call(vaultConfig, 'vaultId')) {
+ printLog(logs.errorLogs.EMPTY_VAULT_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_VAULT_ID);
+ }
+ if (!vaultConfig.vaultId || typeof vaultConfig.vaultId !== 'string') {
+ printLog(logs.errorLogs.INVALID_VAULT_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID);
+ }
+ if (!Object.prototype.hasOwnProperty.call(vaultConfig, 'clusterId')) {
+ printLog(logs.errorLogs.EMPTY_CLUSTER_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CLUSTER_ID, [vaultConfig?.vaultId]);
+ }
+ if (!vaultConfig.clusterId || typeof vaultConfig.clusterId !== 'string') {
+ printLog(logs.errorLogs.INVALID_CLUSTER_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CLUSTER_ID, [vaultConfig?.vaultId]);
+ }
+ if (vaultConfig?.env && !isEnv(vaultConfig.env)) {
+ printLog(logs.errorLogs.INVALID_ENV, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_ENV, [vaultConfig?.vaultId]);
+ }
+ if (vaultConfig?.credentials) {
+ validateCredentialsWithId(vaultConfig.credentials, CONFIG.VAULT, CONFIG.VAULT_ID, vaultConfig.vaultId, logLevel);
+ }
+ } else {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_VAULT_CONFIG);
+ }
+};
+
+export const validateUpdateVaultConfig = (vaultConfig: VaultConfig, logLevel: LogLevel = LogLevel.ERROR) => {
+ if (vaultConfig) {
+ if (!Object.prototype.hasOwnProperty.call(vaultConfig, 'vaultId')) {
+ printLog(logs.errorLogs.EMPTY_VAULT_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_VAULT_ID);
+ }
+ if (!vaultConfig?.vaultId || typeof vaultConfig?.vaultId !== 'string') {
+ printLog(logs.errorLogs.INVALID_VAULT_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_VAULT_ID);
+ }
+ if (vaultConfig?.clusterId && typeof vaultConfig.clusterId !== 'string') {
+ printLog(logs.errorLogs.INVALID_CLUSTER_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CLUSTER_ID, [vaultConfig?.vaultId]);
+ }
+ if (vaultConfig?.env && !isEnv(vaultConfig.env)) {
+ printLog(logs.errorLogs.INVALID_ENV, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_ENV, [vaultConfig?.vaultId]);
+ }
+ if (vaultConfig?.credentials) {
+ validateCredentialsWithId(vaultConfig.credentials, CONFIG.VAULT, CONFIG.VAULT_ID, vaultConfig.vaultId, logLevel);
+ }
+ } else {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_VAULT_CONFIG);
+ }
+};
+
+export const validateSkyflowCredentials = (credentials: Credentials, logLevel: LogLevel = LogLevel.ERROR) => {
+ if (!credentials) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.CREDENTIALS_WITH_NO_VALID_KEY);
+ }
+
+ const isTokenCred = 'token' in credentials;
+ const isPathCred = 'path' in credentials;
+ const isStringCred = 'credentialsString' in credentials;
+ const isApiKeyCred = 'apiKey' in credentials;
+
+ // Check if exactly one credential type is provided
+ const definedTypes = [isTokenCred, isPathCred, isStringCred, isApiKeyCred].filter(Boolean).length;
+
+ if (definedTypes === 0) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.CREDENTIALS_WITH_NO_VALID_KEY);
+ }
+
+ if (definedTypes > 1) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.MULTIPLE_CREDENTIALS_PASSED);
+ }
+
+ // Validate TokenCredentials
+ if (isTokenCred) {
+ const tokenCred = credentials as TokenCredentials;
+ if (typeof tokenCred.token !== 'string' || isExpired(tokenCred.token)) {
+ printLog(logs.errorLogs.EMPTY_TOKEN_VALUE, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_BEARER_TOKEN);
+ }
+ }
+
+ // Validate PathCredentials
+ if (isPathCred) {
+ const pathCred = credentials as PathCredentials;
+ if (typeof pathCred.path !== 'string' || !isValidPath(pathCred.path)) {
+ printLog(logs.errorLogs.EMPTY_CREDENTIALS_PATH, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CREDENTIALS_FILE_PATH);
+ }
+ if (pathCred.roles !== undefined && !Array.isArray(pathCred.roles)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE);
+ }
+ if (pathCred.context !== undefined && (typeof pathCred.context !== 'string' && typeof pathCred.context !== 'object')) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONTEXT);
+ }
+
+ if(Object.prototype.hasOwnProperty.call(pathCred, 'tokenUri')) {
+ if (pathCred.tokenUri === undefined || typeof pathCred.tokenUri !== 'string' || !isValidURL(pathCred.tokenUri)) {
+ printLog(logs.errorLogs.INVALID_TOKEN_URI, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TOKEN_URI);
+ }
+ }
+ }
+
+ // Validate StringCredentials
+ if (isStringCred) {
+ const stringCred = credentials as StringCredentials;
+ if (typeof stringCred.credentialsString !== 'string' || !isValidCredentialsString(stringCred.credentialsString)) {
+ printLog(logs.errorLogs.EMPTY_CREDENTIALS_STRING, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_PARSED_CREDENTIALS_STRING);
+ }
+ if (stringCred.roles !== undefined && !Array.isArray(stringCred.roles)) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_ROLES_KEY_TYPE);
+ }
+ // validate both string | Record
+ if (stringCred.context !== undefined && (typeof stringCred.context !== 'string' && typeof stringCred.context !== 'object')) {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONTEXT);
+ }
+ if (Object.prototype.hasOwnProperty.call(stringCred, 'tokenUri')) {
+ if (stringCred.tokenUri === undefined || typeof stringCred.tokenUri !== 'string' || !isValidURL(stringCred.tokenUri)) {
+ printLog(logs.errorLogs.INVALID_TOKEN_URI, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TOKEN_URI);
+ }
+ }
+ }
+
+ // Validate ApiKeyCredentials
+ if (isApiKeyCred) {
+ const apiKeyCred = credentials as ApiKeyCredentials;
+ if (typeof apiKeyCred.apiKey !== 'string' || !isValidAPIKey(apiKeyCred.apiKey)) {
+ printLog(logs.errorLogs.INVALID_KEY, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_KEY);
+ }
+ }
+};
+
+export const validateConnectionConfig = (connectionConfig: ConnectionConfig, logLevel: LogLevel = LogLevel.ERROR) => {
+ if (connectionConfig) {
+ if (!Object.prototype.hasOwnProperty.call(connectionConfig, 'connectionId')) {
+ printLog(logs.errorLogs.EMPTY_CONNECTION_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_ID);
+ }
+
+ if (typeof connectionConfig?.connectionId !== 'string' || connectionConfig?.connectionId.trim().length === 0) {
+ printLog(logs.errorLogs.INVALID_CONNECTION_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID);
+ }
+
+ if (!Object.prototype.hasOwnProperty.call(connectionConfig, 'connectionUrl')) {
+ printLog(logs.errorLogs.EMPTY_CONNECTION_URL, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_URL);
+ }
+
+ if (typeof connectionConfig?.connectionUrl !== 'string' || connectionConfig?.connectionUrl.trim().length === 0) {
+ printLog(logs.errorLogs.INVALID_CONNECTION_URL, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL);
+ }
+
+ if (connectionConfig.connectionUrl && !isValidURL(connectionConfig.connectionUrl)) {
+ printLog(logs.errorLogs.INVALID_CONNECTION_URL_TYPE, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL);
+ }
+
+ if (connectionConfig?.credentials) {
+ validateCredentialsWithId(connectionConfig.credentials, CONFIG.CONNECTION, CONFIG.CONNECTION_ID, connectionConfig.connectionId, logLevel);
+ }
+ } else {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CONFIG)
+ }
+};
+
+export const validateUpdateConnectionConfig = (connectionConfig: ConnectionConfig, logLevel: LogLevel = LogLevel.ERROR) => {
+ if (connectionConfig) {
+ if (!Object.prototype.hasOwnProperty.call(connectionConfig, 'connectionId')) {
+ printLog(logs.errorLogs.EMPTY_CONNECTION_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_ID);
+ }
+
+ if (typeof connectionConfig?.connectionId !== 'string' || connectionConfig?.connectionId.trim().length === 0) {
+ printLog(logs.errorLogs.INVALID_CONNECTION_ID, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_ID);
+ }
+
+ if (connectionConfig?.connectionUrl && (typeof connectionConfig?.connectionUrl !== 'string' || connectionConfig?.connectionUrl.trim().length === 0)) {
+ printLog(logs.errorLogs.INVALID_CONNECTION_URL, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL);
+ }
+
+ if (connectionConfig?.connectionUrl && !isValidURL(connectionConfig.connectionUrl)) {
+ printLog(logs.errorLogs.INVALID_CONNECTION_URL_TYPE, MessageType.ERROR, logLevel);
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_CONNECTION_URL);
+ }
+
+ if (connectionConfig?.credentials) {
+ validateCredentialsWithId(connectionConfig.credentials, CONFIG.CONNECTION, CONFIG.CONNECTION_ID, connectionConfig.connectionId, logLevel);
+ }
+ } else {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CONFIG)
+ }
+};
diff --git a/test/demo-credentials/empty.json b/common/test/demo-credentials/empty.json
similarity index 100%
rename from test/demo-credentials/empty.json
rename to common/test/demo-credentials/empty.json
diff --git a/test/demo-credentials/invalidJson.json b/common/test/demo-credentials/invalidJson.json
similarity index 100%
rename from test/demo-credentials/invalidJson.json
rename to common/test/demo-credentials/invalidJson.json
diff --git a/test/demo-credentials/noClientId.json b/common/test/demo-credentials/noClientId.json
similarity index 100%
rename from test/demo-credentials/noClientId.json
rename to common/test/demo-credentials/noClientId.json
diff --git a/test/demo-credentials/noKeyId.json b/common/test/demo-credentials/noKeyId.json
similarity index 100%
rename from test/demo-credentials/noKeyId.json
rename to common/test/demo-credentials/noKeyId.json
diff --git a/test/demo-credentials/noPrivateKey.json b/common/test/demo-credentials/noPrivateKey.json
similarity index 100%
rename from test/demo-credentials/noPrivateKey.json
rename to common/test/demo-credentials/noPrivateKey.json
diff --git a/test/demo-credentials/noTokenURI.json b/common/test/demo-credentials/noTokenURI.json
similarity index 100%
rename from test/demo-credentials/noTokenURI.json
rename to common/test/demo-credentials/noTokenURI.json
diff --git a/test/demo-credentials/valid.json b/common/test/demo-credentials/valid.json
similarity index 100%
rename from test/demo-credentials/valid.json
rename to common/test/demo-credentials/valid.json
diff --git a/test/error/skyflow-error.test.js b/common/test/error/skyflow-error.test.js
similarity index 100%
rename from test/error/skyflow-error.test.js
rename to common/test/error/skyflow-error.test.js
diff --git a/test/service-account/client.test.js b/common/test/service-account/client.test.js
similarity index 78%
rename from test/service-account/client.test.js
rename to common/test/service-account/client.test.js
index 814b4cf2..5c8fbf37 100644
--- a/test/service-account/client.test.js
+++ b/common/test/service-account/client.test.js
@@ -1,11 +1,11 @@
-jest.mock('../../src/ _generated_/rest/api/resources/authentication/client/Client', () => ({
+jest.mock('../../src/_generated_/rest/api/resources/authentication/client/Client', () => ({
Authentication: jest.fn().mockImplementation((config) => ({ config })),
}));
const Client = require('../../src/service-account/client').default;
const {
Authentication,
-} = require('../../src/ _generated_/rest/api/resources/authentication/client/Client');
+} = require('../../src/_generated_/rest/api/resources/authentication/client/Client');
describe('Service Account Client', () => {
beforeEach(() => {
diff --git a/test/service-account/token.test.js b/common/test/service-account/token.test.js
similarity index 100%
rename from test/service-account/token.test.js
rename to common/test/service-account/token.test.js
diff --git a/test/vault/utils/jwt-utils/jwt.test.js b/common/test/utils/jwt.test.js
similarity index 93%
rename from test/vault/utils/jwt-utils/jwt.test.js
rename to common/test/utils/jwt.test.js
index 74e16598..e1c560be 100644
--- a/test/vault/utils/jwt-utils/jwt.test.js
+++ b/common/test/utils/jwt.test.js
@@ -1,5 +1,5 @@
import { jwtDecode } from 'jwt-decode';
-import { isTokenValid, isExpired } from '../../../../src/utils/jwt-utils';
+import { isTokenValid, isExpired } from '../../src/utils/jwt-utils';
jest.mock('jwt-decode');
diff --git a/test/vault/utils/utils.test.js b/common/test/utils/utils.test.js
similarity index 96%
rename from test/vault/utils/utils.test.js
rename to common/test/utils/utils.test.js
index d7887d66..aaa51926 100644
--- a/test/vault/utils/utils.test.js
+++ b/common/test/utils/utils.test.js
@@ -1,14 +1,17 @@
/* eslint-disable camelcase */
-import errorMessages from "../../../src/error/messages";
-import { Env, getConnectionBaseURL, getVaultURL, validateToken, isValidURL, fillUrlWithPathAndQueryParams, generateSDKMetrics, printLog, getToken, getBearerToken, getBaseUrl, removeSDKVersion, parameterizedString, MessageType, LogLevel, objectToXML } from "../../../src/utils";
+import errorMessages from "../../src/error/messages";
+import { Env, getConnectionBaseURL, getVaultURL, validateToken, isValidURL, fillUrlWithPathAndQueryParams, generateSDKMetrics, printLog, getToken, getBearerToken, getBaseUrl, removeSDKVersion, parameterizedString, MessageType, LogLevel, objectToXML } from "../../src/utils";
import { jwtDecode as jwt_decode } from 'jwt-decode';
import os from 'os';
-import { generateBearerTokenFromCreds, generateBearerToken } from '../../../src/service-account';
-import sdkDetails from '../../../package.json';
+import { generateBearerTokenFromCreds, generateBearerToken } from '../../src/service-account';
+import sdkDetails from '../../../skyvault/package.json';
+// Same identity module instance that src/utils closes over (loaded above), so
+// spying on it affects generateSDKMetrics/printLog even across jest.resetModules().
+const identityModule = require('../../src/identity');
jest.mock('jwt-decode');
-jest.mock('../../../src/service-account', () => ({
+jest.mock('../../src/service-account', () => ({
generateBearerTokenFromCreds: jest.fn(),
generateBearerToken: jest.fn(),
}));
@@ -303,17 +306,19 @@ describe('generateSDKMetrics With errors', () => {
Object.defineProperty(process, 'arch', { value: originalArch });
});
- test('should log error and set sdkNameVersion to empty if accessing sdkDetails throws an error', () => {
- // Override the sdkDetails module to throw an error
- jest.resetModules();
- Object.defineProperty(sdkDetails, 'name', {
- get: () => { throw new Error('mock error'); }
+ test('should log error and set sdkNameVersion to empty if accessing the SDK identity throws an error', () => {
+ // In skyflow-common the name/version come from getSdkIdentity(), not package.json.
+ // Make accessing `.name` throw so generateSDKMetrics falls into its catch block.
+ jest.spyOn(identityModule, 'getSdkIdentity').mockReturnValue({
+ get name() { throw new Error('mock error'); },
+ version: '2.1.2',
});
const metrics = generateSDKMetrics(logLevel);
// Ensure sdkNameVersion is set to an empty string
expect(metrics.sdk_name_version).toBe("");
+ jest.restoreAllMocks();
});
});
@@ -578,13 +583,11 @@ describe('printLog version fallback', () => {
jest.restoreAllMocks();
});
- test('uses empty version when sdkDetails.version is undefined', () => {
- const origDescriptor = Object.getOwnPropertyDescriptor(sdkDetails, 'version');
- Object.defineProperty(sdkDetails, 'version', { value: undefined, writable: true, configurable: true });
+ test('uses empty version when the SDK identity version is undefined', () => {
+ jest.spyOn(identityModule, 'getSdkIdentity').mockReturnValue({ name: 'skyflow-node', version: undefined });
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
printLog('test msg', MessageType.LOG, LogLevel.DEBUG);
expect(consoleSpy).toHaveBeenCalledWith('DEBUG: [Skyflow Node SDK ] test msg');
- Object.defineProperty(sdkDetails, 'version', origDescriptor);
});
});
@@ -593,15 +596,10 @@ describe('generateSDKMetrics branch coverage', () => {
jest.restoreAllMocks();
});
- test('uses empty strings when sdkDetails name and version are falsy', () => {
- const origName = Object.getOwnPropertyDescriptor(sdkDetails, 'name');
- const origVersion = Object.getOwnPropertyDescriptor(sdkDetails, 'version');
- Object.defineProperty(sdkDetails, 'name', { value: '', writable: true, configurable: true });
- Object.defineProperty(sdkDetails, 'version', { value: '', writable: true, configurable: true });
+ test('uses empty strings when the SDK identity name and version are falsy', () => {
+ jest.spyOn(identityModule, 'getSdkIdentity').mockReturnValue({ name: '', version: '' });
const metrics = generateSDKMetrics();
expect(metrics.sdk_name_version).toBe('');
- Object.defineProperty(sdkDetails, 'name', origName);
- Object.defineProperty(sdkDetails, 'version', origVersion);
});
test('uses empty string for clientDeviceModel when process.platform is undefined', () => {
@@ -626,7 +624,7 @@ describe('generateSDKMetrics branch coverage', () => {
});
describe('objectToXML', () => {
- const { objectToXML } = require('../../../src/utils');
+ const { objectToXML } = require('../../src/utils');
test('should convert simple object to XML with default root', () => {
const obj = { name: 'John', age: 30 };
@@ -897,7 +895,7 @@ describe('objectToXML', () => {
});
describe('objectToXML', () => {
- const { objectToXML } = require('../../../src/utils');
+ const { objectToXML } = require('../../src/utils');
test('should convert simple object to XML with default root', () => {
const obj = { name: 'John', age: 30 };
diff --git a/common/tsconfig.json b/common/tsconfig.json
new file mode 100644
index 00000000..1d272823
--- /dev/null
+++ b/common/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "sourceMap": true,
+ "declaration": true,
+ "declarationMap": true,
+ "stripInternal": true,
+ "noImplicitAny": false,
+ "target": "es6",
+ "lib": ["es2015", "es2017", "es2019", "dom"],
+ "esModuleInterop": true,
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "removeComments": false,
+ "allowSyntheticDefaultImports": true,
+ "allowJs": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": false,
+ "isolatedModules": false,
+ "noEmit": false,
+ "rootDir": "src",
+ "outDir": "lib",
+ "types": ["node"]
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": [
+ "node_modules",
+ "lib",
+ "src/_generated_/rest/Client.ts",
+ "src/_generated_/rest/index.ts"
+ ]
+}
diff --git a/docs/sdk-split-plan.md b/docs/sdk-split-plan.md
new file mode 100644
index 00000000..20b3584d
--- /dev/null
+++ b/docs/sdk-split-plan.md
@@ -0,0 +1,186 @@
+# Skyflow Node SDK — Split Architecture (`common` / `skyvault` / `flowvault`)
+
+## Context
+
+This repository historically shipped a single npm package, `skyflow-node`. To support a second
+backend contract (**FlowVault**, v2) as its own installable SDK without forking the shared
+plumbing, the repo is restructured into an **npm-workspaces monorepo** with three modules —
+`common`, `skyvault`, `flowvault` — mirroring the Skyflow **Java** SDK split
+(`common` / `skyvault` / `flowvault`).
+
+Which SDK an application gets is decided **solely by which npm package it installs**
+(`npm i skyflow-node` vs `npm i skyflow-flowvault-node`): no build flags, no runtime mode
+switches, no environment configuration.
+
+Design contract for the FlowVault surface:
+
+
+## Module layout
+
+```
+/ (workspace root — private, not published)
+├── common/ → package "skyflow-common" (private/internal; bundled into each published SDK)
+├── skyvault/ → package "skyflow-node" (2.x; the original SDK, backward compatible)
+├── flowvault/ → package "skyflow-flowvault-node" (1.x; the new v2 SDK)
+└── package.json (workspaces: [common, skyvault, flowvault]; shared devDeps; build/test fan-out)
+```
+
+**Dependency direction:** `skyvault → common ← flowvault`. `common` depends on neither layer and
+compiles & tests with neither present. A layer never imports the other layer.
+
+### `common` (contract-agnostic core)
+Everything that is not tied to a specific wire contract:
+- `error/` — `SkyflowError`, error codes, message catalog.
+- `utils/` — enums/consts, logging, SDK metrics, URL + auth helpers, JWT helpers.
+- `service-account/` — bearer-token / signed-data-token lifecycle (wired to the shared **auth**
+ generated client in `common/src/_generated_/`).
+- `config/` — `Credentials` union, `VaultConfig`, `ConnectionConfig`.
+- `validations/` — generic config/credential validators only.
+- `types/` — pure, contract-agnostic types.
+- `identity.ts` — the **mutable SDK identity** (`setSdkIdentity` / `getSdkIdentity`), default
+ `skyflow-node`.
+- `base/` — `BaseOptions` + `BaseRequest` / `BaseResponse` marker classes (shared
+ extension points).
+- `skyflow/base.ts` — **`BaseSkyflow`**, the shared facade base (vault-client registry, common
+ credentials, log level, and the `addVaultConfig`/`updateVaultConfig`/`getVaultConfig`/
+ `removeVaultConfig`/`setLogLevel`/`updateLogLevel`/`getLogLevel`/`updateSkyflowCredentials`/
+ `getSkyflowCredentials` methods). Each layer extends it, implements `buildVaultEntry` to construct
+ its own `VaultClient` + controllers, and adds its layer-specific accessors. Mirrors Java's
+ `common` `BaseSkyflow`.
+
+### `skyvault` (→ `skyflow-node`)
+The original contract, unchanged behavior: vault data plane (insert/get/update/delete/
+detokenize/tokenize/query/file-upload) + connections + detect. Its `Skyflow` **extends
+`BaseSkyflow`** (adding the detect controller per vault + the connection registry/`connection()`);
+its `VaultClient`, controllers and models import shared code from `skyflow-common`; its concrete
+model classes extend the `common` `Base*` classes; the per-request validators live here. It keeps
+its existing data-plane generated client under `skyvault/src/ _generated_`.
+
+### `flowvault` (→ `skyflow-flowvault-node`)
+The new v2 contract (unary only): insert/get/update/delete/query/detokenize/getTokens/uploadFiles/
+deleteFiles. Its `Skyflow` **extends `BaseSkyflow`** (just `buildVaultEntry` + `vault()`); its
+models/options extend the same `Base*` classes; it wires to its own generated client at
+`flowvault/_generated_/rest/src` (`.records`/`.tokens`/`.files`/`.query`). No connections/detect.
+
+## Consumer experience (one import per SDK)
+
+Each layer **re-exports `common`'s public surface** from its own `index.ts`, so applications
+install exactly one package and import everything from it — they never import `skyflow-common`
+directly and never install both SDKs. `skyflow-common` is an **unpublished** workspace package
+**bundled into each published tarball** at build time; `/** @internal */` + `stripInternal` keep
+internal symbols out of the published `.d.ts`, and only the public entry is exposed via each
+package's `"exports"` map.
+
+## SDK identity (self-reporting)
+
+`common` holds a mutable identity object. Each layer **stamps** it at client construction:
+- `skyvault` → `{ name: "skyflow-node", version: }` (also the default).
+- `flowvault` → `setSdkIdentity({ name: "skyflow-flowvault-node", version: })`.
+
+Telemetry (`generateSDKMetrics` → `sky-metadata` header, `sdk_name_version`) reads the live
+identity, so each SDK self-reports its own `name@version`.
+
+## Independent versioning & tagging
+
+Each published package has its own `version` and release tags:
+
+| Package | Version line | Git tag that triggers its release |
+| --- | --- | --- |
+| `skyflow-node` | 2.x (unchanged from today) | **plain semver** `x.y.z` (and `x.y.z-beta.n`) |
+| `skyflow-flowvault-node` | 1.x (new) | **prefixed** `flowvault/x.y.z` (and `flowvault/x.y.z-beta.n`) |
+
+npm resolves packages by name from the registry (not from git tags), so the prefixed
+`flowvault/*` tags are purely CI triggers — no distribution mirror repo is needed. The products
+are distinguished solely by package name.
+
+### Version-bump script
+`scripts/bump_version.sh [sha]` where `` is `skyflow-node` or
+`skyflow-flowvault-node`; it bumps the right package's `package.json` `version` (and, for internal
+builds, appends `-dev.`).
+
+## Release flow (CI)
+
+- **PR / push CI**: one command builds and tests **both** SDKs — the root `npm run build`
+ (`tsc -b` across the workspace) + `npm test` (fans out to each package).
+- **Public release**:
+ - plain-semver tag → publish **`skyflow-node`** to npm only.
+ - `flowvault/*` tag → publish **`skyflow-flowvault-node`** to npm only.
+- **Beta**: `x.y.z-beta.n` → `skyflow-node@beta`; `flowvault/x.y.z-beta.n` →
+ `skyflow-flowvault-node@beta`.
+- **Internal (JFrog)**: branch-namespaced builds publish `-dev.` to the private registry,
+ per product.
+
+## Build & packaging
+
+- **Dev/CI build** — TypeScript **project references**: the root `npm run build` (`tsc -b`) builds
+ `common` first, then `skyvault` and `flowvault` against `common`'s declarations (each to its own
+ `lib/`). `npm test` fans out across the three workspaces.
+- **Publish build** — each publishable package has a **tsup** bundle (`npm run build:dist`) that
+ **inlines `skyflow-common`** (`noExternal: ['skyflow-common']`) into a self-contained
+ `dist/index.js` + bundled `dist/index.d.ts`, keeping real npm deps external. `skyflow-common` is a
+ **devDependency** of each layer, so it never appears in the published `dependencies` (verified via
+ `npm pack`). `stripInternal` keeps `/** @internal */` symbols out of the published `.d.ts`, and
+ each package's `"exports"` map exposes only `.` (CJS `require` + `types`). Common's runtime deps
+ (`jsonwebtoken`, `jwt-decode`) are declared in each layer's `dependencies` since common is inlined.
+- **Verified**: `npm pack` → install the tarball into a fresh consumer → `require()` works for both
+ SDKs; skyvault keeps all 74 public type names and the `v2.1.2` prefix, flowvault self-reports
+ `v1.0.0`.
+
+### Minor packaging note
+The bundler materializes a handful of **type-only** exports (`Credentials`, `VaultConfig`,
+`ConnectionConfig`, the credential subtypes, the service-account option types, `SkyflowRecordError`)
+as inert `undefined` runtime keys on skyvault's CJS export object — because their source re-exports
+use value syntax (`export { X }`) rather than `export type { X }`. This is harmless (a type has no
+runtime value) and the full type surface is preserved in the `.d.ts`; marking those re-exports
+`export type` in `common`'s barrel + the layer barrels would remove the extra keys.
+
+## Generated code (maintainer-owned)
+
+The OpenAPI spec is split and the client/transport code is generated **per module**:
+`common/src/_generated_/` (auth), `skyvault/src/ _generated_` (v1 data plane),
+`flowvault/_generated_/rest/src` (v2 data plane). Generated directories are treated as **provided
+inputs** — not generated, moved, renamed, or hand-edited by the split. Where the hand-written
+layer needs something the generated surface doesn't provide, it is **flagged** for the maintainer
+to regenerate (see "Known flags" below).
+
+## Known flags (for the maintainer)
+
+1. **common auth generated `Client.ts` fails `strict`** — `common/src/_generated_/rest/Client.ts`
+ (a management-plane `patchV2VaultsId` stub) calls `encodeURIComponent(vaultId)` on a possibly
+ `undefined` `vaultId` (TS2345). It is unused by `common` (service-account uses the
+ `authentication` resource directly), so it + `rest/index.ts` are `exclude`d in
+ `common/tsconfig.json`. To compile them, the generated code needs a null-guard — a regeneration
+ concern, not a hand-edit.
+2. **Error-message version — resolved.** `error/codes` is now Proxy-wrapped so each code's `message`
+ resolves lazily from the identity-aware `errorMessages` at throw time. Each layer stamps its
+ identity from an in-source `version.ts` constant (kept in sync by the bump script), so error text
+ self-reports the live version: skyvault → `v2.1.2`, flowvault → `v1.0.0`. Note the error-prefix
+ *name* ("Skyflow Node SDK") is still a shared string (keeping skyvault backward-compatible with
+ its `removeSDKVersion` regex); the per-product *name* is reported in telemetry (`sdk_name_version`).
+3. **`src/ _generated_` leading space** — the legacy skyvault data-plane generated directory name
+ contains a leading space; it is preserved (not renamed) and remains wired into jest/eslint/
+ cspell/gitleaks patterns.
+
+### FlowVault contract — reconciled against the Java & Python flowvault SDKs
+
+The flowvault layer was first written to the frozen design doc, then **aligned to the Java and
+Python flowvault SDKs** (the authoritative cross-language contract). Final decisions:
+
+| Topic | Decision (aligned to Java/Python) |
+| --- | --- |
+| **Errors** | Throw a flat camelCase `SkyflowError` (`httpCode` from HTTP status, `requestId` from the `x-request-id` header, `grpcCode`/`httpStatus`/`message`/`details`). The wire body is snake_case under `error`; the SDK reads it and surfaces camelCase. No nested `error` key on the thrown object. Matches Java `SkyflowException`. |
+| **BYOT `tokens` (insert/update)** | **Kept** — Java flowvault's `InsertRequestRecord`/`UpdateRequestRecord` carry `tokens`. The Node generated `InsertRecordData`/`UpdateRecordData` now model `tokens?` (regenerated), so the SDK forwards it typed (no cast). ✅ resolved. |
+| **Detokenize response** | Read wire key `response[]`, expose as `records[]`. Matches Java/Python. |
+| **Delete response** | **No `tableName`** on `DeleteResponseRecord` (only `skyflowId`/`httpCode`/`error`/`requestId`). Matches Java `DeleteResponseRecord` and Python `DeleteResponseRecord`. |
+| **`uniqueValues` (get & delete)** | A **list of `{ column: value, … }` maps**, each sent as `{ data: {…} }`. (Not `{columnName, columnValues[]}`.) Matches Java `List