From 34cda0ccbae369617e7bf55644d44e698e113090 Mon Sep 17 00:00:00 2001 From: saileshwar-skyflow Date: Wed, 14 Oct 2026 21:10:24 +0530 Subject: [PATCH 1/4] SK-3225: Split SDK into common/skyvault/flowvault; add FlowVault Node SDK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure the single `skyflow-node` package into an npm-workspaces monorepo mirroring the Java SDK split (common / skyvault / flowvault). - common (private, bundled into each published package): the contract-agnostic core — errors, utils, jwt, logging, service-account auth, config/credentials, generic validations, mutable SDK identity, Base* request/response/options classes, the BaseSkyflow facade base, and the generated auth client. Builds and tests in isolation. - skyvault -> skyflow-node (2.x): the existing contract, public API unchanged (74 exports, byte-for-byte). Its Skyflow/models extend the common Base* classes; keeps connections, detect and its data-plane generated client. - flowvault -> skyflow-flowvault-node (1.x): new unary v2 contract (insert/get/update/delete/query/detokenize/getTokens/uploadFiles/deleteFiles) wired to the generated v2 client; reconciled with the Java/Python flowvault SDKs. Each layer re-exports common and bundles it (tsup) into a self-contained tarball (verified via npm pack); the mutable SDK identity makes each product self-report its own name@version. Independent versioning: plain-semver tags publish skyflow-node, flowvault/* tags publish skyflow-flowvault-node. Tests: common 232, skyvault 878 (backward compatibility preserved), flowvault 132 (~99.85% coverage). Per-package api-extractor contract snapshots gate the public API of both SDKs. PR CI builds and tests both SDKs with one command. Docs: docs/sdk-split-plan.md. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/beta-release.yml | 4 +- .github/workflows/common-ci.yml | 7 +- .github/workflows/common-release.yml | 90 +- .github/workflows/contract-tests.yml | 18 +- .github/workflows/flowvault-beta-release.yml | 18 + .github/workflows/flowvault-release.yml | 19 + .github/workflows/internal-release.yml | 9 +- .github/workflows/release.yml | 4 +- .gitignore | 5 +- api-extractor.json | 47 - common/.babelrc | 14 + common/babel.config.js | 32 + common/jest.config.js | 12 + common/package.json | 23 + common/src/_generated_/rest/Client.ts | 119 ++ .../src/_generated_/rest/api}/client/index.ts | 0 .../client/requests/PatchV2VaultsIdRequest.ts | 9 + .../rest/api/client/requests/index.ts | 1 + .../rest/api/errors/BadRequestError.ts | 18 + .../rest/api/errors/NotFoundError.ts | 18 + .../rest/api/errors/UnauthorizedError.ts | 18 + .../src/_generated_/rest/api/errors/index.ts | 3 + common/src/_generated_/rest/api/index.ts | 4 + .../resources/authentication/client/Client.ts | 137 ++ .../resources/authentication/client/index.ts | 0 .../client/requests/V1GetAuthTokenRequest.ts | 25 + .../authentication/client/requests/index.ts | 0 .../api/resources/authentication/index.ts | 0 .../_generated_/rest/api/resources/index.ts | 2 + .../rest/api/types/GooglerpcStatus.ts | 0 .../rest/api/types/ProtobufAny.ts | 0 .../rest/api/types/V1GetAuthTokenResponse.ts | 0 .../src/_generated_/rest/api/types/index.ts | 3 + .../_generated_}/rest/core/auth/BasicAuth.ts | 0 .../rest/core/auth/BearerToken.ts | 0 .../src/_generated_}/rest/core/auth/index.ts | 0 .../rest/core/fetcher/APIResponse.ts | 0 .../_generated_}/rest/core/fetcher/Fetcher.ts | 0 .../rest/core/fetcher/HttpResponsePromise.ts | 0 .../rest/core/fetcher/RawResponse.ts | 0 .../rest/core/fetcher/Supplier.ts | 0 .../rest/core/fetcher/createRequestUrl.ts | 0 .../rest/core/fetcher/getFetchFn.ts | 0 .../rest/core/fetcher/getHeader.ts | 0 .../rest/core/fetcher/getRequestBody.ts | 0 .../rest/core/fetcher/getResponseBody.ts | 0 .../_generated_}/rest/core/fetcher/index.ts | 0 .../rest/core/fetcher/makeRequest.ts | 0 .../rest/core/fetcher/requestWithRetries.ts | 0 .../_generated_}/rest/core/fetcher/signals.ts | 0 .../Node18UniversalStreamWrapper.ts | 259 ++++ .../stream-wrappers/NodePre18StreamWrapper.ts | 0 .../stream-wrappers/UndiciStreamWrapper.ts | 244 +++ .../stream-wrappers/chooseStreamWrapper.ts | 0 common/src/_generated_/rest/core/index.ts | 3 + .../src/_generated_}/rest/core/json.ts | 0 .../_generated_}/rest/core/runtime/index.ts | 0 .../_generated_}/rest/core/runtime/runtime.ts | 0 common/src/_generated_/rest/environments.ts | 10 + .../_generated_}/rest/errors/SkyflowError.ts | 0 .../rest/errors/SkyflowTimeoutError.ts | 0 .../src/_generated_}/rest/errors/index.ts | 0 .../src/_generated_}/rest/index.ts | 0 .../src/_generated_}/rest/version.ts | 0 common/src/base/options.ts | 11 + common/src/base/request.ts | 14 + common/src/base/response.ts | 14 + .../src}/config/connection/index.ts | 0 .../src}/config/credentials/index.ts | 0 .../src}/config/vault/index.ts | 2 +- {src => common/src}/error/codes/index.ts | 22 +- {src => common/src}/error/index.ts | 0 common/src/error/messages/index.ts | 252 +++ common/src/identity.ts | 15 + common/src/index.ts | 82 + .../src}/service-account/client/index.ts | 2 +- {src => common/src}/service-account/index.ts | 6 +- common/src/skyflow/base.ts | 219 +++ common/src/types/index.ts | 66 + {src => common/src}/utils/index.ts | 12 +- {src => common/src}/utils/jwt-utils/index.ts | 0 {src => common/src}/utils/logs/index.ts | 0 common/src/validations/index.ts | 380 +++++ .../test}/demo-credentials/empty.json | 0 .../test}/demo-credentials/invalidJson.json | 0 .../test}/demo-credentials/noClientId.json | 0 .../test}/demo-credentials/noKeyId.json | 0 .../test}/demo-credentials/noPrivateKey.json | 0 .../test}/demo-credentials/noTokenURI.json | 0 .../test}/demo-credentials/valid.json | 0 .../test}/error/skyflow-error.test.js | 0 .../test}/service-account/client.test.js | 4 +- .../test}/service-account/token.test.js | 0 .../test/utils}/jwt.test.js | 2 +- .../vault => common/test}/utils/utils.test.js | 44 +- common/tsconfig.json | 34 + docs/sdk-split-plan.md | 186 +++ flowvault/LICENSE | 21 + flowvault/README.md | 53 + flowvault/_generated_/rest/src/Client.ts | 142 ++ .../_generated_/rest/src/api}/client/index.ts | 0 .../client/requests/PatchV2VaultsIdRequest.ts | 9 + .../rest/src/api/client/requests/index.ts | 1 + .../rest/src/api/errors/BadRequestError.ts | 19 + .../rest/src/api/errors/ForbiddenError.ts | 19 + .../src}/api/errors/InternalServerError.ts | 0 .../rest/src/api/errors/NotFoundError.ts | 19 + .../src/api/errors/TooManyRequestsError.ts | 19 + .../rest/src/api/errors/UnauthorizedError.ts | 19 + .../_generated_/rest/src/api/errors/index.ts | 6 + flowvault/_generated_/rest/src/api/index.ts | 4 + .../src/api/resources/files/client/Client.ts | 290 ++++ .../src}/api/resources/files/client/index.ts | 0 .../client/requests/FileDeleteRequest.ts | 23 + .../client/requests/FileUploadRequest.ts | 28 + .../resources/files/client/requests/index.ts | 2 + .../rest/src/api/resources/files}/index.ts | 0 .../rest/src/api/resources/index.ts | 9 + .../src/api/resources/query/client/Client.ts | 154 ++ .../src/api/resources/query}/client/index.ts | 0 .../client/requests/ExecuteQueryRequest.ts | 17 + .../resources/query/client/requests/index.ts | 1 + .../rest/src/api/resources/query}/index.ts | 0 .../api/resources/records/client/Client.ts | 541 +++++++ .../api/resources/records}/client/index.ts | 0 .../records/client/requests/DeleteRequest.ts | 22 + .../records/client/requests/GetRequest.ts | 32 + .../records/client/requests/InsertRequest.ts | 83 + .../records/client/requests/UpdateRequest.ts | 48 + .../records/client/requests/index.ts | 4 + .../rest/src/api/resources/records}/index.ts | 0 .../records/types/UpdateRequestUpdateType.ts | 12 + .../src/api/resources/records/types/index.ts | 1 + .../src/api/resources/tokens/client/Client.ts | 265 ++++ .../src/api/resources/tokens}/client/index.ts | 0 .../client/requests/DetokenizeRequest.ts | 21 + .../requests/GetTokensFromValuesRequest.ts | 19 + .../resources/tokens/client/requests/index.ts | 2 + .../rest/src/api/resources/tokens}/index.ts | 0 .../rest/src/api/types/ColumnRedactions.ts | 10 + .../rest/src/api/types/DeleteResponse.ts | 10 + .../src/api/types/DeleteResponseObject.ts | 12 + .../rest/src/api/types/DetokenizeResponse.ts | 10 + .../src/api/types/DetokenizeResponseObject.ts | 19 + .../rest/src}/api/types/ErrorResponse.ts | 0 .../rest/src/api/types/ErrorResponseError.ts | 14 + .../api/types/ExecuteQueryRecordResponse.ts | 8 + .../src/api/types/ExecuteQueryResponse.ts | 11 + .../api/types/ExecuteQueryResponseMetadata.ts | 11 + .../rest/src/api/types/FileDeleteRecord.ts | 16 + .../rest/src/api/types/FileDeleteResponse.ts | 10 + .../src/api/types/FileDeleteResponseObject.ts | 16 + .../rest/src/api/types/FileUploadColumn.ts | 10 + .../rest/src/api/types/FileUploadRecord.ts | 14 + .../rest/src/api/types/FileUploadResponse.ts | 10 + .../src/api/types/FileUploadResponseObject.ts | 16 + .../rest/src/api/types/GetRequestData.ts | 18 + .../rest/src/api/types/GetResponse.ts | 10 + .../types/GetTokensFromValuesRequestObject.ts | 11 + .../api/types/GetTokensFromValuesResponse.ts | 10 + .../rest/src/api/types/GoogleProtobufValue.ts | 8 + .../rest/src/api/types/HttpCode.ts | 8 + .../rest/src/api/types/InsertRecordData.ts | 15 + .../rest/src/api/types/InsertResponse.ts | 10 + .../src/api/types/RecordResponseObject.ts | 20 + .../src/api/types/TokenGroupRedactions.ts | 10 + .../src/api/types/TokenizeResponseObject.ts | 17 + .../rest/src/api/types/UniqueValue.ts | 8 + .../rest/src/api/types/UpdateRecordData.ts | 18 + .../api/types/UpdateRecordDataUpdateType.ts | 12 + .../rest/src/api/types/UpdateResponse.ts | 10 + .../_generated_/rest/src/api/types/Upsert.ts | 15 + .../rest/src/api/types/UpsertUpdateType.ts | 12 + .../_generated_/rest/src/api/types/index.ts | 34 + .../rest/src/core/auth/BasicAuth.ts | 31 + .../rest/src/core/auth/BearerToken.ts | 15 + .../_generated_/rest/src/core/auth/index.ts | 2 + .../rest/src/core/fetcher/APIResponse.ts | 23 + .../rest/src/core/fetcher/Fetcher.ts | 151 ++ .../src/core/fetcher/HttpResponsePromise.ts | 116 ++ .../rest/src/core/fetcher/RawResponse.ts | 59 + .../rest/src/core/fetcher/Supplier.ts | 11 + .../rest/src/core/fetcher/createRequestUrl.ts | 10 + .../rest/src/core/fetcher/getFetchFn.ts | 25 + .../rest/src/core/fetcher/getHeader.ts | 8 + .../rest/src/core/fetcher/getRequestBody.ts | 16 + .../rest/src/core/fetcher/getResponseBody.ts | 34 + .../rest/src/core/fetcher/index.ts | 8 + .../rest/src/core/fetcher/makeRequest.ts | 44 + .../src/core/fetcher/requestWithRetries.ts | 33 + .../rest/src/core/fetcher/signals.ts | 38 + .../Node18UniversalStreamWrapper.ts | 259 ++++ .../stream-wrappers/NodePre18StreamWrapper.ts | 107 ++ .../stream-wrappers/UndiciStreamWrapper.ts | 244 +++ .../stream-wrappers/chooseStreamWrapper.ts | 34 + flowvault/_generated_/rest/src/core/index.ts | 3 + flowvault/_generated_/rest/src/core/json.ts | 27 + .../rest/src/core/runtime/index.ts | 1 + .../rest/src/core/runtime/runtime.ts | 131 ++ .../_generated_/rest/src/environments.ts | 10 + .../rest/src/errors/SkyflowError.ts | 55 + .../rest/src/errors/SkyflowTimeoutError.ts | 10 + .../_generated_/rest/src/errors/index.ts | 2 + flowvault/_generated_/rest/src/index.ts | 4 + flowvault/_generated_/rest/src/version.ts | 1 + flowvault/api-extractor.json | 32 + .../api-report/skyflow-flowvault-node.api.md | 571 +++++++ flowvault/jest.config.js | 19 + flowvault/package.json | 61 + flowvault/src/client/index.ts | 215 +++ flowvault/src/controller/vault/index.ts | 709 +++++++++ flowvault/src/index.ts | 67 + .../src/model/options/delete-files/index.ts | 10 + flowvault/src/model/options/delete/index.ts | 10 + .../src/model/options/detokenize/index.ts | 10 + .../src/model/options/get-tokens/index.ts | 10 + flowvault/src/model/options/get/index.ts | 57 + flowvault/src/model/options/insert/index.ts | 21 + flowvault/src/model/options/query/index.ts | 10 + flowvault/src/model/options/update/index.ts | 10 + .../src/model/options/upload-files/index.ts | 10 + .../src/model/request/delete-files/index.ts | 22 + flowvault/src/model/request/delete/index.ts | 43 + .../src/model/request/detokenize/index.ts | 32 + .../src/model/request/get-tokens/index.ts | 22 + flowvault/src/model/request/get/index.ts | 47 + flowvault/src/model/request/insert/index.ts | 32 + flowvault/src/model/request/query/index.ts | 21 + flowvault/src/model/request/update/index.ts | 32 + .../src/model/request/upload-files/index.ts | 22 + .../src/model/response/delete-files/index.ts | 14 + flowvault/src/model/response/delete/index.ts | 14 + .../src/model/response/detokenize/index.ts | 14 + .../src/model/response/get-tokens/index.ts | 14 + flowvault/src/model/response/get/index.ts | 14 + flowvault/src/model/response/insert/index.ts | 14 + flowvault/src/model/response/query/index.ts | 30 + flowvault/src/model/response/update/index.ts | 14 + .../src/model/response/upload-files/index.ts | 14 + flowvault/src/skyflow/index.ts | 56 + flowvault/src/types/index.ts | 175 +++ flowvault/src/validations/index.ts | 209 +++ flowvault/src/version.ts | 4 + flowvault/test/client/client.test.ts | 173 +++ .../test/controller/delete-files.test.ts | 93 ++ flowvault/test/controller/delete.test.ts | 56 + flowvault/test/controller/detokenize.test.ts | 83 + flowvault/test/controller/get-tokens.test.ts | 58 + flowvault/test/controller/get.test.ts | 110 ++ flowvault/test/controller/insert.test.ts | 113 ++ flowvault/test/controller/query.test.ts | 52 + flowvault/test/controller/update.test.ts | 60 + .../test/controller/upload-files.test.ts | 229 +++ flowvault/test/helpers.ts | 68 + flowvault/test/model/options.test.ts | 61 + flowvault/test/model/requests.test.ts | 123 ++ flowvault/test/model/responses.test.ts | 89 ++ flowvault/test/skyflow/skyflow.test.ts | 157 ++ flowvault/test/smoke.test.ts | 232 +++ .../test/validations/validations.test.ts | 253 +++ flowvault/tsconfig.dts.json | 32 + flowvault/tsconfig.json | 36 + flowvault/tsconfig.test.json | 19 + flowvault/tsup.config.ts | 19 + package-lock.json | 1373 ++++++++++++++++- package.json | 42 +- scripts/bump_version.sh | 70 +- skyvault/.babelrc | 14 + skyvault/LICENSE | 21 + skyvault/README.md | 1325 ++++++++++++++++ skyvault/api-extractor.json | 32 + .../api-report}/skyflow-node.api.md | 573 ++----- skyvault/babel.config.js | 32 + skyvault/jest.config.js | 19 + skyvault/package.json | 60 + .../src}/ _generated_/rest/Client.ts | 0 .../rest/api/errors/BadRequestError.ts | 0 .../rest/api/errors/InternalServerError.ts | 19 + .../rest/api/errors/NotFoundError.ts | 0 .../rest/api/errors/UnauthorizedError.ts | 0 .../ _generated_/rest/api/errors/index.ts | 0 .../src}/ _generated_/rest/api/index.ts | 0 .../rest/api/resources/audit/client/Client.ts | 0 .../rest/api/resources/audit}/client/index.ts | 0 .../AuditServiceListAuditEventsRequest.ts | 0 .../resources/audit/client/requests/index.ts | 0 .../rest/api/resources/audit}/index.ts | 0 ...stAuditEventsRequestFilterOpsActionType.ts | 0 ...EventsRequestFilterOpsContextAccessType.ts | 0 ...tEventsRequestFilterOpsContextActorType.ts | 0 ...itEventsRequestFilterOpsContextAuthMode.ts | 0 ...AuditEventsRequestFilterOpsResourceType.ts | 0 ...iceListAuditEventsRequestSortOpsOrderBy.ts | 0 .../rest/api/resources/audit/types/index.ts | 0 .../resources/authentication/client/Client.ts | 0 .../resources/authentication}/client/index.ts | 0 .../client/requests/V1GetAuthTokenRequest.ts | 0 .../authentication/client/requests/index.ts | 1 + .../api/resources/authentication}/index.ts | 0 .../api/resources/binLookup/client/Client.ts | 0 .../api/resources/binLookup/client/index.ts | 1 + .../client/requests/V1BinListRequest.ts | 0 .../binLookup/client/requests/index.ts | 0 .../rest/api/resources/binLookup/index.ts | 1 + .../rest/api/resources/files/client/Client.ts | 0 .../rest/api/resources/files/client/index.ts | 1 + .../client/requests/DeidentifyAudioRequest.ts | 0 .../requests/DeidentifyDocumentRequest.ts | 0 .../client/requests/DeidentifyFileRequest.ts | 0 .../client/requests/DeidentifyImageRequest.ts | 0 .../client/requests/DeidentifyPdfRequest.ts | 0 .../requests/DeidentifyPresentationRequest.ts | 0 .../requests/DeidentifySpreadsheetRequest.ts | 0 .../DeidentifyStructuredTextRequest.ts | 0 .../client/requests/DeidentifyTextRequest.ts | 0 .../files/client/requests/GetRunRequest.ts | 0 .../client/requests/ReidentifyFileRequest.ts | 0 .../resources/files/client/requests/index.ts | 0 .../rest/api/resources/files}/index.ts | 0 .../files/types/DeidentifyAudioRequestFile.ts | 0 .../DeidentifyAudioRequestFileDataFormat.ts | 0 ...identifyAudioRequestOutputTranscription.ts | 0 .../types/DeidentifyDocumentRequestFile.ts | 0 ...DeidentifyDocumentRequestFileDataFormat.ts | 0 .../files/types/DeidentifyFileRequestFile.ts | 0 .../DeidentifyFileRequestFileDataFormat.ts | 0 .../files/types/DeidentifyImageRequestFile.ts | 0 .../DeidentifyImageRequestFileDataFormat.ts | 0 .../DeidentifyImageRequestMaskingMethod.ts | 0 .../files/types/DeidentifyPdfRequestFile.ts | 0 .../DeidentifyPresentationRequestFile.ts | 0 ...entifyPresentationRequestFileDataFormat.ts | 0 .../types/DeidentifySpreadsheetRequestFile.ts | 0 ...dentifySpreadsheetRequestFileDataFormat.ts | 0 .../DeidentifyStructuredTextRequestFile.ts | 0 ...tifyStructuredTextRequestFileDataFormat.ts | 0 .../files/types/DeidentifyTextRequestFile.ts | 0 .../files/types/ReidentifyFileRequestFile.ts | 0 .../ReidentifyFileRequestFileDataFormat.ts | 0 .../types/ReidentifyFileRequestFormat.ts | 0 .../rest/api/resources/files/types/index.ts | 0 .../api/resources/guardrails/client/Client.ts | 0 .../api/resources/guardrails/client/index.ts | 1 + .../client/requests/CheckGuardrailsRequest.ts | 0 .../guardrails/client/requests/index.ts | 0 .../rest/api/resources/guardrails/index.ts | 1 + .../ _generated_/rest/api/resources/index.ts | 0 .../rest/api/resources/query/client/Client.ts | 0 .../rest/api/resources/query/client/index.ts | 1 + .../requests/QueryServiceExecuteQueryBody.ts | 0 .../resources/query/client/requests/index.ts | 0 .../rest/api/resources/query/index.ts | 1 + .../api/resources/records/client/Client.ts | 0 .../api/resources/records/client/index.ts | 1 + .../requests/FileServiceUploadFileRequest.ts | 0 .../RecordServiceBatchOperationBody.ts | 0 .../RecordServiceBulkDeleteRecordBody.ts | 0 .../RecordServiceBulkGetRecordRequest.ts | 0 .../requests/RecordServiceGetRecordRequest.ts | 0 .../requests/RecordServiceInsertRecordBody.ts | 0 .../requests/RecordServiceUpdateRecordBody.ts | 0 .../client/requests/UploadFileV2Request.ts | 0 .../records/client/requests/index.ts | 0 .../rest/api/resources/records}/index.ts | 0 ...ecordServiceBulkGetRecordRequestOrderBy.ts | 0 ...ordServiceBulkGetRecordRequestRedaction.ts | 0 .../RecordServiceGetRecordRequestRedaction.ts | 0 .../rest/api/resources/records/types/index.ts | 0 .../api/resources/strings/client/Client.ts | 0 .../api/resources/strings/client/index.ts | 1 + .../requests/DeidentifyStringRequest.ts | 0 .../requests/ReidentifyStringRequest.ts | 0 .../strings/client/requests/index.ts | 0 .../rest/api/resources/strings/index.ts | 2 + .../types/ReidentifyStringRequestFormat.ts | 0 .../rest/api/resources/strings/types/index.ts | 0 .../api/resources/tokens/client/Client.ts | 0 .../rest/api/resources/tokens/client/index.ts | 1 + .../client/requests/V1DetokenizePayload.ts | 0 .../client/requests/V1TokenizePayload.ts | 0 .../resources/tokens/client/requests/index.ts | 0 .../rest/api/resources/tokens/index.ts | 1 + .../ _generated_/rest/api/types/AllowRegex.ts | 0 .../api/types/AuditEventAuditResourceType.ts | 0 .../rest/api/types/AuditEventContext.ts | 0 .../rest/api/types/AuditEventData.ts | 0 .../rest/api/types/AuditEventHttpInfo.ts | 0 .../rest/api/types/BatchRecordMethod.ts | 0 .../rest/api/types/CheckGuardrailsResponse.ts | 0 .../CheckGuardrailsResponseValidation.ts | 0 .../rest/api/types/ConfigurationId.ts | 0 .../rest/api/types/ContextAccessType.ts | 0 .../rest/api/types/ContextAuthMode.ts | 0 .../rest/api/types/DeidentifyFileOutput.ts | 0 .../DeidentifyFileOutputProcessedFileType.ts | 0 .../rest/api/types/DeidentifyFileResponse.ts | 0 .../api/types/DeidentifyStatusResponse.ts | 0 .../DeidentifyStatusResponseOutputType.ts | 0 .../types/DeidentifyStatusResponseStatus.ts | 0 .../api/types/DeidentifyStringResponse.ts | 0 .../rest/api/types/DetectedEntity.ts | 0 .../DetokenizeRecordResponseValueType.ts | 0 .../rest/api/types/EntityLocation.ts | 0 .../ _generated_/rest/api/types/EntityType.ts | 0 .../rest/api/types/EntityTypes.ts | 0 .../rest/api/types/ErrorResponse.ts | 9 + .../rest/api/types/ErrorResponseError.ts | 0 .../rest/api/types/GooglerpcStatus.ts | 11 + .../rest/api/types/ProtobufAny.ts | 7 + .../rest/api/types/RedactionEnumRedaction.ts | 0 .../rest/api/types/ReidentifyFileResponse.ts | 0 .../api/types/ReidentifyFileResponseOutput.ts | 0 .../types/ReidentifyFileResponseOutputType.ts | 0 .../api/types/ReidentifyFileResponseStatus.ts | 0 .../api/types/ReidentifyStringResponse.ts | 0 .../rest/api/types/RequestActionType.ts | 0 .../ _generated_/rest/api/types/ResourceId.ts | 0 .../rest/api/types/RestrictRegex.ts | 0 .../ _generated_/rest/api/types/TokenType.ts | 0 .../rest/api/types/TokenTypeDefault.ts | 0 .../rest/api/types/TokenTypeWithoutVault.ts | 0 .../api/types/TokenTypeWithoutVaultDefault.ts | 0 .../rest/api/types/Transformations.ts | 0 .../api/types/TransformationsShiftDates.ts | 0 ...ransformationsShiftDatesEntityTypesItem.ts | 0 .../rest/api/types/UploadFileV2Response.ts | 0 .../src}/ _generated_/rest/api/types/Uuid.ts | 0 .../rest/api/types/V1AuditAfterOptions.ts | 0 .../rest/api/types/V1AuditEventResponse.ts | 0 .../rest/api/types/V1AuditResponse.ts | 0 .../rest/api/types/V1AuditResponseEvent.ts | 0 .../api/types/V1AuditResponseEventRequest.ts | 0 .../api/types/V1BatchOperationResponse.ts | 0 .../rest/api/types/V1BatchRecord.ts | 0 .../rest/api/types/V1BinListResponse.ts | 0 .../api/types/V1BulkDeleteRecordResponse.ts | 0 .../rest/api/types/V1BulkGetRecordResponse.ts | 0 .../ _generated_/rest/api/types/V1Byot.ts | 0 .../ _generated_/rest/api/types/V1Card.ts | 0 .../rest/api/types/V1DeleteFileResponse.ts | 0 .../rest/api/types/V1DeleteRecordResponse.ts | 0 .../api/types/V1DetokenizeRecordRequest.ts | 0 .../api/types/V1DetokenizeRecordResponse.ts | 0 .../rest/api/types/V1DetokenizeResponse.ts | 0 .../rest/api/types/V1FieldRecords.ts | 0 .../rest/api/types/V1FileAvScanStatus.ts | 0 .../rest/api/types/V1GetAuthTokenResponse.ts | 10 + .../api/types/V1GetFileScanStatusResponse.ts | 0 .../rest/api/types/V1GetQueryResponse.ts | 0 .../rest/api/types/V1InsertRecordResponse.ts | 0 .../rest/api/types/V1MemberType.ts | 0 .../rest/api/types/V1RecordMetaProperties.ts | 0 .../rest/api/types/V1TokenizeRecordRequest.ts | 0 .../api/types/V1TokenizeRecordResponse.ts | 0 .../rest/api/types/V1TokenizeResponse.ts | 0 .../rest/api/types/V1UpdateRecordResponse.ts | 0 .../rest/api/types/V1VaultFieldMapping.ts | 0 .../rest/api/types/V1VaultSchemaConfig.ts | 0 .../ _generated_/rest/api/types/VaultId.ts | 0 .../src}/ _generated_/rest/api/types/index.ts | 0 .../ _generated_/rest/core/auth/BasicAuth.ts | 31 + .../rest/core/auth/BearerToken.ts | 15 + .../src/ _generated_/rest/core/auth/index.ts | 2 + .../rest/core/fetcher/APIResponse.ts | 23 + .../ _generated_/rest/core/fetcher/Fetcher.ts | 151 ++ .../rest/core/fetcher/HttpResponsePromise.ts | 116 ++ .../rest/core/fetcher/RawResponse.ts | 59 + .../rest/core/fetcher/Supplier.ts | 11 + .../rest/core/fetcher/createRequestUrl.ts | 10 + .../rest/core/fetcher/getFetchFn.ts | 25 + .../rest/core/fetcher/getHeader.ts | 8 + .../rest/core/fetcher/getRequestBody.ts | 16 + .../rest/core/fetcher/getResponseBody.ts | 34 + .../ _generated_/rest/core/fetcher/index.ts | 8 + .../rest/core/fetcher/makeRequest.ts | 44 + .../rest/core/fetcher/requestWithRetries.ts | 33 + .../ _generated_/rest/core/fetcher/signals.ts | 38 + .../Node18UniversalStreamWrapper.ts | 0 .../stream-wrappers/NodePre18StreamWrapper.ts | 107 ++ .../stream-wrappers/UndiciStreamWrapper.ts | 0 .../stream-wrappers/chooseStreamWrapper.ts | 34 + .../core/form-data-utils/FormDataWrapper.ts | 0 .../form-data-utils/encodeAsFormParameter.ts | 0 .../rest/core/form-data-utils/index.ts | 0 .../src}/ _generated_/rest/core/index.ts | 0 skyvault/src/ _generated_/rest/core/json.ts | 27 + .../ _generated_/rest/core/runtime/index.ts | 1 + .../ _generated_/rest/core/runtime/runtime.ts | 131 ++ .../src}/ _generated_/rest/environments.ts | 0 .../ _generated_/rest/errors/SkyflowError.ts | 55 + .../rest/errors/SkyflowTimeoutError.ts | 10 + .../src/ _generated_/rest/errors/index.ts | 2 + skyvault/src/ _generated_/rest/index.ts | 4 + skyvault/src/ _generated_/rest/version.ts | 1 + {src => skyvault/src}/index.ts | 20 +- .../management/config/management/index.ts | 0 .../management/controller/policy/index.ts | 0 .../src}/management/controller/role/index.ts | 0 .../src}/management/controller/vault/index.ts | 0 {src => skyvault/src}/management/index.ts | 0 .../model/request/create-vault/index.ts | 0 .../model/request/delete-policy/index.ts | 0 .../model/response/create-vault/index.ts | 0 .../model/response/delete-policy/index.ts | 0 .../src}/management/skyflow/index.ts | 0 .../src}/management/types/index.ts | 2 +- .../src}/utils/validations/index.ts | 380 +---- {src => skyvault/src}/vault/client/index.ts | 12 +- .../src}/vault/controller/audit/index.ts | 0 .../src}/vault/controller/binlookup/index.ts | 0 .../vault/controller/connections/index.ts | 8 +- .../src}/vault/controller/detect/index.ts | 8 +- .../src}/vault/controller/vault/index.ts | 8 +- .../deidentify-file/bleep-audio/index.ts | 0 .../model/options/deidentify-file/index.ts | 2 +- .../model/options/deidentify-text/index.ts | 2 +- .../deidentify-text/token-format/index.ts | 2 +- .../deidentify-text/transformations/index.ts | 2 +- .../vault/model/options/detokenize/index.ts | 9 +- .../vault/model/options/fileUpload/index.ts | 5 +- .../src}/vault/model/options/get/index.ts | 9 +- .../src}/vault/model/options/insert/index.ts | 7 +- .../model/options/reidentify-text/index.ts | 2 +- .../src}/vault/model/options/update/index.ts | 6 +- .../model/request/deidentify-file/index.ts | 0 .../model/request/deidentify-text/index.ts | 0 .../src}/vault/model/request/delete/index.ts | 4 +- .../vault/model/request/detokenize/index.ts | 4 +- .../vault/model/request/file-upload/index.ts | 8 +- .../vault/model/request/get-column/index.ts | 0 .../model/request/get-detect-run/index.ts | 0 .../src}/vault/model/request/get/index.ts | 6 +- .../src}/vault/model/request/inkove/index.ts | 6 +- .../src}/vault/model/request/insert/index.ts | 4 +- .../src}/vault/model/request/query/index.ts | 4 +- .../model/request/reidentify-text/index.ts | 0 .../vault/model/request/tokenize/index.ts | 4 +- .../src}/vault/model/request/update/index.ts | 4 +- .../model/response/deidentify-file/index.ts | 2 +- .../model/response/deidentify-text/index.ts | 2 +- .../src}/vault/model/response/delete/index.ts | 6 +- .../vault/model/response/detokenize/index.ts | 6 +- .../vault/model/response/file-upload/index.ts | 6 +- .../src}/vault/model/response/get/index.ts | 6 +- .../src}/vault/model/response/insert/index.ts | 8 +- .../vault/model/response/invoke/invoke.ts | 6 +- .../src}/vault/model/response/query/index.ts | 6 +- .../model/response/reidentify-text/index.ts | 0 .../vault/model/response/tokenize/index.ts | 6 +- .../src}/vault/model/response/update/index.ts | 8 +- skyvault/src/vault/skyflow/index.ts | 201 +++ {src => skyvault/src}/vault/types/index.ts | 88 +- skyvault/src/version.ts | 4 + skyvault/test/demo-credentials/empty.json | 0 .../test/demo-credentials/invalidJson.json | 1 + .../test/demo-credentials/noClientId.json | 3 + skyvault/test/demo-credentials/noKeyId.json | 4 + .../test/demo-credentials/noPrivateKey.json | 1 + .../test/demo-credentials/noTokenURI.json | 5 + skyvault/test/demo-credentials/valid.json | 7 + {test => skyvault/test}/mockData/pii.pdf | Bin .../test}/utils/validations.test.js | 26 +- .../test}/vault/client/client.test.js | 15 +- .../test}/vault/controller/connection.test.js | 79 +- .../controller/deprecated-detokenize.test.js | 46 +- .../controller/deprecated-insert.test.js | 45 +- .../test}/vault/controller/detect.test.js | 83 +- .../test}/vault/controller/vault.test.js | 100 +- .../test}/vault/model/deprecated.test.js | 29 +- .../test}/vault/skyflow/skyflow.test.js | 0 skyvault/tsconfig.dts.json | 25 + skyvault/tsconfig.json | 30 + skyvault/tsup.config.ts | 19 + src/error/messages/index.ts | 252 --- src/vault/skyflow/index.ts | 274 ---- tsconfig.json | 30 +- 576 files changed, 15788 insertions(+), 1995 deletions(-) create mode 100644 .github/workflows/flowvault-beta-release.yml create mode 100644 .github/workflows/flowvault-release.yml delete mode 100644 api-extractor.json create mode 100644 common/.babelrc create mode 100644 common/babel.config.js create mode 100644 common/jest.config.js create mode 100644 common/package.json create mode 100644 common/src/_generated_/rest/Client.ts rename {src/ _generated_/rest/api/resources/audit => common/src/_generated_/rest/api}/client/index.ts (100%) create mode 100644 common/src/_generated_/rest/api/client/requests/PatchV2VaultsIdRequest.ts create mode 100644 common/src/_generated_/rest/api/client/requests/index.ts create mode 100644 common/src/_generated_/rest/api/errors/BadRequestError.ts create mode 100644 common/src/_generated_/rest/api/errors/NotFoundError.ts create mode 100644 common/src/_generated_/rest/api/errors/UnauthorizedError.ts create mode 100644 common/src/_generated_/rest/api/errors/index.ts create mode 100644 common/src/_generated_/rest/api/index.ts create mode 100644 common/src/_generated_/rest/api/resources/authentication/client/Client.ts rename {src/ _generated_ => common/src/_generated_}/rest/api/resources/authentication/client/index.ts (100%) create mode 100644 common/src/_generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts rename {src/ _generated_ => common/src/_generated_}/rest/api/resources/authentication/client/requests/index.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/api/resources/authentication/index.ts (100%) create mode 100644 common/src/_generated_/rest/api/resources/index.ts rename {src/ _generated_ => common/src/_generated_}/rest/api/types/GooglerpcStatus.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/api/types/ProtobufAny.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/api/types/V1GetAuthTokenResponse.ts (100%) create mode 100644 common/src/_generated_/rest/api/types/index.ts rename {src/ _generated_ => common/src/_generated_}/rest/core/auth/BasicAuth.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/auth/BearerToken.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/auth/index.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/APIResponse.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/Fetcher.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/HttpResponsePromise.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/RawResponse.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/Supplier.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/createRequestUrl.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/getFetchFn.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/getHeader.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/getRequestBody.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/getResponseBody.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/index.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/makeRequest.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/requestWithRetries.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/signals.ts (100%) create mode 100644 common/src/_generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts (100%) create mode 100644 common/src/_generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts rename {src/ _generated_ => common/src/_generated_}/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts (100%) create mode 100644 common/src/_generated_/rest/core/index.ts rename {src/ _generated_ => common/src/_generated_}/rest/core/json.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/runtime/index.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/core/runtime/runtime.ts (100%) create mode 100644 common/src/_generated_/rest/environments.ts rename {src/ _generated_ => common/src/_generated_}/rest/errors/SkyflowError.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/errors/SkyflowTimeoutError.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/errors/index.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/index.ts (100%) rename {src/ _generated_ => common/src/_generated_}/rest/version.ts (100%) create mode 100644 common/src/base/options.ts create mode 100644 common/src/base/request.ts create mode 100644 common/src/base/response.ts rename {src/vault => common/src}/config/connection/index.ts (100%) rename {src/vault => common/src}/config/credentials/index.ts (100%) rename {src/vault => common/src}/config/vault/index.ts (83%) rename {src => common/src}/error/codes/index.ts (95%) rename {src => common/src}/error/index.ts (100%) create mode 100644 common/src/error/messages/index.ts create mode 100644 common/src/identity.ts create mode 100644 common/src/index.ts rename {src => common/src}/service-account/client/index.ts (69%) rename {src => common/src}/service-account/index.ts (99%) create mode 100644 common/src/skyflow/base.ts create mode 100644 common/src/types/index.ts rename {src => common/src}/utils/index.ts (97%) rename {src => common/src}/utils/jwt-utils/index.ts (100%) rename {src => common/src}/utils/logs/index.ts (100%) create mode 100644 common/src/validations/index.ts rename {test => common/test}/demo-credentials/empty.json (100%) rename {test => common/test}/demo-credentials/invalidJson.json (100%) rename {test => common/test}/demo-credentials/noClientId.json (100%) rename {test => common/test}/demo-credentials/noKeyId.json (100%) rename {test => common/test}/demo-credentials/noPrivateKey.json (100%) rename {test => common/test}/demo-credentials/noTokenURI.json (100%) rename {test => common/test}/demo-credentials/valid.json (100%) rename {test => common/test}/error/skyflow-error.test.js (100%) rename {test => common/test}/service-account/client.test.js (78%) rename {test => common/test}/service-account/token.test.js (100%) rename {test/vault/utils/jwt-utils => common/test/utils}/jwt.test.js (93%) rename {test/vault => common/test}/utils/utils.test.js (96%) create mode 100644 common/tsconfig.json create mode 100644 docs/sdk-split-plan.md create mode 100644 flowvault/LICENSE create mode 100644 flowvault/README.md create mode 100644 flowvault/_generated_/rest/src/Client.ts rename {src/ _generated_/rest/api/resources/binLookup => flowvault/_generated_/rest/src/api}/client/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/client/requests/PatchV2VaultsIdRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/client/requests/index.ts create mode 100644 flowvault/_generated_/rest/src/api/errors/BadRequestError.ts create mode 100644 flowvault/_generated_/rest/src/api/errors/ForbiddenError.ts rename {src/ _generated_/rest => flowvault/_generated_/rest/src}/api/errors/InternalServerError.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/errors/NotFoundError.ts create mode 100644 flowvault/_generated_/rest/src/api/errors/TooManyRequestsError.ts create mode 100644 flowvault/_generated_/rest/src/api/errors/UnauthorizedError.ts create mode 100644 flowvault/_generated_/rest/src/api/errors/index.ts create mode 100644 flowvault/_generated_/rest/src/api/index.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/files/client/Client.ts rename {src/ _generated_/rest => flowvault/_generated_/rest/src}/api/resources/files/client/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/resources/files/client/requests/FileDeleteRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/files/client/requests/FileUploadRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/files/client/requests/index.ts rename {src/ _generated_/rest/api/resources/binLookup => flowvault/_generated_/rest/src/api/resources/files}/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/resources/index.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/query/client/Client.ts rename {src/ _generated_/rest/api/resources/guardrails => flowvault/_generated_/rest/src/api/resources/query}/client/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/resources/query/client/requests/ExecuteQueryRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/query/client/requests/index.ts rename {src/ _generated_/rest/api/resources/guardrails => flowvault/_generated_/rest/src/api/resources/query}/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/resources/records/client/Client.ts rename {src/ _generated_/rest/api/resources/query => flowvault/_generated_/rest/src/api/resources/records}/client/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/resources/records/client/requests/DeleteRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/records/client/requests/GetRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/records/client/requests/InsertRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/records/client/requests/UpdateRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/records/client/requests/index.ts rename {src/ _generated_/rest/api/resources/audit => flowvault/_generated_/rest/src/api/resources/records}/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/resources/records/types/UpdateRequestUpdateType.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/records/types/index.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/tokens/client/Client.ts rename {src/ _generated_/rest/api/resources/records => flowvault/_generated_/rest/src/api/resources/tokens}/client/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/resources/tokens/client/requests/DetokenizeRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/tokens/client/requests/GetTokensFromValuesRequest.ts create mode 100644 flowvault/_generated_/rest/src/api/resources/tokens/client/requests/index.ts rename {src/ _generated_/rest/api/resources/query => flowvault/_generated_/rest/src/api/resources/tokens}/index.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/types/ColumnRedactions.ts create mode 100644 flowvault/_generated_/rest/src/api/types/DeleteResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/DeleteResponseObject.ts create mode 100644 flowvault/_generated_/rest/src/api/types/DetokenizeResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/DetokenizeResponseObject.ts rename {src/ _generated_/rest => flowvault/_generated_/rest/src}/api/types/ErrorResponse.ts (100%) create mode 100644 flowvault/_generated_/rest/src/api/types/ErrorResponseError.ts create mode 100644 flowvault/_generated_/rest/src/api/types/ExecuteQueryRecordResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/ExecuteQueryResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/ExecuteQueryResponseMetadata.ts create mode 100644 flowvault/_generated_/rest/src/api/types/FileDeleteRecord.ts create mode 100644 flowvault/_generated_/rest/src/api/types/FileDeleteResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/FileDeleteResponseObject.ts create mode 100644 flowvault/_generated_/rest/src/api/types/FileUploadColumn.ts create mode 100644 flowvault/_generated_/rest/src/api/types/FileUploadRecord.ts create mode 100644 flowvault/_generated_/rest/src/api/types/FileUploadResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/FileUploadResponseObject.ts create mode 100644 flowvault/_generated_/rest/src/api/types/GetRequestData.ts create mode 100644 flowvault/_generated_/rest/src/api/types/GetResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/GetTokensFromValuesRequestObject.ts create mode 100644 flowvault/_generated_/rest/src/api/types/GetTokensFromValuesResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/GoogleProtobufValue.ts create mode 100644 flowvault/_generated_/rest/src/api/types/HttpCode.ts create mode 100644 flowvault/_generated_/rest/src/api/types/InsertRecordData.ts create mode 100644 flowvault/_generated_/rest/src/api/types/InsertResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/RecordResponseObject.ts create mode 100644 flowvault/_generated_/rest/src/api/types/TokenGroupRedactions.ts create mode 100644 flowvault/_generated_/rest/src/api/types/TokenizeResponseObject.ts create mode 100644 flowvault/_generated_/rest/src/api/types/UniqueValue.ts create mode 100644 flowvault/_generated_/rest/src/api/types/UpdateRecordData.ts create mode 100644 flowvault/_generated_/rest/src/api/types/UpdateRecordDataUpdateType.ts create mode 100644 flowvault/_generated_/rest/src/api/types/UpdateResponse.ts create mode 100644 flowvault/_generated_/rest/src/api/types/Upsert.ts create mode 100644 flowvault/_generated_/rest/src/api/types/UpsertUpdateType.ts create mode 100644 flowvault/_generated_/rest/src/api/types/index.ts create mode 100644 flowvault/_generated_/rest/src/core/auth/BasicAuth.ts create mode 100644 flowvault/_generated_/rest/src/core/auth/BearerToken.ts create mode 100644 flowvault/_generated_/rest/src/core/auth/index.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/APIResponse.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/Fetcher.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/HttpResponsePromise.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/RawResponse.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/Supplier.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/createRequestUrl.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/getFetchFn.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/getHeader.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/getRequestBody.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/getResponseBody.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/index.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/makeRequest.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/requestWithRetries.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/signals.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts create mode 100644 flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts create mode 100644 flowvault/_generated_/rest/src/core/index.ts create mode 100644 flowvault/_generated_/rest/src/core/json.ts create mode 100644 flowvault/_generated_/rest/src/core/runtime/index.ts create mode 100644 flowvault/_generated_/rest/src/core/runtime/runtime.ts create mode 100644 flowvault/_generated_/rest/src/environments.ts create mode 100644 flowvault/_generated_/rest/src/errors/SkyflowError.ts create mode 100644 flowvault/_generated_/rest/src/errors/SkyflowTimeoutError.ts create mode 100644 flowvault/_generated_/rest/src/errors/index.ts create mode 100644 flowvault/_generated_/rest/src/index.ts create mode 100644 flowvault/_generated_/rest/src/version.ts create mode 100644 flowvault/api-extractor.json create mode 100644 flowvault/api-report/skyflow-flowvault-node.api.md create mode 100644 flowvault/jest.config.js create mode 100644 flowvault/package.json create mode 100644 flowvault/src/client/index.ts create mode 100644 flowvault/src/controller/vault/index.ts create mode 100644 flowvault/src/index.ts create mode 100644 flowvault/src/model/options/delete-files/index.ts create mode 100644 flowvault/src/model/options/delete/index.ts create mode 100644 flowvault/src/model/options/detokenize/index.ts create mode 100644 flowvault/src/model/options/get-tokens/index.ts create mode 100644 flowvault/src/model/options/get/index.ts create mode 100644 flowvault/src/model/options/insert/index.ts create mode 100644 flowvault/src/model/options/query/index.ts create mode 100644 flowvault/src/model/options/update/index.ts create mode 100644 flowvault/src/model/options/upload-files/index.ts create mode 100644 flowvault/src/model/request/delete-files/index.ts create mode 100644 flowvault/src/model/request/delete/index.ts create mode 100644 flowvault/src/model/request/detokenize/index.ts create mode 100644 flowvault/src/model/request/get-tokens/index.ts create mode 100644 flowvault/src/model/request/get/index.ts create mode 100644 flowvault/src/model/request/insert/index.ts create mode 100644 flowvault/src/model/request/query/index.ts create mode 100644 flowvault/src/model/request/update/index.ts create mode 100644 flowvault/src/model/request/upload-files/index.ts create mode 100644 flowvault/src/model/response/delete-files/index.ts create mode 100644 flowvault/src/model/response/delete/index.ts create mode 100644 flowvault/src/model/response/detokenize/index.ts create mode 100644 flowvault/src/model/response/get-tokens/index.ts create mode 100644 flowvault/src/model/response/get/index.ts create mode 100644 flowvault/src/model/response/insert/index.ts create mode 100644 flowvault/src/model/response/query/index.ts create mode 100644 flowvault/src/model/response/update/index.ts create mode 100644 flowvault/src/model/response/upload-files/index.ts create mode 100644 flowvault/src/skyflow/index.ts create mode 100644 flowvault/src/types/index.ts create mode 100644 flowvault/src/validations/index.ts create mode 100644 flowvault/src/version.ts create mode 100644 flowvault/test/client/client.test.ts create mode 100644 flowvault/test/controller/delete-files.test.ts create mode 100644 flowvault/test/controller/delete.test.ts create mode 100644 flowvault/test/controller/detokenize.test.ts create mode 100644 flowvault/test/controller/get-tokens.test.ts create mode 100644 flowvault/test/controller/get.test.ts create mode 100644 flowvault/test/controller/insert.test.ts create mode 100644 flowvault/test/controller/query.test.ts create mode 100644 flowvault/test/controller/update.test.ts create mode 100644 flowvault/test/controller/upload-files.test.ts create mode 100644 flowvault/test/helpers.ts create mode 100644 flowvault/test/model/options.test.ts create mode 100644 flowvault/test/model/requests.test.ts create mode 100644 flowvault/test/model/responses.test.ts create mode 100644 flowvault/test/skyflow/skyflow.test.ts create mode 100644 flowvault/test/smoke.test.ts create mode 100644 flowvault/test/validations/validations.test.ts create mode 100644 flowvault/tsconfig.dts.json create mode 100644 flowvault/tsconfig.json create mode 100644 flowvault/tsconfig.test.json create mode 100644 flowvault/tsup.config.ts create mode 100644 skyvault/.babelrc create mode 100644 skyvault/LICENSE create mode 100644 skyvault/README.md create mode 100644 skyvault/api-extractor.json rename {api-report => skyvault/api-report}/skyflow-node.api.md (59%) create mode 100644 skyvault/babel.config.js create mode 100644 skyvault/jest.config.js create mode 100644 skyvault/package.json rename {src => skyvault/src}/ _generated_/rest/Client.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/errors/BadRequestError.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/errors/InternalServerError.ts rename {src => skyvault/src}/ _generated_/rest/api/errors/NotFoundError.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/errors/UnauthorizedError.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/errors/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/client/Client.ts (100%) rename {src/ _generated_/rest/api/resources/strings => skyvault/src/ _generated_/rest/api/resources/audit}/client/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/client/requests/AuditServiceListAuditEventsRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/client/requests/index.ts (100%) rename {src/ _generated_/rest/api/resources/files => skyvault/src/ _generated_/rest/api/resources/audit}/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsActionType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAccessType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextActorType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAuthMode.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsResourceType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestSortOpsOrderBy.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/audit/types/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/authentication/client/Client.ts (100%) rename {src/ _generated_/rest/api/resources/tokens => skyvault/src/ _generated_/rest/api/resources/authentication}/client/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/authentication/client/requests/index.ts rename {src/ _generated_/rest/api/resources/tokens => skyvault/src/ _generated_/rest/api/resources/authentication}/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/binLookup/client/Client.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/binLookup/client/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/binLookup/client/requests/V1BinListRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/binLookup/client/requests/index.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/binLookup/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/Client.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/files/client/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyAudioRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyDocumentRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyFileRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyImageRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyPdfRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyPresentationRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifySpreadsheetRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyStructuredTextRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/DeidentifyTextRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/GetRunRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/ReidentifyFileRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/client/requests/index.ts (100%) rename {src/ _generated_/rest/api/resources/records => skyvault/src/ _generated_/rest/api/resources/files}/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestOutputTranscription.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestMaskingMethod.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyPdfRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/DeidentifyTextRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFile.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFileDataFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/files/types/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/guardrails/client/Client.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/guardrails/client/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/guardrails/client/requests/CheckGuardrailsRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/guardrails/client/requests/index.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/guardrails/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/query/client/Client.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/query/client/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/query/client/requests/QueryServiceExecuteQueryBody.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/query/client/requests/index.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/query/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/Client.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/records/client/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/FileServiceUploadFileRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/RecordServiceBatchOperationBody.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkDeleteRecordBody.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkGetRecordRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/RecordServiceGetRecordRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/RecordServiceInsertRecordBody.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/RecordServiceUpdateRecordBody.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/client/requests/index.ts (100%) rename {src/ _generated_/rest/api/resources/strings => skyvault/src/ _generated_/rest/api/resources/records}/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestOrderBy.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestRedaction.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/types/RecordServiceGetRecordRequestRedaction.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/records/types/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/strings/client/Client.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/strings/client/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/strings/client/requests/DeidentifyStringRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/strings/client/requests/ReidentifyStringRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/strings/client/requests/index.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/strings/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/strings/types/ReidentifyStringRequestFormat.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/strings/types/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/tokens/client/Client.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/tokens/client/index.ts rename {src => skyvault/src}/ _generated_/rest/api/resources/tokens/client/requests/V1DetokenizePayload.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/tokens/client/requests/V1TokenizePayload.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/resources/tokens/client/requests/index.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/resources/tokens/index.ts rename {src => skyvault/src}/ _generated_/rest/api/types/AllowRegex.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/AuditEventAuditResourceType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/AuditEventContext.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/AuditEventData.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/AuditEventHttpInfo.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/BatchRecordMethod.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/CheckGuardrailsResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/CheckGuardrailsResponseValidation.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ConfigurationId.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ContextAccessType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ContextAuthMode.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DeidentifyFileOutput.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DeidentifyFileOutputProcessedFileType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DeidentifyFileResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DeidentifyStatusResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DeidentifyStringResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DetectedEntity.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/DetokenizeRecordResponseValueType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/EntityLocation.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/EntityType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/EntityTypes.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/types/ErrorResponse.ts rename {src => skyvault/src}/ _generated_/rest/api/types/ErrorResponseError.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/types/GooglerpcStatus.ts create mode 100644 skyvault/src/ _generated_/rest/api/types/ProtobufAny.ts rename {src => skyvault/src}/ _generated_/rest/api/types/RedactionEnumRedaction.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ReidentifyFileResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ReidentifyFileResponseOutput.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ReidentifyStringResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/RequestActionType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/ResourceId.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/RestrictRegex.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/TokenType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/TokenTypeDefault.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/TokenTypeWithoutVault.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/TokenTypeWithoutVaultDefault.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/Transformations.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/TransformationsShiftDates.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/TransformationsShiftDatesEntityTypesItem.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/UploadFileV2Response.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/Uuid.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1AuditAfterOptions.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1AuditEventResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1AuditResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1AuditResponseEvent.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1AuditResponseEventRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1BatchOperationResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1BatchRecord.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1BinListResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1BulkDeleteRecordResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1BulkGetRecordResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1Byot.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1Card.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1DeleteFileResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1DeleteRecordResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1DetokenizeRecordRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1DetokenizeRecordResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1DetokenizeResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1FieldRecords.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1FileAvScanStatus.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/api/types/V1GetAuthTokenResponse.ts rename {src => skyvault/src}/ _generated_/rest/api/types/V1GetFileScanStatusResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1GetQueryResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1InsertRecordResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1MemberType.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1RecordMetaProperties.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1TokenizeRecordRequest.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1TokenizeRecordResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1TokenizeResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1UpdateRecordResponse.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1VaultFieldMapping.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/V1VaultSchemaConfig.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/VaultId.ts (100%) rename {src => skyvault/src}/ _generated_/rest/api/types/index.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/core/auth/BasicAuth.ts create mode 100644 skyvault/src/ _generated_/rest/core/auth/BearerToken.ts create mode 100644 skyvault/src/ _generated_/rest/core/auth/index.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/APIResponse.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/Fetcher.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/HttpResponsePromise.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/RawResponse.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/Supplier.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/createRequestUrl.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/getFetchFn.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/getHeader.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/getRequestBody.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/getResponseBody.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/index.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/makeRequest.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/requestWithRetries.ts create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/signals.ts rename {src => skyvault/src}/ _generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts rename {src => skyvault/src}/ _generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts rename {src => skyvault/src}/ _generated_/rest/core/form-data-utils/FormDataWrapper.ts (100%) rename {src => skyvault/src}/ _generated_/rest/core/form-data-utils/encodeAsFormParameter.ts (100%) rename {src => skyvault/src}/ _generated_/rest/core/form-data-utils/index.ts (100%) rename {src => skyvault/src}/ _generated_/rest/core/index.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/core/json.ts create mode 100644 skyvault/src/ _generated_/rest/core/runtime/index.ts create mode 100644 skyvault/src/ _generated_/rest/core/runtime/runtime.ts rename {src => skyvault/src}/ _generated_/rest/environments.ts (100%) create mode 100644 skyvault/src/ _generated_/rest/errors/SkyflowError.ts create mode 100644 skyvault/src/ _generated_/rest/errors/SkyflowTimeoutError.ts create mode 100644 skyvault/src/ _generated_/rest/errors/index.ts create mode 100644 skyvault/src/ _generated_/rest/index.ts create mode 100644 skyvault/src/ _generated_/rest/version.ts rename {src => skyvault/src}/index.ts (91%) rename {src => skyvault/src}/management/config/management/index.ts (100%) rename {src => skyvault/src}/management/controller/policy/index.ts (100%) rename {src => skyvault/src}/management/controller/role/index.ts (100%) rename {src => skyvault/src}/management/controller/vault/index.ts (100%) rename {src => skyvault/src}/management/index.ts (100%) rename {src => skyvault/src}/management/model/request/create-vault/index.ts (100%) rename {src => skyvault/src}/management/model/request/delete-policy/index.ts (100%) rename {src => skyvault/src}/management/model/response/create-vault/index.ts (100%) rename {src => skyvault/src}/management/model/response/delete-policy/index.ts (100%) rename {src => skyvault/src}/management/skyflow/index.ts (100%) rename {src => skyvault/src}/management/types/index.ts (78%) rename {src => skyvault/src}/utils/validations/index.ts (69%) rename {src => skyvault/src}/vault/client/index.ts (98%) rename {src => skyvault/src}/vault/controller/audit/index.ts (100%) rename {src => skyvault/src}/vault/controller/binlookup/index.ts (100%) rename {src => skyvault/src}/vault/controller/connections/index.ts (98%) rename {src => skyvault/src}/vault/controller/detect/index.ts (99%) rename {src => skyvault/src}/vault/controller/vault/index.ts (99%) rename {src => skyvault/src}/vault/model/options/deidentify-file/bleep-audio/index.ts (100%) rename {src => skyvault/src}/vault/model/options/deidentify-file/index.ts (99%) rename {src => skyvault/src}/vault/model/options/deidentify-text/index.ts (96%) rename {src => skyvault/src}/vault/model/options/deidentify-text/token-format/index.ts (94%) rename {src => skyvault/src}/vault/model/options/deidentify-text/transformations/index.ts (89%) rename {src => skyvault/src}/vault/model/options/detokenize/index.ts (82%) rename {src => skyvault/src}/vault/model/options/fileUpload/index.ts (89%) rename {src => skyvault/src}/vault/model/options/get/index.ts (93%) rename {src => skyvault/src}/vault/model/options/insert/index.ts (89%) rename {src => skyvault/src}/vault/model/options/reidentify-text/index.ts (94%) rename {src => skyvault/src}/vault/model/options/update/index.ts (83%) rename {src => skyvault/src}/vault/model/request/deidentify-file/index.ts (100%) rename {src => skyvault/src}/vault/model/request/deidentify-text/index.ts (100%) rename {src => skyvault/src}/vault/model/request/delete/index.ts (84%) rename {src => skyvault/src}/vault/model/request/detokenize/index.ts (77%) rename {src => skyvault/src}/vault/model/request/file-upload/index.ts (87%) rename {src => skyvault/src}/vault/model/request/get-column/index.ts (100%) rename {src => skyvault/src}/vault/model/request/get-detect-run/index.ts (100%) rename {src => skyvault/src}/vault/model/request/get/index.ts (80%) rename {src => skyvault/src}/vault/model/request/inkove/index.ts (77%) rename {src => skyvault/src}/vault/model/request/insert/index.ts (85%) rename {src => skyvault/src}/vault/model/request/query/index.ts (73%) rename {src => skyvault/src}/vault/model/request/reidentify-text/index.ts (100%) rename {src => skyvault/src}/vault/model/request/tokenize/index.ts (80%) rename {src => skyvault/src}/vault/model/request/update/index.ts (85%) rename {src => skyvault/src}/vault/model/response/deidentify-file/index.ts (96%) rename {src => skyvault/src}/vault/model/response/deidentify-text/index.ts (95%) rename {src => skyvault/src}/vault/model/response/delete/index.ts (73%) rename {src => skyvault/src}/vault/model/response/detokenize/index.ts (74%) rename {src => skyvault/src}/vault/model/response/file-upload/index.ts (64%) rename {src => skyvault/src}/vault/model/response/get/index.ts (70%) rename {src => skyvault/src}/vault/model/response/insert/index.ts (70%) rename {src => skyvault/src}/vault/model/response/invoke/invoke.ts (68%) rename {src => skyvault/src}/vault/model/response/query/index.ts (70%) rename {src => skyvault/src}/vault/model/response/reidentify-text/index.ts (100%) rename {src => skyvault/src}/vault/model/response/tokenize/index.ts (65%) rename {src => skyvault/src}/vault/model/response/update/index.ts (64%) create mode 100644 skyvault/src/vault/skyflow/index.ts rename {src => skyvault/src}/vault/types/index.ts (72%) create mode 100644 skyvault/src/version.ts create mode 100644 skyvault/test/demo-credentials/empty.json create mode 100644 skyvault/test/demo-credentials/invalidJson.json create mode 100644 skyvault/test/demo-credentials/noClientId.json create mode 100644 skyvault/test/demo-credentials/noKeyId.json create mode 100644 skyvault/test/demo-credentials/noPrivateKey.json create mode 100644 skyvault/test/demo-credentials/noTokenURI.json create mode 100644 skyvault/test/demo-credentials/valid.json rename {test => skyvault/test}/mockData/pii.pdf (100%) rename {test => skyvault/test}/utils/validations.test.js (98%) rename {test => skyvault/test}/vault/client/client.test.js (99%) rename {test => skyvault/test}/vault/controller/connection.test.js (96%) rename {test => skyvault/test}/vault/controller/deprecated-detokenize.test.js (81%) rename {test => skyvault/test}/vault/controller/deprecated-insert.test.js (88%) rename {test => skyvault/test}/vault/controller/detect.test.js (97%) rename {test => skyvault/test}/vault/controller/vault.test.js (98%) rename {test => skyvault/test}/vault/model/deprecated.test.js (96%) rename {test => skyvault/test}/vault/skyflow/skyflow.test.js (100%) create mode 100644 skyvault/tsconfig.dts.json create mode 100644 skyvault/tsconfig.json create mode 100644 skyvault/tsup.config.ts delete mode 100644 src/error/messages/index.ts delete mode 100644 src/vault/skyflow/index.ts 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/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..214c8111 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 === "symbol" || !(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` → `V1UniqueValue{data}` and Python `__to_unique_values`. | +| **deleteFiles column status** | **Parsed** from each column's response value (object → `.status`, else the value), not assumed `"DELETED"`. Mirrors Python `__file_column_status`. (`DeleteFilesResponseRecord` keeps `tableName` + `columns` — unlike record-delete.) | +| **insert/update `fields`** | Generated `RecordResponseObject` splits `data` + `tokens`; the SDK exposes one `fields = tokens ?? data`. (Retained merge choice.) | +| **Query** | flowvault **has** `query` (as in Python; Java flowvault has none). Response is `fields`/`records` + `metadata.columns` + call-level `requestId`; **no per-record `errors`** (the query API has no partial-error responses, so `errors` is always `null`). | + +Open regeneration items (generated code untouched; flag for the maintainer): +- Both `common` and `flowvault` generated clients ship a top-level `Client.ts` (`patchV2VaultsId`) + that fails `strict` (`encodeURIComponent(vaultId)` on `string | undefined`); unused by the + hand-written layers (which compose resource clients directly), so it + its `index.ts` are + `exclude`d in the relevant tsconfigs — a regeneration concern, not a hand-edit. + +🤖 Generated with [Claude Code](https://claude.com/claude-code) diff --git a/flowvault/LICENSE b/flowvault/LICENSE new file mode 100644 index 00000000..189f34b5 --- /dev/null +++ b/flowvault/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Skyflow Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/flowvault/README.md b/flowvault/README.md new file mode 100644 index 00000000..4a868788 --- /dev/null +++ b/flowvault/README.md @@ -0,0 +1,53 @@ +# Skyflow FlowVault Node SDK (`skyflow-flowvault-node`) + +The FlowVault (v2) Node.js SDK for Skyflow's data privacy vault. It provides a unary, +strongly-typed contract over the v2 vault data plane. + +> This is a separate product from `skyflow-node` (the SkyVault SDK). Install exactly one: +> `npm i skyflow-flowvault-node` **or** `npm i skyflow-node`. A lower `flowvault` version +> number does not mean it is older — the two products version independently. + +## Install + +```bash +npm install skyflow-flowvault-node +``` + +## Quickstart + +```typescript +import { Skyflow, InsertRequest, InsertOptions, LogLevel } from 'skyflow-flowvault-node'; + +const client = new Skyflow({ + vaultConfigs: [{ + vaultId: 'your-vault-id', + clusterId: 'your-cluster-id', + credentials: { token: 'your-bearer-token' }, + }], + logLevel: LogLevel.ERROR, +}); + +const req = new InsertRequest('cards', [ + { data: { card_number: '4111111111111111', cardholder_name: 'Jane Doe' } }, +]); + +const res = await client.vault('your-vault-id').insert(req); +console.log(res.records); +``` + +## Operations (all unary) + +`insert` · `get` · `update` · `delete` · `query` · `detokenize` · `getTokens` · +`uploadFiles` · `deleteFiles`. + +Every response returns a single `records` list with per-record `httpCode` / `error` / +`requestId`; whole-call failures throw `SkyflowError`. + +## Design & architecture + +- Contract design doc: +- Monorepo split (`common` / `skyvault` / `flowvault`): see `docs/sdk-split-plan.md`. + +## License + +MIT diff --git a/flowvault/_generated_/rest/src/Client.ts b/flowvault/_generated_/rest/src/Client.ts new file mode 100644 index 00000000..0bd4bec0 --- /dev/null +++ b/flowvault/_generated_/rest/src/Client.ts @@ -0,0 +1,142 @@ +/** + * 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 { Files } from "./api/resources/files/client/Client"; +import { Query } from "./api/resources/query/client/Client"; +import { Records } from "./api/resources/records/client/Client"; +import { Tokens } from "./api/resources/tokens/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 _files: Files | undefined; + protected _query: Query | undefined; + protected _records: Records | undefined; + protected _tokens: Tokens | undefined; + + constructor(protected readonly _options: SkyflowClient.Options = {}) {} + + public get files(): Files { + return (this._files ??= new Files(this._options)); + } + + public get query(): Query { + return (this._query ??= new Query(this._options)); + } + + public get records(): Records { + return (this._records ??= new Records(this._options)); + } + + public get tokens(): Tokens { + return (this._tokens ??= new Tokens(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 { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/src/ _generated_/rest/api/resources/binLookup/client/index.ts b/flowvault/_generated_/rest/src/api/client/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/binLookup/client/index.ts rename to flowvault/_generated_/rest/src/api/client/index.ts diff --git a/flowvault/_generated_/rest/src/api/client/requests/PatchV2VaultsIdRequest.ts b/flowvault/_generated_/rest/src/api/client/requests/PatchV2VaultsIdRequest.ts new file mode 100644 index 00000000..a2929a18 --- /dev/null +++ b/flowvault/_generated_/rest/src/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/flowvault/_generated_/rest/src/api/client/requests/index.ts b/flowvault/_generated_/rest/src/api/client/requests/index.ts new file mode 100644 index 00000000..1a0e15b2 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/client/requests/index.ts @@ -0,0 +1 @@ +export { type PatchV2VaultsIdRequest } from "./PatchV2VaultsIdRequest"; diff --git a/flowvault/_generated_/rest/src/api/errors/BadRequestError.ts b/flowvault/_generated_/rest/src/api/errors/BadRequestError.ts new file mode 100644 index 00000000..1f9b3de6 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/errors/BadRequestError.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index"; +import * as Skyflow from "../index"; +import * as core from "../../core"; + +export class BadRequestError extends errors.SkyflowError { + constructor(body: Skyflow.ErrorResponse, rawResponse?: core.RawResponse) { + super({ + message: "BadRequestError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, BadRequestError.prototype); + } +} diff --git a/flowvault/_generated_/rest/src/api/errors/ForbiddenError.ts b/flowvault/_generated_/rest/src/api/errors/ForbiddenError.ts new file mode 100644 index 00000000..699b312d --- /dev/null +++ b/flowvault/_generated_/rest/src/api/errors/ForbiddenError.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index"; +import * as Skyflow from "../index"; +import * as core from "../../core"; + +export class ForbiddenError extends errors.SkyflowError { + constructor(body: Skyflow.ErrorResponse, rawResponse?: core.RawResponse) { + super({ + message: "ForbiddenError", + statusCode: 403, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, ForbiddenError.prototype); + } +} diff --git a/src/ _generated_/rest/api/errors/InternalServerError.ts b/flowvault/_generated_/rest/src/api/errors/InternalServerError.ts similarity index 100% rename from src/ _generated_/rest/api/errors/InternalServerError.ts rename to flowvault/_generated_/rest/src/api/errors/InternalServerError.ts diff --git a/flowvault/_generated_/rest/src/api/errors/NotFoundError.ts b/flowvault/_generated_/rest/src/api/errors/NotFoundError.ts new file mode 100644 index 00000000..f5226d35 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/errors/NotFoundError.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index"; +import * as Skyflow from "../index"; +import * as core from "../../core"; + +export class NotFoundError extends errors.SkyflowError { + constructor(body: Skyflow.ErrorResponse, rawResponse?: core.RawResponse) { + super({ + message: "NotFoundError", + statusCode: 404, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, NotFoundError.prototype); + } +} diff --git a/flowvault/_generated_/rest/src/api/errors/TooManyRequestsError.ts b/flowvault/_generated_/rest/src/api/errors/TooManyRequestsError.ts new file mode 100644 index 00000000..879bed1c --- /dev/null +++ b/flowvault/_generated_/rest/src/api/errors/TooManyRequestsError.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index"; +import * as Skyflow from "../index"; +import * as core from "../../core"; + +export class TooManyRequestsError extends errors.SkyflowError { + constructor(body: Skyflow.ErrorResponse, rawResponse?: core.RawResponse) { + super({ + message: "TooManyRequestsError", + statusCode: 429, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, TooManyRequestsError.prototype); + } +} diff --git a/flowvault/_generated_/rest/src/api/errors/UnauthorizedError.ts b/flowvault/_generated_/rest/src/api/errors/UnauthorizedError.ts new file mode 100644 index 00000000..9d763249 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/errors/UnauthorizedError.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index"; +import * as Skyflow from "../index"; +import * as core from "../../core"; + +export class UnauthorizedError extends errors.SkyflowError { + constructor(body: Skyflow.ErrorResponse, rawResponse?: core.RawResponse) { + super({ + message: "UnauthorizedError", + statusCode: 401, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, UnauthorizedError.prototype); + } +} diff --git a/flowvault/_generated_/rest/src/api/errors/index.ts b/flowvault/_generated_/rest/src/api/errors/index.ts new file mode 100644 index 00000000..9dc2c3b7 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/errors/index.ts @@ -0,0 +1,6 @@ +export * from "./BadRequestError"; +export * from "./UnauthorizedError"; +export * from "./ForbiddenError"; +export * from "./NotFoundError"; +export * from "./TooManyRequestsError"; +export * from "./InternalServerError"; diff --git a/flowvault/_generated_/rest/src/api/index.ts b/flowvault/_generated_/rest/src/api/index.ts new file mode 100644 index 00000000..d5c609c4 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/index.ts @@ -0,0 +1,4 @@ +export * from "./resources"; +export * from "./types"; +export * from "./errors"; +export * from "./client"; diff --git a/flowvault/_generated_/rest/src/api/resources/files/client/Client.ts b/flowvault/_generated_/rest/src/api/resources/files/client/Client.ts new file mode 100644 index 00000000..f38567a7 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/files/client/Client.ts @@ -0,0 +1,290 @@ +/** + * 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 Files { + 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 Files { + constructor(protected readonly _options: Files.Options = {}) {} + + /** + * Deletes one or more files from the specified file column(s) of existing record(s) in a vault, and removes the underlying object from cloud storage. + * + * @param {Skyflow.FileDeleteRequest} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.ForbiddenError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.TooManyRequestsError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.files.deleteFiles({ + * vaultID: "d408485953784308a000f8dcf81901ef", + * records: [{ + * tableName: "onboarding", + * skyflowID: "77ea0577-894a-4a42-90a9-51b0eaf97159", + * columns: ["resumePDF", "photoID"] + * }] + * }) + */ + public deleteFiles( + request: Skyflow.FileDeleteRequest, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__deleteFiles(request, requestOptions)); + } + + private async __deleteFiles( + request: Skyflow.FileDeleteRequest, + requestOptions?: Files.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/files/delete", + ), + 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.FileDeleteResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 403: + throw new Skyflow.ForbiddenError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 429: + throw new Skyflow.TooManyRequestsError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/files/delete."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * Generates signed URLs for uploading files to specified columns in a vault. Returns a signed URL per column that expires in 15 minutes by default. If a skyflowID is not provided for a record, a new record is created and its skyflowID is returned. + * + * @param {Skyflow.FileUploadRequest} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.ForbiddenError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.TooManyRequestsError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.files.uploadFiles({ + * vaultID: "d408485953784308a000f8dcf81901ef", + * records: [{ + * tableName: "onboarding", + * columns: [{ + * column: "resumePDF", + * fileName: "sampleresume.pdf" + * }, { + * column: "photoID", + * fileName: "samplephoto.jpg" + * }] + * }] + * }) + */ + public uploadFiles( + request: Skyflow.FileUploadRequest, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadFiles(request, requestOptions)); + } + + private async __uploadFiles( + request: Skyflow.FileUploadRequest, + requestOptions?: Files.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/files/upload", + ), + 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.FileUploadResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 403: + throw new Skyflow.ForbiddenError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 429: + throw new Skyflow.TooManyRequestsError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/files/upload."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/src/ _generated_/rest/api/resources/files/client/index.ts b/flowvault/_generated_/rest/src/api/resources/files/client/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/index.ts rename to flowvault/_generated_/rest/src/api/resources/files/client/index.ts diff --git a/flowvault/_generated_/rest/src/api/resources/files/client/requests/FileDeleteRequest.ts b/flowvault/_generated_/rest/src/api/resources/files/client/requests/FileDeleteRequest.ts new file mode 100644 index 00000000..4472e273 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/files/client/requests/FileDeleteRequest.ts @@ -0,0 +1,23 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef", + * records: [{ + * tableName: "onboarding", + * skyflowID: "77ea0577-894a-4a42-90a9-51b0eaf97159", + * columns: ["resumePDF", "photoID"] + * }] + * } + */ +export interface FileDeleteRequest { + /** ID of the vault. */ + vaultID: string; + /** List of record objects (max 20,000) identifying the file columns to delete. */ + records: Skyflow.FileDeleteRecord[]; +} diff --git a/flowvault/_generated_/rest/src/api/resources/files/client/requests/FileUploadRequest.ts b/flowvault/_generated_/rest/src/api/resources/files/client/requests/FileUploadRequest.ts new file mode 100644 index 00000000..8f8235ba --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/files/client/requests/FileUploadRequest.ts @@ -0,0 +1,28 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef", + * records: [{ + * tableName: "onboarding", + * columns: [{ + * column: "resumePDF", + * fileName: "sampleresume.pdf" + * }, { + * column: "photoID", + * fileName: "samplephoto.jpg" + * }] + * }] + * } + */ +export interface FileUploadRequest { + /** ID of the vault. */ + vaultID: string; + /** List of records for which signed file upload URLs are requested. */ + records: Skyflow.FileUploadRecord[]; +} diff --git a/flowvault/_generated_/rest/src/api/resources/files/client/requests/index.ts b/flowvault/_generated_/rest/src/api/resources/files/client/requests/index.ts new file mode 100644 index 00000000..c073ac4e --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/files/client/requests/index.ts @@ -0,0 +1,2 @@ +export { type FileDeleteRequest } from "./FileDeleteRequest"; +export { type FileUploadRequest } from "./FileUploadRequest"; diff --git a/src/ _generated_/rest/api/resources/binLookup/index.ts b/flowvault/_generated_/rest/src/api/resources/files/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/binLookup/index.ts rename to flowvault/_generated_/rest/src/api/resources/files/index.ts diff --git a/flowvault/_generated_/rest/src/api/resources/index.ts b/flowvault/_generated_/rest/src/api/resources/index.ts new file mode 100644 index 00000000..924c424a --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/index.ts @@ -0,0 +1,9 @@ +export * as records from "./records"; +export * from "./records/types"; +export * as files from "./files"; +export * as query from "./query"; +export * as tokens from "./tokens"; +export * from "./files/client/requests"; +export * from "./query/client/requests"; +export * from "./records/client/requests"; +export * from "./tokens/client/requests"; diff --git a/flowvault/_generated_/rest/src/api/resources/query/client/Client.ts b/flowvault/_generated_/rest/src/api/resources/query/client/Client.ts new file mode 100644 index 00000000..a8fdb633 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/query/client/Client.ts @@ -0,0 +1,154 @@ +/** + * 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 Query { + 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 Query { + constructor(protected readonly _options: Query.Options = {}) {} + + /** + * Returns records for a valid SQL query. This endpoint + * - Can return masked record values. + * - Supports only the `SELECT` command. + * - Returns a maximum of 25 records. To return additional records, perform another query using the `OFFSET` keyword. + * - Can't modify the vault or perform transactions. + * - Can't return tokens. + * - Can't return file download or render URLs. + * + * @param {Skyflow.ExecuteQueryRequest} request + * @param {Query.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.query.executeQuery({ + * vaultID: "d408485953784308a000f8dcf81901ef", + * query: "query" + * }) + */ + public executeQuery( + request: Skyflow.ExecuteQueryRequest, + requestOptions?: Query.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__executeQuery(request, requestOptions)); + } + + private async __executeQuery( + request: Skyflow.ExecuteQueryRequest, + requestOptions?: Query.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/query", + ), + 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.ExecuteQueryResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/query."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/src/ _generated_/rest/api/resources/guardrails/client/index.ts b/flowvault/_generated_/rest/src/api/resources/query/client/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/guardrails/client/index.ts rename to flowvault/_generated_/rest/src/api/resources/query/client/index.ts diff --git a/flowvault/_generated_/rest/src/api/resources/query/client/requests/ExecuteQueryRequest.ts b/flowvault/_generated_/rest/src/api/resources/query/client/requests/ExecuteQueryRequest.ts new file mode 100644 index 00000000..08494057 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/query/client/requests/ExecuteQueryRequest.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef", + * query: "query" + * } + */ +export interface ExecuteQueryRequest { + /** ID of the vault where the query is being performed. */ + vaultID: string; + /** Query to perform. */ + query: string; +} diff --git a/flowvault/_generated_/rest/src/api/resources/query/client/requests/index.ts b/flowvault/_generated_/rest/src/api/resources/query/client/requests/index.ts new file mode 100644 index 00000000..22e2aaab --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/query/client/requests/index.ts @@ -0,0 +1 @@ +export { type ExecuteQueryRequest } from "./ExecuteQueryRequest"; diff --git a/src/ _generated_/rest/api/resources/guardrails/index.ts b/flowvault/_generated_/rest/src/api/resources/query/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/guardrails/index.ts rename to flowvault/_generated_/rest/src/api/resources/query/index.ts diff --git a/flowvault/_generated_/rest/src/api/resources/records/client/Client.ts b/flowvault/_generated_/rest/src/api/resources/records/client/Client.ts new file mode 100644 index 00000000..a717b2ef --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/client/Client.ts @@ -0,0 +1,541 @@ +/** + * 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 Records { + 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 Records { + constructor(protected readonly _options: Records.Options = {}) {} + + /** + * Deletes records from a vault. + * + * @param {Skyflow.DeleteRequest} request + * @param {Records.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.records.deleteRecords({ + * vaultID: "d408485953784308a000f8dcf81901ef" + * }) + */ + public deleteRecords( + request: Skyflow.DeleteRequest, + requestOptions?: Records.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__deleteRecords(request, requestOptions)); + } + + private async __deleteRecords( + request: Skyflow.DeleteRequest, + requestOptions?: Records.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/records/delete", + ), + 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.DeleteResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/records/delete."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * Returns the specified records from a vault. + * + * @param {Skyflow.GetRequest} request + * @param {Records.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.records.getRecords({ + * vaultID: "d408485953784308a000f8dcf81901ef" + * }) + */ + public getRecords( + request: Skyflow.GetRequest, + requestOptions?: Records.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getRecords(request, requestOptions)); + } + + private async __getRecords( + request: Skyflow.GetRequest, + requestOptions?: Records.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/records/get", + ), + 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.GetResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/records/get."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * Inserts new records into a vault. + * + * @param {Skyflow.InsertRequest} request + * @param {Records.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.records.insertRecords({ + * vaultID: "d408485953784308a000f8dcf81901ef", + * tableName: "employees", + * records: [{ + * data: { + * "name": "Vivek1", + * "email": "vivek.varshney@skyflow.com", + * "age": 23, + * "adult": true, + * "address": { + * "street": "Bata Gali", + * "city": "Aligarh", + * "state": "UP", + * "postal_code": 202001, + * "country": "India", + * "phone_numbers": [ + * { + * "type": "home", + * "number": [ + * 1234, + * 5678 + * ] + * }, + * { + * "type": "work", + * "number": [ + * 4321, + * 8765 + * ] + * } + * ] + * } + * } + * }, { + * data: { + * "name": "Asad1", + * "email": "asad.public@gmail.com", + * "age": 16, + * "adult": false, + * "address": { + * "street": "Sarojini", + * "city": "Deoria", + * "state": "UP", + * "postal_code": 274001, + * "country": "India", + * "phone_numbers": [ + * { + * "type": "home", + * "number": [ + * 7890, + * 3456 + * ] + * }, + * { + * "type": "work", + * "number": [ + * 9087, + * 6543 + * ] + * } + * ] + * } + * } + * }] + * }) + */ + public insertRecords( + request: Skyflow.InsertRequest, + requestOptions?: Records.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__insertRecords(request, requestOptions)); + } + + private async __insertRecords( + request: Skyflow.InsertRequest, + requestOptions?: Records.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/records/insert", + ), + 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.InsertResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/records/insert."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * Updates the specified records in a vault. + * + * @param {Skyflow.UpdateRequest} request + * @param {Records.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.records.updateRecords({ + * vaultID: "d408485953784308a000f8dcf81901ef", + * tableName: "employees", + * records: [{ + * skyflowID: "97cdd1af-02ac-47eb-ab0d-8339dbef6ccb", + * data: { + * "name": "Vivek", + * "email": "asad.public5@gmail.com", + * "age": 25, + * "adult": true, + * "address": { + * "city": "aligarh", + * "country": "India" + * } + * } + * }, { + * skyflowID: "aed32bbc-e7a4-4c7b-8c88-7f52ce8cb066", + * data: { + * "name": "Asad", + * "email": "asad.public7@gmail.com", + * "age": 24, + * "adult": false, + * "address": { + * "city": "deoria", + * "country": "India" + * } + * } + * }] + * }) + */ + public updateRecords( + request: Skyflow.UpdateRequest, + requestOptions?: Records.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__updateRecords(request, requestOptions)); + } + + private async __updateRecords( + request: Skyflow.UpdateRequest, + requestOptions?: Records.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/records/update", + ), + 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.UpdateResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/records/update."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/src/ _generated_/rest/api/resources/query/client/index.ts b/flowvault/_generated_/rest/src/api/resources/records/client/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/query/client/index.ts rename to flowvault/_generated_/rest/src/api/resources/records/client/index.ts diff --git a/flowvault/_generated_/rest/src/api/resources/records/client/requests/DeleteRequest.ts b/flowvault/_generated_/rest/src/api/resources/records/client/requests/DeleteRequest.ts new file mode 100644 index 00000000..6c528dbb --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/client/requests/DeleteRequest.ts @@ -0,0 +1,22 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef" + * } + */ +export interface DeleteRequest { + /** ID of the vault. */ + vaultID: string; + /** Name of the table. */ + tableName?: string; + /** Skyflow IDs of the records to delete. */ + skyflowIDs?: string[]; + /** List of unique constraint values to query records by data. */ + uniqueValues?: Skyflow.UniqueValue[]; +} diff --git a/flowvault/_generated_/rest/src/api/resources/records/client/requests/GetRequest.ts b/flowvault/_generated_/rest/src/api/resources/records/client/requests/GetRequest.ts new file mode 100644 index 00000000..29cb25a5 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/client/requests/GetRequest.ts @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef" + * } + */ +export interface GetRequest { + /** ID of the vault. */ + vaultID: string; + /** Name of the table to perform the operation on. */ + tableName?: string; + /** Skyflow IDs of the records to return. Either `skyflowIDs` or `uniqueValues` are required. If both are provided, the request fails. */ + skyflowIDs?: string[]; + /** List of columns to redact. */ + columnRedactions?: Skyflow.ColumnRedactions[]; + /** List of columns to return. */ + columns?: string[]; + /** Limit for the number of records to be fetched. */ + limit?: number; + /** Offset for the number of records to be fetched. */ + offset?: number; + /** List of unique constraint values to query records by data. */ + uniqueValues?: Skyflow.UniqueValue[]; + /** List of records to be fetched. This field contains tableName and skyflowIDs belonging to the table. */ + records?: Skyflow.GetRequestData[]; +} diff --git a/flowvault/_generated_/rest/src/api/resources/records/client/requests/InsertRequest.ts b/flowvault/_generated_/rest/src/api/resources/records/client/requests/InsertRequest.ts new file mode 100644 index 00000000..e89511dc --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/client/requests/InsertRequest.ts @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef", + * tableName: "employees", + * records: [{ + * data: { + * "name": "Vivek1", + * "email": "vivek.varshney@skyflow.com", + * "age": 23, + * "adult": true, + * "address": { + * "street": "Bata Gali", + * "city": "Aligarh", + * "state": "UP", + * "postal_code": 202001, + * "country": "India", + * "phone_numbers": [ + * { + * "type": "home", + * "number": [ + * 1234, + * 5678 + * ] + * }, + * { + * "type": "work", + * "number": [ + * 4321, + * 8765 + * ] + * } + * ] + * } + * } + * }, { + * data: { + * "name": "Asad1", + * "email": "asad.public@gmail.com", + * "age": 16, + * "adult": false, + * "address": { + * "street": "Sarojini", + * "city": "Deoria", + * "state": "UP", + * "postal_code": 274001, + * "country": "India", + * "phone_numbers": [ + * { + * "type": "home", + * "number": [ + * 7890, + * 3456 + * ] + * }, + * { + * "type": "work", + * "number": [ + * 9087, + * 6543 + * ] + * } + * ] + * } + * } + * }] + * } + */ +export interface InsertRequest { + /** ID of the vault. */ + vaultID: string; + /** Name of the table to perform the operation on. Can be defined at both the request body level and individual record level. If provided at both levels, the record-level `tableName` takes precedence. */ + tableName: string; + /** Data to insert as a list of records. */ + records: Skyflow.InsertRecordData[]; + upsert?: Skyflow.Upsert; +} diff --git a/flowvault/_generated_/rest/src/api/resources/records/client/requests/UpdateRequest.ts b/flowvault/_generated_/rest/src/api/resources/records/client/requests/UpdateRequest.ts new file mode 100644 index 00000000..a952479f --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/client/requests/UpdateRequest.ts @@ -0,0 +1,48 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef", + * tableName: "employees", + * records: [{ + * skyflowID: "97cdd1af-02ac-47eb-ab0d-8339dbef6ccb", + * data: { + * "name": "Vivek", + * "email": "asad.public5@gmail.com", + * "age": 25, + * "adult": true, + * "address": { + * "city": "aligarh", + * "country": "India" + * } + * } + * }, { + * skyflowID: "aed32bbc-e7a4-4c7b-8c88-7f52ce8cb066", + * data: { + * "name": "Asad", + * "email": "asad.public7@gmail.com", + * "age": 24, + * "adult": false, + * "address": { + * "city": "deoria", + * "country": "India" + * } + * } + * }] + * } + */ +export interface UpdateRequest { + /** ID of the vault. */ + vaultID: string; + /** Name of the table. */ + tableName: string; + /** Data to update as a list of records. */ + records: Skyflow.UpdateRecordData[]; + /** Type of update operation to perform. */ + updateType?: Skyflow.UpdateRequestUpdateType; +} diff --git a/flowvault/_generated_/rest/src/api/resources/records/client/requests/index.ts b/flowvault/_generated_/rest/src/api/resources/records/client/requests/index.ts new file mode 100644 index 00000000..29bbd957 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/client/requests/index.ts @@ -0,0 +1,4 @@ +export { type DeleteRequest } from "./DeleteRequest"; +export { type GetRequest } from "./GetRequest"; +export { type InsertRequest } from "./InsertRequest"; +export { type UpdateRequest } from "./UpdateRequest"; diff --git a/src/ _generated_/rest/api/resources/audit/index.ts b/flowvault/_generated_/rest/src/api/resources/records/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/index.ts rename to flowvault/_generated_/rest/src/api/resources/records/index.ts diff --git a/flowvault/_generated_/rest/src/api/resources/records/types/UpdateRequestUpdateType.ts b/flowvault/_generated_/rest/src/api/resources/records/types/UpdateRequestUpdateType.ts new file mode 100644 index 00000000..626c5af8 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/types/UpdateRequestUpdateType.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Type of update operation to perform. + */ +export type UpdateRequestUpdateType = "UPDATE" | "REPLACE"; +export const UpdateRequestUpdateType = { + Update: "UPDATE", + Replace: "REPLACE", +} as const; diff --git a/flowvault/_generated_/rest/src/api/resources/records/types/index.ts b/flowvault/_generated_/rest/src/api/resources/records/types/index.ts new file mode 100644 index 00000000..8b874903 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/records/types/index.ts @@ -0,0 +1 @@ +export * from "./UpdateRequestUpdateType"; diff --git a/flowvault/_generated_/rest/src/api/resources/tokens/client/Client.ts b/flowvault/_generated_/rest/src/api/resources/tokens/client/Client.ts new file mode 100644 index 00000000..a1325a11 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/tokens/client/Client.ts @@ -0,0 +1,265 @@ +/** + * 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 Tokens { + 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 Tokens { + constructor(protected readonly _options: Tokens.Options = {}) {} + + /** + * Returns the deterministic token previously issued for each supplied plaintext value within the specified token group. Only applicable to deterministic tokengroups. + * + * @param {Skyflow.GetTokensFromValuesRequest} request + * @param {Tokens.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.ForbiddenError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.TooManyRequestsError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.tokens.getTokens({ + * vaultID: "d408485953784308a000f8dcf81901ef", + * records: [] + * }) + */ + public getTokens( + request: Skyflow.GetTokensFromValuesRequest, + requestOptions?: Tokens.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getTokens(request, requestOptions)); + } + + private async __getTokens( + request: Skyflow.GetTokensFromValuesRequest, + requestOptions?: Tokens.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/records/getTokens", + ), + 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.GetTokensFromValuesResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 403: + throw new Skyflow.ForbiddenError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 429: + throw new Skyflow.TooManyRequestsError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/records/getTokens."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * Returns values that correspond to the specified tokens. + * + * @param {Skyflow.DetokenizeRequest} request + * @param {Tokens.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Skyflow.BadRequestError} + * @throws {@link Skyflow.UnauthorizedError} + * @throws {@link Skyflow.NotFoundError} + * @throws {@link Skyflow.InternalServerError} + * + * @example + * await client.tokens.detokenize({ + * vaultID: "d408485953784308a000f8dcf81901ef", + * tokens: ["RYtQoeJdSQ", "8bd036b6-8fe0-4176-945b-3a5a63e8fd18"] + * }) + */ + public detokenize( + request: Skyflow.DetokenizeRequest, + requestOptions?: Tokens.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__detokenize(request, requestOptions)); + } + + private async __detokenize( + request: Skyflow.DetokenizeRequest, + requestOptions?: Tokens.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/tokens/detokenize", + ), + 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.DetokenizeResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Skyflow.BadRequestError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 401: + throw new Skyflow.UnauthorizedError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 404: + throw new Skyflow.NotFoundError( + _response.error.body as Skyflow.ErrorResponse, + _response.rawResponse, + ); + case 500: + throw new Skyflow.InternalServerError( + _response.error.body as Skyflow.ErrorResponse, + _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 /v2/tokens/detokenize."); + case "unknown": + throw new errors.SkyflowError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/src/ _generated_/rest/api/resources/records/client/index.ts b/flowvault/_generated_/rest/src/api/resources/tokens/client/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/index.ts rename to flowvault/_generated_/rest/src/api/resources/tokens/client/index.ts diff --git a/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/DetokenizeRequest.ts b/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/DetokenizeRequest.ts new file mode 100644 index 00000000..0e0dacec --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/DetokenizeRequest.ts @@ -0,0 +1,21 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef", + * tokens: ["RYtQoeJdSQ", "8bd036b6-8fe0-4176-945b-3a5a63e8fd18"] + * } + */ +export interface DetokenizeRequest { + /** ID of the vault. */ + vaultID: string; + /** Token to be detokenized */ + tokens: string[]; + /** List of token groups to redact. */ + tokenGroupRedactions?: Skyflow.TokenGroupRedactions[]; +} diff --git a/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/GetTokensFromValuesRequest.ts b/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/GetTokensFromValuesRequest.ts new file mode 100644 index 00000000..8b9a0d52 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/GetTokensFromValuesRequest.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../../../../index"; + +/** + * @example + * { + * vaultID: "d408485953784308a000f8dcf81901ef", + * records: [] + * } + */ +export interface GetTokensFromValuesRequest { + /** ID of the vault. */ + vaultID: string; + /** Array of value/token-group pairs to look up. */ + records: Skyflow.GetTokensFromValuesRequestObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/index.ts b/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/index.ts new file mode 100644 index 00000000..82a675c6 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/resources/tokens/client/requests/index.ts @@ -0,0 +1,2 @@ +export { type GetTokensFromValuesRequest } from "./GetTokensFromValuesRequest"; +export { type DetokenizeRequest } from "./DetokenizeRequest"; diff --git a/src/ _generated_/rest/api/resources/query/index.ts b/flowvault/_generated_/rest/src/api/resources/tokens/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/query/index.ts rename to flowvault/_generated_/rest/src/api/resources/tokens/index.ts diff --git a/flowvault/_generated_/rest/src/api/types/ColumnRedactions.ts b/flowvault/_generated_/rest/src/api/types/ColumnRedactions.ts new file mode 100644 index 00000000..7ab8d8f7 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/ColumnRedactions.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface ColumnRedactions { + /** Name of the column to redact. */ + columnName: string; + /** Name of the redaction type. */ + redaction: string; +} diff --git a/flowvault/_generated_/rest/src/api/types/DeleteResponse.ts b/flowvault/_generated_/rest/src/api/types/DeleteResponse.ts new file mode 100644 index 00000000..947b5018 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/DeleteResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface DeleteResponse { + /** List of deleted records with skyflow ID and any partial errors. */ + records: Skyflow.DeleteResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/DeleteResponseObject.ts b/flowvault/_generated_/rest/src/api/types/DeleteResponseObject.ts new file mode 100644 index 00000000..56ad6d73 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/DeleteResponseObject.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface DeleteResponseObject { + /** Skyflow ID of the deleted record. */ + skyflowID: string; + /** Error message, if any. */ + error?: string; + /** HTTP status code of the response. */ + httpCode: number; +} diff --git a/flowvault/_generated_/rest/src/api/types/DetokenizeResponse.ts b/flowvault/_generated_/rest/src/api/types/DetokenizeResponse.ts new file mode 100644 index 00000000..4fdde282 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/DetokenizeResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface DetokenizeResponse { + /** Detokenized data */ + response: Skyflow.DetokenizeResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/DetokenizeResponseObject.ts b/flowvault/_generated_/rest/src/api/types/DetokenizeResponseObject.ts new file mode 100644 index 00000000..385efc65 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/DetokenizeResponseObject.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface DetokenizeResponseObject { + /** Token that was detokenized. */ + token: string; + value?: Skyflow.GoogleProtobufValue | undefined; + /** Name of the token group. */ + tokenGroupName?: string; + /** Error message, if any. */ + error?: string; + /** HTTP status code of the response. */ + httpCode?: number; + /** Additional metadata associated with the token, such as tableName or skyflowID. */ + metadata?: Record; +} diff --git a/src/ _generated_/rest/api/types/ErrorResponse.ts b/flowvault/_generated_/rest/src/api/types/ErrorResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/ErrorResponse.ts rename to flowvault/_generated_/rest/src/api/types/ErrorResponse.ts diff --git a/flowvault/_generated_/rest/src/api/types/ErrorResponseError.ts b/flowvault/_generated_/rest/src/api/types/ErrorResponseError.ts new file mode 100644 index 00000000..68e842c9 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/ErrorResponseError.ts @@ -0,0 +1,14 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface ErrorResponseError { + /** gRPC status codes. See https://grpc.io/docs/guides/status-codes. */ + grpc_code: number; + http_code: Skyflow.HttpCode; + http_status: string; + message: string; + details?: Record[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/ExecuteQueryRecordResponse.ts b/flowvault/_generated_/rest/src/api/types/ExecuteQueryRecordResponse.ts new file mode 100644 index 00000000..09cf4037 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/ExecuteQueryRecordResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface ExecuteQueryRecordResponse { + /** Fields and values for the record. */ + data?: Record; +} diff --git a/flowvault/_generated_/rest/src/api/types/ExecuteQueryResponse.ts b/flowvault/_generated_/rest/src/api/types/ExecuteQueryResponse.ts new file mode 100644 index 00000000..71321863 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/ExecuteQueryResponse.ts @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface ExecuteQueryResponse { + /** Records corresponding to the specified query. */ + records?: Skyflow.ExecuteQueryRecordResponse[]; + metadata?: Skyflow.ExecuteQueryResponseMetadata; +} diff --git a/flowvault/_generated_/rest/src/api/types/ExecuteQueryResponseMetadata.ts b/flowvault/_generated_/rest/src/api/types/ExecuteQueryResponseMetadata.ts new file mode 100644 index 00000000..1237e10b --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/ExecuteQueryResponseMetadata.ts @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Metadata for the query. + */ +export interface ExecuteQueryResponseMetadata { + /** Columns returned for the query. */ + columns?: string[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/FileDeleteRecord.ts b/flowvault/_generated_/rest/src/api/types/FileDeleteRecord.ts new file mode 100644 index 00000000..52b27fa3 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/FileDeleteRecord.ts @@ -0,0 +1,16 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface FileDeleteRecord { + /** Name of the table. */ + tableName: string; + /** Skyflow ID of the record containing the file column(s) to delete. Either skyflowID or uniqueValues must be specified, not both. */ + skyflowID?: string; + /** Names of the file columns to delete on this record. If any listed column has no file present, the request fails for this record. */ + columns: string[]; + /** List of unique constraint values used to look up the record(s) to delete, in place of skyflowID. If more than one object is provided, each is resolved independently and the specified columns are deleted from every matching record. */ + uniqueValues?: Skyflow.UniqueValue[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/FileDeleteResponse.ts b/flowvault/_generated_/rest/src/api/types/FileDeleteResponse.ts new file mode 100644 index 00000000..65be3f92 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/FileDeleteResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface FileDeleteResponse { + /** List of records (max 100) with the result of the delete operation. */ + records: Skyflow.FileDeleteResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/FileDeleteResponseObject.ts b/flowvault/_generated_/rest/src/api/types/FileDeleteResponseObject.ts new file mode 100644 index 00000000..8dfbd5cc --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/FileDeleteResponseObject.ts @@ -0,0 +1,16 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface FileDeleteResponseObject { + /** Skyflow ID of the record that was targeted. When the request specifies uniqueValues, this is the skyflowID resolved from the matching record. Null if the record failed validation before a skyflowID could be resolved. */ + skyflowID?: string; + /** Name of the table. */ + tableName: string; + /** Map of column name to deletion result. Populated only when the record's delete succeeds, with one entry per requested column with status DELETED. Null if any requested column has no file present, in which case error is populated instead. */ + data?: Record; + /** Error message describing any issue with the specific record. Null if no error occurred. */ + error?: string; + /** HTTP status code representing the result of the API request for this record. */ + httpCode: number; +} diff --git a/flowvault/_generated_/rest/src/api/types/FileUploadColumn.ts b/flowvault/_generated_/rest/src/api/types/FileUploadColumn.ts new file mode 100644 index 00000000..5fe790de --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/FileUploadColumn.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface FileUploadColumn { + /** Name of the column where the file is being uploaded. */ + column: string; + /** Name of the file being uploaded to the file column. If not provided, a random 16-byte UUID name is generated. */ + fileName?: string; +} diff --git a/flowvault/_generated_/rest/src/api/types/FileUploadRecord.ts b/flowvault/_generated_/rest/src/api/types/FileUploadRecord.ts new file mode 100644 index 00000000..7283fafc --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/FileUploadRecord.ts @@ -0,0 +1,14 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface FileUploadRecord { + /** Name of the table. */ + tableName: string; + /** Skyflow ID of the record. If not provided, a new record is created and its skyflowID is used for the file upload. */ + skyflowID?: string; + /** List of file columns to generate signed upload URLs for. */ + columns: Skyflow.FileUploadColumn[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/FileUploadResponse.ts b/flowvault/_generated_/rest/src/api/types/FileUploadResponse.ts new file mode 100644 index 00000000..92aafd48 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/FileUploadResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface FileUploadResponse { + /** List of records with signed upload URLs. */ + records: Skyflow.FileUploadResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/FileUploadResponseObject.ts b/flowvault/_generated_/rest/src/api/types/FileUploadResponseObject.ts new file mode 100644 index 00000000..413e0e67 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/FileUploadResponseObject.ts @@ -0,0 +1,16 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface FileUploadResponseObject { + /** Skyflow ID of the record. A new skyflowID is returned if one was not provided in the request. */ + skyflowID: string; + /** Name of the table. */ + tableName: string; + /** Map of column names to signed upload URLs. Each signed URL expires after 15 minutes by default. */ + data?: Record; + /** Error message describing any issue with the specific record. Null if no error occurred. */ + error?: string; + /** HTTP status code representing the result of the API request for this record. */ + httpCode: number; +} diff --git a/flowvault/_generated_/rest/src/api/types/GetRequestData.ts b/flowvault/_generated_/rest/src/api/types/GetRequestData.ts new file mode 100644 index 00000000..f1fa4f02 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/GetRequestData.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface GetRequestData { + /** Name of the table. */ + tableName: string; + /** Skyflow IDs of the records to return. */ + skyflowIDs: string[]; + /** List of columns to redact. */ + columnRedactions?: Skyflow.ColumnRedactions[]; + /** List of columns to return. */ + columns?: string[]; + /** List of unique constraint values to query records by data. */ + uniqueValues?: Skyflow.UniqueValue[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/GetResponse.ts b/flowvault/_generated_/rest/src/api/types/GetResponse.ts new file mode 100644 index 00000000..51cb78b7 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/GetResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface GetResponse { + /** List of fetched records. For file columns, the value in the data map is an object containing: fileName, mimeType, sizeBytes, fileStatus (PENDING | READY | FAILED | SCAN_ERROR), fileFailureReason (non-null only when fileStatus is FAILED), url (pre-signed download URL, non-null only when fileStatus is READY), and urlExpiresAt (UTC expiry of the URL). */ + records: Skyflow.RecordResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/GetTokensFromValuesRequestObject.ts b/flowvault/_generated_/rest/src/api/types/GetTokensFromValuesRequestObject.ts new file mode 100644 index 00000000..dfa91a91 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/GetTokensFromValuesRequestObject.ts @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface GetTokensFromValuesRequestObject { + value?: Skyflow.GoogleProtobufValue; + /** Name of the deterministic token group. */ + tokenGroupName: string; +} diff --git a/flowvault/_generated_/rest/src/api/types/GetTokensFromValuesResponse.ts b/flowvault/_generated_/rest/src/api/types/GetTokensFromValuesResponse.ts new file mode 100644 index 00000000..3a11cf9d --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/GetTokensFromValuesResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface GetTokensFromValuesResponse { + /** Array of token result objects, one per input entry, in the same order as the request. */ + records: Skyflow.TokenizeResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/GoogleProtobufValue.ts b/flowvault/_generated_/rest/src/api/types/GoogleProtobufValue.ts new file mode 100644 index 00000000..d3d90c4c --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/GoogleProtobufValue.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Represents a dynamically typed value which can be either null, a number, a string, a boolean, a recursive struct value, or a list of values. + */ +export type GoogleProtobufValue = unknown; diff --git a/flowvault/_generated_/rest/src/api/types/HttpCode.ts b/flowvault/_generated_/rest/src/api/types/HttpCode.ts new file mode 100644 index 00000000..e85dbda8 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/HttpCode.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * HTTP status codes. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status. + */ +export type HttpCode = number; diff --git a/flowvault/_generated_/rest/src/api/types/InsertRecordData.ts b/flowvault/_generated_/rest/src/api/types/InsertRecordData.ts new file mode 100644 index 00000000..dc23f38c --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/InsertRecordData.ts @@ -0,0 +1,15 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface InsertRecordData { + /** Columns and values for the record. */ + data: Record; + /** Columns and tokens for the record. */ + tokens?: Record; + /** Name of the table to insert data into. */ + tableName?: string; + upsert?: Skyflow.Upsert; +} diff --git a/flowvault/_generated_/rest/src/api/types/InsertResponse.ts b/flowvault/_generated_/rest/src/api/types/InsertResponse.ts new file mode 100644 index 00000000..02fec926 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/InsertResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface InsertResponse { + /** List of inserted records. */ + records: Skyflow.RecordResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/RecordResponseObject.ts b/flowvault/_generated_/rest/src/api/types/RecordResponseObject.ts new file mode 100644 index 00000000..376bd587 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/RecordResponseObject.ts @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface RecordResponseObject { + /** Skyflow ID for the inserted record */ + skyflowID?: string | null; + /** Columns and tokens for the record. */ + tokens?: Record; + /** Columns and values for the record. For file columns, the value is an object containing file metadata: fileName, mimeType, sizeBytes, fileStatus (PENDING | READY | FAILED | SCAN_ERROR), fileFailureReason (populated only when fileStatus is FAILED), url (pre-signed download URL, populated only when fileStatus is READY), and urlExpiresAt (UTC expiry of the URL). */ + data?: Record; + /** Columns and hashed values for the record. */ + hashedData?: Record; + /** Error message, if any. */ + error?: string | null; + /** HTTP status code of the response. */ + httpCode: number; + /** Name of the table that the record belongs to. */ + tableName?: string; +} diff --git a/flowvault/_generated_/rest/src/api/types/TokenGroupRedactions.ts b/flowvault/_generated_/rest/src/api/types/TokenGroupRedactions.ts new file mode 100644 index 00000000..a1cb1236 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/TokenGroupRedactions.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface TokenGroupRedactions { + /** Name of the token group to redact. */ + tokenGroupName?: string; + /** Name of the redaction to perform. */ + redaction?: string; +} diff --git a/flowvault/_generated_/rest/src/api/types/TokenizeResponseObject.ts b/flowvault/_generated_/rest/src/api/types/TokenizeResponseObject.ts new file mode 100644 index 00000000..74c20845 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/TokenizeResponseObject.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface TokenizeResponseObject { + /** Token that was generated. */ + token: string; + value?: Skyflow.GoogleProtobufValue | undefined; + /** Name of the token group. */ + tokenGroupName: string | null; + /** Error message, if any. */ + error?: string; + /** HTTP status code of the response. */ + httpCode?: number; +} diff --git a/flowvault/_generated_/rest/src/api/types/UniqueValue.ts b/flowvault/_generated_/rest/src/api/types/UniqueValue.ts new file mode 100644 index 00000000..2a0e2356 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/UniqueValue.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface UniqueValue { + /** Columns names and values for the unique value entry. */ + data: Record; +} diff --git a/flowvault/_generated_/rest/src/api/types/UpdateRecordData.ts b/flowvault/_generated_/rest/src/api/types/UpdateRecordData.ts new file mode 100644 index 00000000..92e97383 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/UpdateRecordData.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface UpdateRecordData { + /** Skyflow ID of the record to update. */ + skyflowID: string; + /** Columns and values for the record. */ + data: Record; + /** Columns and tokens for the record. */ + tokens?: Record; + /** Name of the table to update data in. */ + tableName?: string; + /** Type of update operation to perform. */ + updateType?: Skyflow.UpdateRecordDataUpdateType; +} diff --git a/flowvault/_generated_/rest/src/api/types/UpdateRecordDataUpdateType.ts b/flowvault/_generated_/rest/src/api/types/UpdateRecordDataUpdateType.ts new file mode 100644 index 00000000..dc4b89a4 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/UpdateRecordDataUpdateType.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Type of update operation to perform. + */ +export type UpdateRecordDataUpdateType = "UPDATE" | "REPLACE"; +export const UpdateRecordDataUpdateType = { + Update: "UPDATE", + Replace: "REPLACE", +} as const; diff --git a/flowvault/_generated_/rest/src/api/types/UpdateResponse.ts b/flowvault/_generated_/rest/src/api/types/UpdateResponse.ts new file mode 100644 index 00000000..ee933ba2 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/UpdateResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface UpdateResponse { + /** List of updated records. */ + records: Skyflow.RecordResponseObject[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/Upsert.ts b/flowvault/_generated_/rest/src/api/types/Upsert.ts new file mode 100644 index 00000000..4af2b7af --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/Upsert.ts @@ -0,0 +1,15 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +/** + * Upsert details. + */ +export interface Upsert { + /** Type of update operation to perform. */ + updateType?: Skyflow.UpsertUpdateType; + /** List of unique columns in the table that upsert operations use to identify if a record with matching values exists. If a matching record exists, the record updates with the specified values. If a matching record doesn't exist, the upsert operation inserts a new record. */ + uniqueColumns: string[]; +} diff --git a/flowvault/_generated_/rest/src/api/types/UpsertUpdateType.ts b/flowvault/_generated_/rest/src/api/types/UpsertUpdateType.ts new file mode 100644 index 00000000..07929246 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/UpsertUpdateType.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Type of update operation to perform. + */ +export type UpsertUpdateType = "UPDATE" | "REPLACE"; +export const UpsertUpdateType = { + Update: "UPDATE", + Replace: "REPLACE", +} as const; diff --git a/flowvault/_generated_/rest/src/api/types/index.ts b/flowvault/_generated_/rest/src/api/types/index.ts new file mode 100644 index 00000000..0ca892a1 --- /dev/null +++ b/flowvault/_generated_/rest/src/api/types/index.ts @@ -0,0 +1,34 @@ +export * from "./ColumnRedactions"; +export * from "./DeleteResponse"; +export * from "./DeleteResponseObject"; +export * from "./ExecuteQueryRecordResponse"; +export * from "./ExecuteQueryResponse"; +export * from "./ExecuteQueryResponseMetadata"; +export * from "./FileDeleteRecord"; +export * from "./FileDeleteResponse"; +export * from "./FileDeleteResponseObject"; +export * from "./FileUploadColumn"; +export * from "./FileUploadRecord"; +export * from "./FileUploadResponse"; +export * from "./FileUploadResponseObject"; +export * from "./DetokenizeResponse"; +export * from "./DetokenizeResponseObject"; +export * from "./GetTokensFromValuesRequestObject"; +export * from "./GetTokensFromValuesResponse"; +export * from "./TokenizeResponseObject"; +export * from "./GetRequestData"; +export * from "./GetResponse"; +export * from "./GoogleProtobufValue"; +export * from "./InsertRecordData"; +export * from "./InsertResponse"; +export * from "./RecordResponseObject"; +export * from "./TokenGroupRedactions"; +export * from "./UniqueValue"; +export * from "./UpdateRecordDataUpdateType"; +export * from "./UpdateRecordData"; +export * from "./UpdateResponse"; +export * from "./UpsertUpdateType"; +export * from "./Upsert"; +export * from "./HttpCode"; +export * from "./ErrorResponseError"; +export * from "./ErrorResponse"; diff --git a/flowvault/_generated_/rest/src/core/auth/BasicAuth.ts b/flowvault/_generated_/rest/src/core/auth/BasicAuth.ts new file mode 100644 index 00000000..146df215 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/auth/BasicAuth.ts @@ -0,0 +1,31 @@ +import { Base64 } from "js-base64"; + +export interface BasicAuth { + username: string; + password: string; +} + +const BASIC_AUTH_HEADER_PREFIX = /^Basic /i; + +export const BasicAuth = { + toAuthorizationHeader: (basicAuth: BasicAuth | undefined): string | undefined => { + if (basicAuth == null) { + return undefined; + } + const token = Base64.encode(`${basicAuth.username}:${basicAuth.password}`); + return `Basic ${token}`; + }, + fromAuthorizationHeader: (header: string): BasicAuth => { + const credentials = header.replace(BASIC_AUTH_HEADER_PREFIX, ""); + const decoded = Base64.decode(credentials); + const [username, password] = decoded.split(":", 2); + + if (username == null || password == null) { + throw new Error("Invalid basic auth"); + } + return { + username, + password, + }; + }, +}; diff --git a/flowvault/_generated_/rest/src/core/auth/BearerToken.ts b/flowvault/_generated_/rest/src/core/auth/BearerToken.ts new file mode 100644 index 00000000..fe987fc9 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/auth/BearerToken.ts @@ -0,0 +1,15 @@ +export type BearerToken = string; + +const BEARER_AUTH_HEADER_PREFIX = /^Bearer /i; + +export const BearerToken = { + toAuthorizationHeader: (token: BearerToken | undefined): string | undefined => { + if (token == null) { + return undefined; + } + return `Bearer ${token}`; + }, + fromAuthorizationHeader: (header: string): BearerToken => { + return header.replace(BEARER_AUTH_HEADER_PREFIX, "").trim() as BearerToken; + }, +}; diff --git a/flowvault/_generated_/rest/src/core/auth/index.ts b/flowvault/_generated_/rest/src/core/auth/index.ts new file mode 100644 index 00000000..ee293b34 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/auth/index.ts @@ -0,0 +1,2 @@ +export { BasicAuth } from "./BasicAuth"; +export { BearerToken } from "./BearerToken"; diff --git a/flowvault/_generated_/rest/src/core/fetcher/APIResponse.ts b/flowvault/_generated_/rest/src/core/fetcher/APIResponse.ts new file mode 100644 index 00000000..6335291b --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/APIResponse.ts @@ -0,0 +1,23 @@ +import { RawResponse } from "./RawResponse"; + +/** + * The response of an API call. + * It is a successful response or a failed response. + */ +export type APIResponse = SuccessfulResponse | FailedResponse; + +export interface SuccessfulResponse { + ok: true; + body: T; + /** + * @deprecated Use `rawResponse` instead + */ + headers?: Record; + rawResponse: RawResponse; +} + +export interface FailedResponse { + ok: false; + error: T; + rawResponse: RawResponse; +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/Fetcher.ts b/flowvault/_generated_/rest/src/core/fetcher/Fetcher.ts new file mode 100644 index 00000000..336ee10b --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/Fetcher.ts @@ -0,0 +1,151 @@ +import { toJson } from "../json"; +import { APIResponse } from "./APIResponse"; +import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse"; +import { createRequestUrl } from "./createRequestUrl"; +import { getFetchFn } from "./getFetchFn"; +import { getRequestBody } from "./getRequestBody"; +import { getResponseBody } from "./getResponseBody"; +import { makeRequest } from "./makeRequest"; +import { requestWithRetries } from "./requestWithRetries"; + +export type FetchFunction = (args: Fetcher.Args) => Promise>; + +export declare namespace Fetcher { + export interface Args { + url: string; + method: string; + contentType?: string; + headers?: Record; + queryParameters?: Record; + body?: unknown; + timeoutMs?: number; + maxRetries?: number; + withCredentials?: boolean; + abortSignal?: AbortSignal; + requestType?: "json" | "file" | "bytes"; + responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer"; + duplex?: "half"; + } + + export type Error = FailedStatusCodeError | NonJsonError | TimeoutError | UnknownError; + + export interface FailedStatusCodeError { + reason: "status-code"; + statusCode: number; + body: unknown; + } + + export interface NonJsonError { + reason: "non-json"; + statusCode: number; + rawBody: string; + } + + export interface TimeoutError { + reason: "timeout"; + } + + export interface UnknownError { + reason: "unknown"; + errorMessage: string; + } +} + +export async function fetcherImpl(args: Fetcher.Args): Promise> { + const headers: Record = {}; + if (args.body !== undefined && args.contentType != null) { + headers["Content-Type"] = args.contentType; + } + + if (args.headers != null) { + for (const [key, value] of Object.entries(args.headers)) { + if (value != null) { + headers[key] = value; + } + } + } + + const url = createRequestUrl(args.url, args.queryParameters); + const requestBody: BodyInit | undefined = await getRequestBody({ + body: args.body, + type: args.requestType === "json" ? "json" : "other", + }); + const fetchFn = await getFetchFn(); + + try { + const response = await requestWithRetries( + async () => + makeRequest( + fetchFn, + url, + args.method, + headers, + requestBody, + args.timeoutMs, + args.abortSignal, + args.withCredentials, + args.duplex, + ), + args.maxRetries, + ); + const responseBody = await getResponseBody(response, args.responseType); + + if (response.status >= 200 && response.status < 400) { + return { + ok: true, + body: responseBody as R, + headers: response.headers, + rawResponse: toRawResponse(response), + }; + } else { + return { + ok: false, + error: { + reason: "status-code", + statusCode: response.status, + body: responseBody, + }, + rawResponse: toRawResponse(response), + }; + } + } catch (error) { + if (args.abortSignal != null && args.abortSignal.aborted) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: "The user aborted a request", + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error && error.name === "AbortError") { + return { + ok: false, + error: { + reason: "timeout", + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: error.message, + }, + rawResponse: unknownRawResponse, + }; + } + + return { + ok: false, + error: { + reason: "unknown", + errorMessage: toJson(error), + }, + rawResponse: unknownRawResponse, + }; + } +} + +export const fetcher: FetchFunction = fetcherImpl; diff --git a/flowvault/_generated_/rest/src/core/fetcher/HttpResponsePromise.ts b/flowvault/_generated_/rest/src/core/fetcher/HttpResponsePromise.ts new file mode 100644 index 00000000..6b0bc4fc --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/HttpResponsePromise.ts @@ -0,0 +1,116 @@ +import { WithRawResponse } from "./RawResponse"; + +/** + * A promise that returns the parsed response and lets you retrieve the raw response too. + */ +export class HttpResponsePromise extends Promise { + private innerPromise: Promise>; + private unwrappedPromise: Promise | undefined; + + private constructor(promise: Promise>) { + // Initialize with a no-op to avoid premature parsing + super((resolve) => { + resolve(undefined as unknown as T); + }); + this.innerPromise = promise; + } + + /** + * Creates an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @param args - Arguments to pass to the function. + * @returns An `HttpResponsePromise` instance. + */ + public static fromFunction Promise>, T>( + fn: F, + ...args: Parameters + ): HttpResponsePromise { + return new HttpResponsePromise(fn(...args)); + } + + /** + * Creates a function that returns an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @returns A function that returns an `HttpResponsePromise` instance. + */ + public static interceptFunction< + F extends (...args: never[]) => Promise>, + T = Awaited>["data"], + >(fn: F): (...args: Parameters) => HttpResponsePromise { + return (...args: Parameters): HttpResponsePromise => { + return HttpResponsePromise.fromPromise(fn(...args)); + }; + } + + /** + * Creates an `HttpResponsePromise` from an existing promise. + * + * @param promise - A promise resolving to a `WithRawResponse` object. + * @returns An `HttpResponsePromise` instance. + */ + public static fromPromise(promise: Promise>): HttpResponsePromise { + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from an executor function. + * + * @param executor - A function that takes resolve and reject callbacks to create a promise. + * @returns An `HttpResponsePromise` instance. + */ + public static fromExecutor( + executor: (resolve: (value: WithRawResponse) => void, reject: (reason?: unknown) => void) => void, + ): HttpResponsePromise { + const promise = new Promise>(executor); + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from a resolved result. + * + * @param result - A `WithRawResponse` object to resolve immediately. + * @returns An `HttpResponsePromise` instance. + */ + public static fromResult(result: WithRawResponse): HttpResponsePromise { + const promise = Promise.resolve(result); + return new HttpResponsePromise(promise); + } + + private unwrap(): Promise { + if (!this.unwrappedPromise) { + this.unwrappedPromise = this.innerPromise.then(({ data }) => data); + } + return this.unwrappedPromise; + } + + /** @inheritdoc */ + public override then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ): Promise { + return this.unwrap().then(onfulfilled, onrejected); + } + + /** @inheritdoc */ + public override catch( + onrejected?: ((reason: unknown) => TResult | PromiseLike) | null, + ): Promise { + return this.unwrap().catch(onrejected); + } + + /** @inheritdoc */ + public override finally(onfinally?: (() => void) | null): Promise { + return this.unwrap().finally(onfinally); + } + + /** + * Retrieves the data and raw response. + * + * @returns A promise resolving to a `WithRawResponse` object. + */ + public async withRawResponse(): Promise> { + return await this.innerPromise; + } +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/RawResponse.ts b/flowvault/_generated_/rest/src/core/fetcher/RawResponse.ts new file mode 100644 index 00000000..c975f23c --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/RawResponse.ts @@ -0,0 +1,59 @@ +/** + * The raw response from the fetch call excluding the body. + */ +export type RawResponse = Omit< + { + [K in keyof Response as Response[K] extends Function ? never : K]: Response[K]; // strips out functions + }, + "ok" | "body" | "bodyUsed" +>; // strips out body and bodyUsed + +/** + * A raw response indicating that the request was aborted. + */ +export const abortRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 499, + statusText: "Client Closed Request", + type: "error", + url: "", +} as const; + +/** + * A raw response indicating an unknown error. + */ +export const unknownRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 0, + statusText: "Unknown Error", + type: "error", + url: "", +} as const; + +/** + * Converts a `RawResponse` object into a `RawResponse` by extracting its properties, + * excluding the `body` and `bodyUsed` fields. + * + * @param response - The `RawResponse` object to convert. + * @returns A `RawResponse` object containing the extracted properties of the input response. + */ +export function toRawResponse(response: Response): RawResponse { + return { + headers: response.headers, + redirected: response.redirected, + status: response.status, + statusText: response.statusText, + type: response.type, + url: response.url, + }; +} + +/** + * Creates a `RawResponse` from a standard `Response` object. + */ +export interface WithRawResponse { + readonly data: T; + readonly rawResponse: RawResponse; +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/Supplier.ts b/flowvault/_generated_/rest/src/core/fetcher/Supplier.ts new file mode 100644 index 00000000..867c931c --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/Supplier.ts @@ -0,0 +1,11 @@ +export type Supplier = T | Promise | (() => T | Promise); + +export const Supplier = { + get: async (supplier: Supplier): Promise => { + if (typeof supplier === "function") { + return (supplier as () => T)(); + } else { + return supplier; + } + }, +}; diff --git a/flowvault/_generated_/rest/src/core/fetcher/createRequestUrl.ts b/flowvault/_generated_/rest/src/core/fetcher/createRequestUrl.ts new file mode 100644 index 00000000..f1157ce7 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/createRequestUrl.ts @@ -0,0 +1,10 @@ +import qs from "qs"; + +export function createRequestUrl( + baseUrl: string, + queryParameters?: Record, +): string { + return Object.keys(queryParameters ?? {}).length > 0 + ? `${baseUrl}?${qs.stringify(queryParameters, { arrayFormat: "repeat" })}` + : baseUrl; +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/getFetchFn.ts b/flowvault/_generated_/rest/src/core/fetcher/getFetchFn.ts new file mode 100644 index 00000000..9fd9bfc4 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/getFetchFn.ts @@ -0,0 +1,25 @@ +import { RUNTIME } from "../runtime"; + +/** + * Returns a fetch function based on the runtime + */ +export async function getFetchFn(): Promise { + // In Node.js 18+ environments, use native fetch + if (RUNTIME.type === "node" && RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + return fetch; + } + + // In Node.js 18 or lower environments, the SDK always uses`node-fetch`. + if (RUNTIME.type === "node") { + return (await import("node-fetch")).default as any; + } + + // Otherwise the SDK uses global fetch if available, + // and falls back to node-fetch. + if (typeof fetch == "function") { + return fetch; + } + + // Defaults to node `node-fetch` if global fetch isn't available + return (await import("node-fetch")).default as any; +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/getHeader.ts b/flowvault/_generated_/rest/src/core/fetcher/getHeader.ts new file mode 100644 index 00000000..50f922b0 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/getHeader.ts @@ -0,0 +1,8 @@ +export function getHeader(headers: Record, header: string): string | undefined { + for (const [headerKey, headerValue] of Object.entries(headers)) { + if (headerKey.toLowerCase() === header.toLowerCase()) { + return headerValue; + } + } + return undefined; +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/getRequestBody.ts b/flowvault/_generated_/rest/src/core/fetcher/getRequestBody.ts new file mode 100644 index 00000000..fce5589c --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/getRequestBody.ts @@ -0,0 +1,16 @@ +import { toJson } from "../json"; + +export declare namespace GetRequestBody { + interface Args { + body: unknown; + type: "json" | "file" | "bytes" | "other"; + } +} + +export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { + if (type.includes("json")) { + return toJson(body); + } else { + return body as BodyInit; + } +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/getResponseBody.ts b/flowvault/_generated_/rest/src/core/fetcher/getResponseBody.ts new file mode 100644 index 00000000..d046e6ea --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/getResponseBody.ts @@ -0,0 +1,34 @@ +import { chooseStreamWrapper } from "./stream-wrappers/chooseStreamWrapper"; + +export async function getResponseBody(response: Response, responseType?: string): Promise { + if (response.body != null && responseType === "blob") { + return await response.blob(); + } else if (response.body != null && responseType === "arrayBuffer") { + return await response.arrayBuffer(); + } else if (response.body != null && responseType === "sse") { + return response.body; + } else if (response.body != null && responseType === "streaming") { + return chooseStreamWrapper(response.body); + } else if (response.body != null && responseType === "text") { + return await response.text(); + } else { + const text = await response.text(); + if (text.length > 0) { + try { + let responseBody = JSON.parse(text); + return responseBody; + } catch (err) { + return { + ok: false, + error: { + reason: "non-json", + statusCode: response.status, + rawBody: text, + }, + }; + } + } else { + return undefined; + } + } +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/index.ts b/flowvault/_generated_/rest/src/core/fetcher/index.ts new file mode 100644 index 00000000..249f5176 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/index.ts @@ -0,0 +1,8 @@ +export type { APIResponse } from "./APIResponse"; +export { fetcher } from "./Fetcher"; +export type { Fetcher, FetchFunction } from "./Fetcher"; +export { getHeader } from "./getHeader"; +export { Supplier } from "./Supplier"; +export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse"; +export type { RawResponse, WithRawResponse } from "./RawResponse"; +export { HttpResponsePromise } from "./HttpResponsePromise"; diff --git a/flowvault/_generated_/rest/src/core/fetcher/makeRequest.ts b/flowvault/_generated_/rest/src/core/fetcher/makeRequest.ts new file mode 100644 index 00000000..1af42bb9 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/makeRequest.ts @@ -0,0 +1,44 @@ +import { anySignal, getTimeoutSignal } from "./signals"; + +export const makeRequest = async ( + fetchFn: (url: string, init: RequestInit) => Promise, + url: string, + method: string, + headers: Record, + requestBody: BodyInit | undefined, + timeoutMs?: number, + abortSignal?: AbortSignal, + withCredentials?: boolean, + duplex?: "half", +): Promise => { + const signals: AbortSignal[] = []; + + // Add timeout signal + let timeoutAbortId: NodeJS.Timeout | undefined = undefined; + if (timeoutMs != null) { + const { signal, abortId } = getTimeoutSignal(timeoutMs); + timeoutAbortId = abortId; + signals.push(signal); + } + + // Add arbitrary signal + if (abortSignal != null) { + signals.push(abortSignal); + } + let newSignals = anySignal(signals); + const response = await fetchFn(url, { + method: method, + headers, + body: requestBody, + signal: newSignals, + credentials: withCredentials ? "include" : undefined, + // @ts-ignore + duplex, + }); + + if (timeoutAbortId != null) { + clearTimeout(timeoutAbortId); + } + + return response; +}; diff --git a/flowvault/_generated_/rest/src/core/fetcher/requestWithRetries.ts b/flowvault/_generated_/rest/src/core/fetcher/requestWithRetries.ts new file mode 100644 index 00000000..add3cce0 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/requestWithRetries.ts @@ -0,0 +1,33 @@ +const INITIAL_RETRY_DELAY = 1000; // in milliseconds +const MAX_RETRY_DELAY = 60000; // in milliseconds +const DEFAULT_MAX_RETRIES = 2; +const JITTER_FACTOR = 0.2; // 20% random jitter + +function addJitter(delay: number): number { + // Generate a random value between -JITTER_FACTOR and +JITTER_FACTOR + const jitterMultiplier = 1 + (Math.random() * 2 - 1) * JITTER_FACTOR; + return delay * jitterMultiplier; +} + +export async function requestWithRetries( + requestFn: () => Promise, + maxRetries: number = DEFAULT_MAX_RETRIES, +): Promise { + let response: Response = await requestFn(); + + for (let i = 0; i < maxRetries; ++i) { + if ([408, 429].includes(response.status) || response.status >= 500) { + // Calculate base delay using exponential backoff (in milliseconds) + const baseDelay = Math.min(INITIAL_RETRY_DELAY * Math.pow(2, i), MAX_RETRY_DELAY); + + // Add jitter to the delay + const delayWithJitter = addJitter(baseDelay); + + await new Promise((resolve) => setTimeout(resolve, delayWithJitter)); + response = await requestFn(); + } else { + break; + } + } + return response!; +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/signals.ts b/flowvault/_generated_/rest/src/core/fetcher/signals.ts new file mode 100644 index 00000000..a8d32a2e --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/signals.ts @@ -0,0 +1,38 @@ +const TIMEOUT = "timeout"; + +export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: NodeJS.Timeout } { + const controller = new AbortController(); + const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); + return { signal: controller.signal, abortId }; +} + +/** + * Returns an abort signal that is getting aborted when + * at least one of the specified abort signals is aborted. + * + * Requires at least node.js 18. + */ +export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { + // Allowing signals to be passed either as array + // of signals or as multiple arguments. + const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as AbortSignal[]; + + const controller = new AbortController(); + + for (const signal of signals) { + if (signal.aborted) { + // Exiting early if one of the signals + // is already aborted. + controller.abort((signal as any)?.reason); + break; + } + + // Listening for signals and removing the listeners + // when at least one symbol is aborted. + signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { + signal: controller.signal, + }); + } + + return controller.signal; +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts b/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts new file mode 100644 index 00000000..7d83aef2 --- /dev/null +++ b/flowvault/_generated_/rest/src/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/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts b/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts new file mode 100644 index 00000000..23c01a1a --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts @@ -0,0 +1,107 @@ +import type { Readable, Writable } from "readable-stream"; + +import { EventCallback, StreamWrapper } from "./chooseStreamWrapper"; + +export class NodePre18StreamWrapper implements StreamWrapper { + private readableStream: Readable; + private encoding: string | undefined; + + constructor(readableStream: Readable) { + this.readableStream = readableStream; + } + + public on(event: string, callback: EventCallback): void { + this.readableStream.on(event, callback); + } + + public off(event: string, callback: EventCallback): void { + this.readableStream.off(event, callback); + } + + public pipe(dest: Writable): Writable { + this.readableStream.pipe(dest); + return dest; + } + + public pipeTo(dest: Writable): Writable { + return this.pipe(dest); + } + + public unpipe(dest?: Writable): void { + if (dest) { + this.readableStream.unpipe(dest); + } else { + this.readableStream.unpipe(); + } + } + + public destroy(error?: Error): void { + this.readableStream.destroy(error); + } + + public pause(): void { + this.readableStream.pause(); + } + + public resume(): void { + this.readableStream.resume(); + } + + public get isPaused(): boolean { + return this.readableStream.isPaused(); + } + + public async read(): Promise { + return new Promise((resolve, reject) => { + const chunk = this.readableStream.read(); + if (chunk) { + resolve(chunk); + } else { + this.readableStream.once("readable", () => { + const chunk = this.readableStream.read(); + resolve(chunk); + }); + this.readableStream.once("error", reject); + } + }); + } + + public setEncoding(encoding?: string): void { + this.readableStream.setEncoding(encoding as BufferEncoding); + this.encoding = encoding; + } + + public async text(): Promise { + const chunks: Uint8Array[] = []; + const encoder = new TextEncoder(); + this.readableStream.setEncoding((this.encoding || "utf-8") as BufferEncoding); + + for await (const chunk of this.readableStream) { + chunks.push(encoder.encode(chunk)); + } + + const decoder = new TextDecoder(this.encoding || "utf-8"); + return decoder.decode(Buffer.concat(chunks)); + } + + public async json(): Promise { + const text = await this.text(); + return JSON.parse(text); + } + + public [Symbol.asyncIterator](): AsyncIterableIterator { + const readableStream = this.readableStream; + const iterator = readableStream[Symbol.asyncIterator](); + + // Create and return an async iterator that yields buffers + return { + async next(): Promise> { + const { value, done } = await iterator.next(); + return { value: value as Buffer, done }; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; + } +} diff --git a/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts b/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts new file mode 100644 index 00000000..e5e8b680 --- /dev/null +++ b/flowvault/_generated_/rest/src/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/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts b/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts new file mode 100644 index 00000000..8c7492fc --- /dev/null +++ b/flowvault/_generated_/rest/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts @@ -0,0 +1,34 @@ +import type { Readable } from "readable-stream"; + +import { RUNTIME } from "../../runtime"; + +export type EventCallback = (data?: any) => void; + +export interface StreamWrapper { + setEncoding(encoding?: string): void; + on(event: string, callback: EventCallback): void; + off(event: string, callback: EventCallback): void; + pipe(dest: WritableStream): WritableStream; + pipeTo(dest: WritableStream): WritableStream; + unpipe(dest?: WritableStream): void; + destroy(error?: Error): void; + pause(): void; + resume(): void; + get isPaused(): boolean; + read(): Promise; + text(): Promise; + json(): Promise; + [Symbol.asyncIterator](): AsyncIterableIterator; +} + +export async function chooseStreamWrapper(responseBody: any): Promise>> { + if (RUNTIME.type === "node" && RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + return new (await import("./Node18UniversalStreamWrapper")).Node18UniversalStreamWrapper( + responseBody as ReadableStream, + ); + } else if (RUNTIME.type !== "node" && typeof fetch === "function") { + return new (await import("./UndiciStreamWrapper")).UndiciStreamWrapper(responseBody as ReadableStream); + } else { + return new (await import("./NodePre18StreamWrapper")).NodePre18StreamWrapper(responseBody as Readable); + } +} diff --git a/flowvault/_generated_/rest/src/core/index.ts b/flowvault/_generated_/rest/src/core/index.ts new file mode 100644 index 00000000..c752c6a6 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/index.ts @@ -0,0 +1,3 @@ +export * from "./fetcher"; +export * from "./auth"; +export * from "./runtime"; diff --git a/flowvault/_generated_/rest/src/core/json.ts b/flowvault/_generated_/rest/src/core/json.ts new file mode 100644 index 00000000..c052f324 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/json.ts @@ -0,0 +1,27 @@ +/** + * Serialize a value to JSON + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + * @returns JSON string + */ +export const toJson = ( + value: unknown, + replacer?: (this: unknown, key: string, value: unknown) => unknown, + space?: string | number, +): string => { + return JSON.stringify(value, replacer, space); +}; + +/** + * Parse JSON string to object, array, or other type + * @param text A valid JSON string. + * @param reviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is. + * @returns Parsed object, array, or other type + */ +export function fromJson( + text: string, + reviver?: (this: unknown, key: string, value: unknown) => unknown, +): T { + return JSON.parse(text, reviver); +} diff --git a/flowvault/_generated_/rest/src/core/runtime/index.ts b/flowvault/_generated_/rest/src/core/runtime/index.ts new file mode 100644 index 00000000..5c76dbb1 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/runtime/index.ts @@ -0,0 +1 @@ +export { RUNTIME } from "./runtime"; diff --git a/flowvault/_generated_/rest/src/core/runtime/runtime.ts b/flowvault/_generated_/rest/src/core/runtime/runtime.ts new file mode 100644 index 00000000..a9750175 --- /dev/null +++ b/flowvault/_generated_/rest/src/core/runtime/runtime.ts @@ -0,0 +1,131 @@ +interface DenoGlobal { + version: { + deno: string; + }; +} + +interface BunGlobal { + version: string; +} + +declare const Deno: DenoGlobal | undefined; +declare const Bun: BunGlobal | undefined; +declare const EdgeRuntime: string | undefined; + +/** + * A constant that indicates which environment and version the SDK is running in. + */ +export const RUNTIME: Runtime = evaluateRuntime(); + +export interface Runtime { + type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd" | "edge-runtime"; + version?: string; + parsedVersion?: number; +} + +function evaluateRuntime(): Runtime { + /** + * A constant that indicates whether the environment the code is running is a Web Browser. + */ + const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; + if (isBrowser) { + return { + type: "browser", + version: window.navigator.userAgent, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Cloudflare. + * https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent + */ + const isCloudflare = typeof globalThis !== "undefined" && globalThis?.navigator?.userAgent === "Cloudflare-Workers"; + if (isCloudflare) { + return { + type: "workerd", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Edge Runtime. + * https://vercel.com/docs/functions/runtimes/edge-runtime#check-if-you're-running-on-the-edge-runtime + */ + const isEdgeRuntime = typeof EdgeRuntime === "string"; + if (isEdgeRuntime) { + return { + type: "edge-runtime", + }; + } + + /** + * A constant that indicates whether the environment the code is running is a Web Worker. + */ + const isWebWorker = + typeof self === "object" && + // @ts-ignore + typeof self?.importScripts === "function" && + (self.constructor?.name === "DedicatedWorkerGlobalScope" || + self.constructor?.name === "ServiceWorkerGlobalScope" || + self.constructor?.name === "SharedWorkerGlobalScope"); + if (isWebWorker) { + return { + type: "web-worker", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Deno. + * FYI Deno spoofs process.versions.node, see https://deno.land/std@0.177.0/node/process.ts?s=versions + */ + const isDeno = + typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"; + if (isDeno) { + return { + type: "deno", + version: Deno.version.deno, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Bun.sh. + */ + const isBun = typeof Bun !== "undefined" && typeof Bun.version !== "undefined"; + if (isBun) { + return { + type: "bun", + version: Bun.version, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Node.JS. + */ + const isNode = + typeof process !== "undefined" && + "version" in process && + !!process.version && + "versions" in process && + !!process.versions?.node; + if (isNode) { + return { + type: "node", + version: process.versions.node, + parsedVersion: Number(process.versions.node.split(".")[0]), + }; + } + + /** + * A constant that indicates whether the environment the code is running is in React-Native. + * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js + */ + const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; + if (isReactNative) { + return { + type: "react-native", + }; + } + + return { + type: "unknown", + }; +} diff --git a/flowvault/_generated_/rest/src/environments.ts b/flowvault/_generated_/rest/src/environments.ts new file mode 100644 index 00000000..c5f61083 --- /dev/null +++ b/flowvault/_generated_/rest/src/environments.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export const SkyflowEnvironment = { + Production: "https://%7B%7Bvault_url%7D%7D", + Sandbox: "https://%7B%7Bvault_url%7D%7D", +} as const; + +export type SkyflowEnvironment = typeof SkyflowEnvironment.Production | typeof SkyflowEnvironment.Sandbox; diff --git a/flowvault/_generated_/rest/src/errors/SkyflowError.ts b/flowvault/_generated_/rest/src/errors/SkyflowError.ts new file mode 100644 index 00000000..003768c2 --- /dev/null +++ b/flowvault/_generated_/rest/src/errors/SkyflowError.ts @@ -0,0 +1,55 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../core"; +import { toJson } from "../core/json"; + +export class SkyflowError extends Error { + public readonly statusCode?: number; + public readonly body?: unknown; + public readonly rawResponse?: core.RawResponse; + + constructor({ + message, + statusCode, + body, + rawResponse, + }: { + message?: string; + statusCode?: number; + body?: unknown; + rawResponse?: core.RawResponse; + }) { + super(buildMessage({ message, statusCode, body })); + Object.setPrototypeOf(this, SkyflowError.prototype); + this.statusCode = statusCode; + this.body = body; + this.rawResponse = rawResponse; + } +} + +function buildMessage({ + message, + statusCode, + body, +}: { + message: string | undefined; + statusCode: number | undefined; + body: unknown | undefined; +}): string { + let lines: string[] = []; + if (message != null) { + lines.push(message); + } + + if (statusCode != null) { + lines.push(`Status code: ${statusCode.toString()}`); + } + + if (body != null) { + lines.push(`Body: ${toJson(body, undefined, 2)}`); + } + + return lines.join("\n"); +} diff --git a/flowvault/_generated_/rest/src/errors/SkyflowTimeoutError.ts b/flowvault/_generated_/rest/src/errors/SkyflowTimeoutError.ts new file mode 100644 index 00000000..75024f63 --- /dev/null +++ b/flowvault/_generated_/rest/src/errors/SkyflowTimeoutError.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export class SkyflowTimeoutError extends Error { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, SkyflowTimeoutError.prototype); + } +} diff --git a/flowvault/_generated_/rest/src/errors/index.ts b/flowvault/_generated_/rest/src/errors/index.ts new file mode 100644 index 00000000..5567f00d --- /dev/null +++ b/flowvault/_generated_/rest/src/errors/index.ts @@ -0,0 +1,2 @@ +export { SkyflowError } from "./SkyflowError"; +export { SkyflowTimeoutError } from "./SkyflowTimeoutError"; diff --git a/flowvault/_generated_/rest/src/index.ts b/flowvault/_generated_/rest/src/index.ts new file mode 100644 index 00000000..0706e084 --- /dev/null +++ b/flowvault/_generated_/rest/src/index.ts @@ -0,0 +1,4 @@ +export * as Skyflow from "./api"; +export { SkyflowClient } from "./Client"; +export { SkyflowEnvironment } from "./environments"; +export { SkyflowError, SkyflowTimeoutError } from "./errors"; diff --git a/flowvault/_generated_/rest/src/version.ts b/flowvault/_generated_/rest/src/version.ts new file mode 100644 index 00000000..e3655fb3 --- /dev/null +++ b/flowvault/_generated_/rest/src/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = "1.0.21"; diff --git a/flowvault/api-extractor.json b/flowvault/api-extractor.json new file mode 100644 index 00000000..0f7db7c9 --- /dev/null +++ b/flowvault/api-extractor.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "projectFolder": ".", + "mainEntryPointFilePath": "/dist/index.d.ts", + "bundledPackages": [], + "compiler": { + "tsconfigFilePath": "/tsconfig.dts.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" }, + "ae-forgotten-export": { "logLevel": "none" } + }, + "tsdocMessageReporting": { + "default": { "logLevel": "none" } + } + } +} diff --git a/flowvault/api-report/skyflow-flowvault-node.api.md b/flowvault/api-report/skyflow-flowvault-node.api.md new file mode 100644 index 00000000..1f75bf26 --- /dev/null +++ b/flowvault/api-report/skyflow-flowvault-node.api.md @@ -0,0 +1,571 @@ +## API Report File for "skyflow-flowvault-node" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { AuthInfo } from 'skyflow-common'; +import { BaseDeleteFilesRequest } from 'skyflow-common'; +import { BaseDeleteFilesResponse } from 'skyflow-common'; +import { BaseDeleteRequest } from 'skyflow-common'; +import { BaseDeleteResponse } from 'skyflow-common'; +import { BaseDetokenizeRequest } from 'skyflow-common'; +import { BaseDetokenizeResponse } from 'skyflow-common'; +import { BaseGetRequest } from 'skyflow-common'; +import { BaseGetResponse } from 'skyflow-common'; +import { BaseGetTokensRequest } from 'skyflow-common'; +import { BaseGetTokensResponse } from 'skyflow-common'; +import { BaseInsertRequest } from 'skyflow-common'; +import { BaseInsertResponse } from 'skyflow-common'; +import { BaseOptions } from 'skyflow-common'; +import { BaseQueryRequest } from 'skyflow-common'; +import { BaseQueryResponse } from 'skyflow-common'; +import { BaseSkyflow } from 'skyflow-common'; +import { BaseUpdateRequest } from 'skyflow-common'; +import { BaseUpdateResponse } from 'skyflow-common'; +import { BaseUploadFilesRequest } from 'skyflow-common'; +import { BaseUploadFilesResponse } from 'skyflow-common'; +import { BaseVaultEntry } from 'skyflow-common'; +import { Credentials } from 'skyflow-common'; +import { LogLevel } from 'skyflow-common'; +import { SkyflowConfig } from 'skyflow-common'; +import { SkyflowError } from 'skyflow-common'; +import { SkyflowRecordError } from 'skyflow-common'; +import { VaultConfig } from 'skyflow-common'; + +// @public +export interface ColumnRedaction { + // (undocumented) + columnName: string; + // (undocumented) + redaction: string; +} + +// @public +export class DeleteFilesOptions extends BaseOptions { + constructor(); +} + +// @public +export interface DeleteFilesRecord { + // (undocumented) + columns: string[]; + // (undocumented) + skyflowId?: string; + // (undocumented) + tableName: string; + // (undocumented) + uniqueValues?: Record[]; +} + +// @public +export class DeleteFilesRequest extends BaseDeleteFilesRequest { + constructor(records: DeleteFilesRecord[]); + // (undocumented) + get records(): DeleteFilesRecord[]; + set records(value: DeleteFilesRecord[]); +} + +// @public +export class DeleteFilesResponse extends BaseDeleteFilesResponse { + constructor(input: { + records: DeleteFilesResponseRecord[]; + }); + // (undocumented) + readonly records: DeleteFilesResponseRecord[]; +} + +// @public (undocumented) +export interface DeleteFilesResponseColumn { + // (undocumented) + column: string; + // (undocumented) + status: string | null; +} + +// @public (undocumented) +export interface DeleteFilesResponseRecord { + // (undocumented) + columns: DeleteFilesResponseColumn[] | null; + // (undocumented) + error?: string | null; + // (undocumented) + httpCode: number; + // (undocumented) + requestId?: string | null; + // (undocumented) + skyflowId?: string | null; + // (undocumented) + tableName?: string | null; +} + +// @public +export class DeleteOptions extends BaseOptions { + constructor(); +} + +// @public +export class DeleteRequest extends BaseDeleteRequest { + constructor(table: string, ids: string[], uniqueValues?: Record[]); + // (undocumented) + get ids(): string[]; + set ids(value: string[]); + // (undocumented) + get table(): string; + set table(value: string); + // (undocumented) + get uniqueValues(): Record[] | undefined; + set uniqueValues(value: Record[] | undefined); +} + +// @public +export class DeleteResponse extends BaseDeleteResponse { + constructor(input: { + records: DeleteResponseRecord[]; + }); + // (undocumented) + readonly records: DeleteResponseRecord[]; +} + +// @public (undocumented) +export interface DeleteResponseRecord { + // (undocumented) + error?: string | null; + // (undocumented) + httpCode: number; + // (undocumented) + requestId?: string | null; + // (undocumented) + skyflowId?: string | null; +} + +// @public +export class DetokenizeOptions extends BaseOptions { + constructor(); +} + +// @public +export class DetokenizeRequest extends BaseDetokenizeRequest { + constructor(tokens: string[], tokenGroupRedactions?: TokenGroupRedaction[]); + // (undocumented) + get tokenGroupRedactions(): TokenGroupRedaction[] | undefined; + set tokenGroupRedactions(value: TokenGroupRedaction[] | undefined); + // (undocumented) + get tokens(): string[]; + set tokens(value: string[]); +} + +// @public +export class DetokenizeResponse extends BaseDetokenizeResponse { + constructor(input: { + records: DetokenizeResponseRecord[]; + }); + // (undocumented) + readonly records: DetokenizeResponseRecord[]; +} + +// @public (undocumented) +export interface DetokenizeResponseRecord { + // (undocumented) + error?: string | null; + // (undocumented) + httpCode: number; + // (undocumented) + metadata?: { + skyflowId?: string | null; + tableName?: string | null; + } | null; + // (undocumented) + requestId?: string | null; + // (undocumented) + token: string; + // (undocumented) + tokenGroupName?: string | null; + // (undocumented) + value?: unknown; +} + +// @public +export type FlowVaultRecordError = SkyflowRecordError; + +// @public +export class GetOptions extends BaseOptions { + constructor(); + // (undocumented) + getColumnRedactions(): ColumnRedaction[] | undefined; + // (undocumented) + getFields(): string[] | undefined; + // (undocumented) + getLimit(): number | undefined; + // (undocumented) + getOffset(): number | undefined; + // (undocumented) + getUniqueValues(): Record[] | undefined; + // (undocumented) + setColumnRedactions(columnRedactions: ColumnRedaction[]): void; + // (undocumented) + setFields(fields: string[]): void; + // (undocumented) + setLimit(limit: number): void; + // (undocumented) + setOffset(offset: number): void; + // (undocumented) + setUniqueValues(uniqueValues: Record[]): void; +} + +// @public +export interface GetRecord { + // (undocumented) + ids: string[]; + // (undocumented) + tableName: string; +} + +// @public +export class GetRequest extends BaseGetRequest { + constructor(table: string, ids: string[], records?: GetRecord[]); + // (undocumented) + get ids(): string[]; + set ids(value: string[]); + // (undocumented) + get records(): GetRecord[] | undefined; + set records(value: GetRecord[] | undefined); + // (undocumented) + get table(): string; + set table(value: string); +} + +// @public +export class GetResponse extends BaseGetResponse { + constructor(input: { + records: GetResponseRecord[]; + }); + // (undocumented) + readonly records: GetResponseRecord[]; +} + +// @public (undocumented) +export interface GetResponseRecord { + // (undocumented) + data?: Record | null; + // (undocumented) + error?: string | null; + // (undocumented) + hashedData?: Record | null; + // (undocumented) + httpCode: number; + // (undocumented) + requestId?: string | null; + // (undocumented) + skyflowId?: string | null; + // (undocumented) + tableName?: string | null; + // (undocumented) + tokens?: Record | null; +} + +// @public +export class GetTokensOptions extends BaseOptions { + constructor(); +} + +// @public +export interface GetTokensRecord { + // (undocumented) + tokenGroupName: string; + // (undocumented) + value: unknown; +} + +// @public +export class GetTokensRequest extends BaseGetTokensRequest { + constructor(records: GetTokensRecord[]); + // (undocumented) + get records(): GetTokensRecord[]; + set records(value: GetTokensRecord[]); +} + +// @public +export class GetTokensResponse extends BaseGetTokensResponse { + constructor(input: { + records: GetTokensResponseRecord[]; + }); + // (undocumented) + readonly records: GetTokensResponseRecord[]; +} + +// @public (undocumented) +export interface GetTokensResponseRecord { + // (undocumented) + error?: string | null; + // (undocumented) + httpCode: number; + // (undocumented) + requestId?: string | null; + // (undocumented) + token?: string | null; + // (undocumented) + tokenGroupName: string | null; + // (undocumented) + value: unknown; +} + +// @public +export class InsertOptions extends BaseOptions { + constructor(); + // (undocumented) + getUpsert(): UpsertOptions | undefined; + // (undocumented) + setUpsert(upsert: UpsertOptions): void; +} + +// @public +export interface InsertRecord { + // (undocumented) + data: Record; + // (undocumented) + tokens?: Record; +} + +// @public +export class InsertRequest extends BaseInsertRequest { + constructor(tableName: string, records: InsertRecord[]); + // (undocumented) + get records(): InsertRecord[]; + set records(value: InsertRecord[]); + // (undocumented) + get tableName(): string; + set tableName(value: string); +} + +// @public +export class InsertResponse extends BaseInsertResponse { + constructor(input: { + records: InsertResponseRecord[]; + }); + // (undocumented) + readonly records: InsertResponseRecord[]; +} + +// @public (undocumented) +export interface InsertResponseRecord { + // (undocumented) + error?: string | null; + // (undocumented) + fields?: Record | null; + // (undocumented) + hashedData?: Record | null; + // (undocumented) + httpCode: number; + // (undocumented) + requestId?: string | null; + // (undocumented) + skyflowId?: string | null; + // (undocumented) + tableName?: string | null; +} + +// @public +export class QueryOptions extends BaseOptions { + constructor(); +} + +// @public +export class QueryRequest extends BaseQueryRequest { + constructor(query: string); + // (undocumented) + get query(): string; + set query(value: string); +} + +// @public +export class QueryResponse extends BaseQueryResponse { + constructor(input: { + fields: Record[]; + errors: FlowVaultRecordError[] | null; + metadata: QueryResponseMetadata; + requestId: string; + }); + // (undocumented) + readonly errors: FlowVaultRecordError[] | null; + // (undocumented) + readonly fields: Record[]; + // (undocumented) + readonly metadata: QueryResponseMetadata; + // (undocumented) + readonly requestId: string; +} + +// @public +export interface QueryResponseMetadata { + // (undocumented) + columns: string[]; +} + +// Warning: (ae-forgotten-export) The symbol "ClientEntry" needs to be exported by the entry point index.d.ts +// +// @public +class Skyflow extends BaseSkyflow { + constructor(config: SkyflowConfig); + // (undocumented) + protected buildVaultEntry(config: VaultConfig): ClientEntry; + // Warning: (ae-forgotten-export) The symbol "VaultController" needs to be exported by the entry point index.d.ts + // + // (undocumented) + vault(vaultId?: string): VaultController; +} +export { Skyflow } +export default Skyflow; + +// @public +export interface TokenGroupRedaction { + // (undocumented) + redaction: string; + // (undocumented) + tokenGroupName: string; +} + +// @public +export class UpdateOptions extends BaseOptions { + constructor(); +} + +// @public +export interface UpdateRecord { + // (undocumented) + data: Record; + // (undocumented) + skyflowId: string; + // (undocumented) + tokens?: Record; +} + +// @public +export class UpdateRequest extends BaseUpdateRequest { + constructor(tableName: string, records: UpdateRecord[]); + // (undocumented) + get records(): UpdateRecord[]; + set records(value: UpdateRecord[]); + // (undocumented) + get tableName(): string; + set tableName(value: string); +} + +// @public +export class UpdateResponse extends BaseUpdateResponse { + constructor(input: { + records: UpdateResponseRecord[]; + }); + // (undocumented) + readonly records: UpdateResponseRecord[]; +} + +// @public (undocumented) +export interface UpdateResponseRecord { + // (undocumented) + error?: string | null; + // (undocumented) + fields?: Record | null; + // (undocumented) + hashedData?: Record | null; + // (undocumented) + httpCode: number; + // (undocumented) + requestId?: string | null; + // (undocumented) + skyflowId?: string | null; + // (undocumented) + tableName?: string | null; +} + +// @public +export interface UploadFilesColumn { + // (undocumented) + base64?: string; + // (undocumented) + column: string; + // (undocumented) + file?: File; + // (undocumented) + fileName?: string; + // (undocumented) + filePath?: string; +} + +// @public +export class UploadFilesOptions extends BaseOptions { + constructor(); +} + +// @public +export interface UploadFilesRecord { + // (undocumented) + columns: UploadFilesColumn[]; + // (undocumented) + skyflowId?: string; + // (undocumented) + tableName: string; +} + +// @public +export class UploadFilesRequest extends BaseUploadFilesRequest { + constructor(records: UploadFilesRecord[]); + // (undocumented) + get records(): UploadFilesRecord[]; + set records(value: UploadFilesRecord[]); +} + +// @public +export class UploadFilesResponse extends BaseUploadFilesResponse { + constructor(input: { + records: UploadFilesResponseRecord[]; + }); + // (undocumented) + readonly records: UploadFilesResponseRecord[]; +} + +// @public (undocumented) +export interface UploadFilesResponseColumn { + // (undocumented) + column: string; + // (undocumented) + error?: string | null; + // (undocumented) + fileName?: string | null; + // (undocumented) + uploadStatus: UploadStatus; +} + +// @public (undocumented) +export interface UploadFilesResponseRecord { + // (undocumented) + columns: UploadFilesResponseColumn[]; + // (undocumented) + error?: string | null; + // (undocumented) + httpCode: number; + // (undocumented) + requestId?: string | null; + // (undocumented) + skyflowId?: string | null; + // (undocumented) + tableName?: string | null; +} + +// @public +export type UploadStatus = 'UPLOADED' | 'FAILED' | 'SKIPPED'; + +// @public +export interface UpsertOptions { + // (undocumented) + uniqueColumns: string[]; + // (undocumented) + updateType: string; +} + + +export * from "skyflow-common"; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/flowvault/jest.config.js b/flowvault/jest.config.js new file mode 100644 index 00000000..b47c51fb --- /dev/null +++ b/flowvault/jest.config.js @@ -0,0 +1,19 @@ +module.exports = { + rootDir: '.', + testEnvironment: 'node', + testMatch: ['/test/**/*.test.ts'], + transform: { + '^.+\\.ts$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.test.json', + }, + ], + }, + moduleFileExtensions: ['ts', 'js', 'json'], + // Coverage reflects the hand-written layer only; the generated (maintainer-owned) + // client is excluded (mirrors skyvault's jest config). + collectCoverageFrom: ['src/**/*.ts'], + coveragePathIgnorePatterns: ['/node_modules/', '/_generated_/'], + verbose: true, +}; diff --git a/flowvault/package.json b/flowvault/package.json new file mode 100644 index 00000000..ac3d372a --- /dev/null +++ b/flowvault/package.json @@ -0,0 +1,61 @@ +{ + "name": "skyflow-flowvault-node", + "version": "1.0.0", + "description": "Skyflow FlowVault SDK for Node.js", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "tsc -b", + "build:dist": "tsup", + "test": "jest --coverage", + "contract-snapshot": "api-extractor run --local", + "contract-snapshot-verify": "api-extractor run" + }, + "repository": { + "type": "git", + "url": "https://github.com/skyflowapi/skyflow-node-sdk.git" + }, + "browser": { + "child_process": false + }, + "author": "", + "license": "MIT", + "keywords": [ + "skyflow", + "privacy", + "api", + "security", + "vault", + "flowvault", + "PII", + "tokenization" + ], + "dependencies": { + "dotenv": "16.6.1", + "form-data": "4.0.6", + "form-data-encoder": "4.1.0", + "formdata-node": "6.0.3", + "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" + }, + "devDependencies": { + "skyflow-common": "*" + } +} diff --git a/flowvault/src/client/index.ts b/flowvault/src/client/index.ts new file mode 100644 index 00000000..da4581f8 --- /dev/null +++ b/flowvault/src/client/index.ts @@ -0,0 +1,215 @@ +import { + AuthInfo, + AuthType, + Credentials, + LogLevel, + MessageType, + SkyflowError, + errorMessages, + getBearerToken, + isExpired, + printLog, + logs, + HTTP_HEADER, + HTTP_STATUS_CODE, +} from 'skyflow-common'; +import { Records } from '../../_generated_/rest/src/api/resources/records/client/Client'; +import { Tokens } from '../../_generated_/rest/src/api/resources/tokens/client/Client'; +import { Files } from '../../_generated_/rest/src/api/resources/files/client/Client'; +import { Query } from '../../_generated_/rest/src/api/resources/query/client/Client'; + +/** + * VaultClient owns the connection details for a single vault: its URL, + * credentials, log level and bearer token. It lazily builds the generated + * resource clients (Records / Tokens / Files / Query), each wired to a + * baseUrl + token supplier, and exposes them as typed accessors. The + * generated top-level `SkyflowClient` wrapper is intentionally not used (see + * notes) — the resource clients take the exact same options and are composed + * directly, mirroring the sibling skyvault SDK. + * + * It also normalizes the v2 error envelope into a common `SkyflowError`. + */ +class VaultClient { + vaultId: string; + + url: string; + + private currentToken = ''; + + private recordsApi?: Records; + + private tokensApi?: Tokens; + + private filesApi?: Files; + + private queryApi?: Query; + + individualCredentials?: Credentials; + + skyflowCredentials?: Credentials; + + logLevel: LogLevel; + + authInfo?: AuthInfo; + + private updateTriggered = false; + + constructor( + url: string, + vaultId: string, + individualCredentials?: Credentials, + skyflowCredentials?: Credentials, + logLevel?: LogLevel, + ) { + this.url = url; + this.vaultId = vaultId; + this.individualCredentials = individualCredentials; + this.skyflowCredentials = skyflowCredentials; + this.logLevel = logLevel || LogLevel.ERROR; + } + + updateClientConfig( + url: string, + vaultId: string, + individualCredentials?: Credentials, + skyflowCredentials?: Credentials, + logLevel?: LogLevel, + ): void { + this.updateTriggered = true; + this.url = url; + this.vaultId = vaultId; + this.individualCredentials = individualCredentials; + this.skyflowCredentials = skyflowCredentials; + this.logLevel = logLevel || LogLevel.ERROR; + // Force the resource clients to be rebuilt against the new URL. + this.recordsApi = undefined; + this.tokensApi = undefined; + this.filesApi = undefined; + this.queryApi = undefined; + } + + getLogLevel(): LogLevel { + return this.logLevel; + } + + setLogLevel(logLevel: LogLevel): void { + this.logLevel = logLevel; + } + + updateSkyflowCredentials(credentials?: Credentials): void { + this.updateTriggered = true; + this.skyflowCredentials = credentials; + } + + getCredentials(): Credentials | undefined { + if (this.authInfo?.key && !this.updateTriggered) { + switch (this.authInfo.type) { + case AuthType.API_KEY: + printLog(logs.infoLogs.REUSE_KEY, MessageType.LOG, this.logLevel); + return { apiKey: this.authInfo.key } as Credentials; + case AuthType.TOKEN: + if (!isExpired(this.authInfo.key)) { + printLog(logs.infoLogs.REUSE_BEARER_TOKEN, MessageType.LOG, this.logLevel); + return { token: this.authInfo.key } as Credentials; + } + break; + default: + break; + } + } + this.updateTriggered = false; + return this.individualCredentials ?? this.skyflowCredentials; + } + + /** Resolve a bearer token and make sure the generated clients are ready. */ + async getToken(): Promise { + const authInfo = await getBearerToken(this.getCredentials(), this.logLevel); + this.authInfo = authInfo; + this.currentToken = authInfo.key; + } + + private supplierOptions(): Records.Options { + return { + baseUrl: () => this.url, + token: () => this.currentToken, + }; + } + + get records(): Records { + if (!this.recordsApi) { + this.recordsApi = new Records(this.supplierOptions()); + } + return this.recordsApi; + } + + get tokens(): Tokens { + if (!this.tokensApi) { + this.tokensApi = new Tokens(this.supplierOptions()); + } + return this.tokensApi; + } + + get files(): Files { + if (!this.filesApi) { + this.filesApi = new Files(this.supplierOptions()); + } + return this.filesApi; + } + + get query(): Query { + if (!this.queryApi) { + this.queryApi = new Query(this.supplierOptions()); + } + return this.queryApi; + } + + /** + * Normalize a thrown error (generated SkyflowError envelope, timeout, or a + * plain Error) into a common `SkyflowError`. The v2 envelope is + * `{ error: { requestId?, grpcCode?/grpc_code?, httpCode?/http_code?, + * httpStatus?/http_status?, message, details? } }`. + */ + failureResponse(err: any): SkyflowError { + if (err instanceof SkyflowError) { + return err; + } + + const rawResponse = err?.rawResponse; + const headers = rawResponse?.headers; + const headerRequestId = + headers && typeof headers.get === 'function' + ? headers.get(HTTP_HEADER.X_REQUEST_ID) || '' + : ''; + + const body = err?.body; + const errorData = (body && typeof body === 'object' ? body.error : undefined) ?? {}; + + const message: string = + errorData.message ?? err?.message ?? errorMessages.ERROR_OCCURRED; + const httpCode = + err?.statusCode ?? + errorData.httpCode ?? + errorData.http_code ?? + HTTP_STATUS_CODE.BAD_REQUEST; + const grpcCode = errorData.grpcCode ?? errorData.grpc_code; + const httpStatus = errorData.httpStatus ?? errorData.http_status; + const details = errorData.details ?? []; + const requestId = errorData.requestId ?? headerRequestId; + + printLog(message, MessageType.ERROR, this.logLevel); + + return new SkyflowError( + { + httpCode, + message, + requestId, + grpcCode, + httpStatus, + details, + }, + [], + ); + } +} + +export default VaultClient; diff --git a/flowvault/src/controller/vault/index.ts b/flowvault/src/controller/vault/index.ts new file mode 100644 index 00000000..9b2cbbbc --- /dev/null +++ b/flowvault/src/controller/vault/index.ts @@ -0,0 +1,709 @@ +import * as fs from 'fs/promises'; +import * as path from 'path'; +import * as Gen from '../../../_generated_/rest/src/api'; +import { + SDK, + MessageType, + LogLevel, + printLog, + generateSDKMetrics, + removeSDKVersion, + SkyflowError, + logs, +} from 'skyflow-common'; +import VaultClient from '../../client'; +import InsertRequest from '../../model/request/insert'; +import InsertOptions from '../../model/options/insert'; +import InsertResponse from '../../model/response/insert'; +import GetRequest from '../../model/request/get'; +import GetOptions from '../../model/options/get'; +import GetResponse from '../../model/response/get'; +import UpdateRequest from '../../model/request/update'; +import UpdateOptions from '../../model/options/update'; +import UpdateResponse from '../../model/response/update'; +import DeleteRequest from '../../model/request/delete'; +import DeleteResponse from '../../model/response/delete'; +import QueryRequest from '../../model/request/query'; +import QueryResponse from '../../model/response/query'; +import DetokenizeRequest from '../../model/request/detokenize'; +import DetokenizeOptions from '../../model/options/detokenize'; +import DetokenizeResponse from '../../model/response/detokenize'; +import GetTokensRequest from '../../model/request/get-tokens'; +import GetTokensResponse from '../../model/response/get-tokens'; +import UploadFilesRequest from '../../model/request/upload-files'; +import UploadFilesResponse from '../../model/response/upload-files'; +import DeleteFilesRequest from '../../model/request/delete-files'; +import DeleteFilesResponse from '../../model/response/delete-files'; +import { + InsertResponseRecord, + GetResponseRecord, + UpdateResponseRecord, + DeleteResponseRecord, + DetokenizeResponseRecord, + GetTokensResponseRecord, + UploadFilesColumn, + UploadFilesResponseColumn, + UploadFilesResponseRecord, + DeleteFilesResponseRecord, +} from '../../types'; +import { + validateInsertRequest, + validateGetRequest, + validateUpdateRequest, + validateDeleteRequest, + validateQueryRequest, + validateDetokenizeRequest, + validateGetTokensRequest, + validateUploadFilesRequest, + validateDeleteFilesRequest, +} from '../../validations'; + +interface PreparedColumn { + column: string; + fileName?: string; + bytes: Uint8Array; + contentType: string; +} + +/** + * VaultController holds the 9 unary FlowVault operations. Each validates its + * input, ensures a bearer token, calls the generated client, and maps the + * generated response onto the hand-written Response shape. Whole-call + * failures are normalized to `SkyflowError` and thrown. + */ +class VaultController { + private client: VaultClient; + + constructor(client: VaultClient) { + this.client = client; + } + + private get logLevel(): LogLevel { + return this.client.getLogLevel(); + } + + private sdkHeaders(): Record { + return { [SDK.METRICS_HEADER_KEY]: JSON.stringify(generateSDKMetrics(this.logLevel)) }; + } + + // eslint-disable-next-line class-methods-use-this + private getRequestId(rawResponse: any): string | undefined { + const headers = rawResponse?.headers; + if (headers && typeof headers.get === 'function') { + return headers.get('x-request-id') || undefined; + } + return undefined; + } + + private handleError(error: unknown): SkyflowError { + if (error instanceof Error) { + printLog(removeSDKVersion(error.message), MessageType.ERROR, this.logLevel); + } + return this.client.failureResponse(error); + } + + // ---------------------------------------------------------------- insert + async insert(request: InsertRequest, options?: InsertOptions): Promise { + try { + printLog(logs.infoLogs.INSERT_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_INSERT_INPUT, MessageType.LOG, this.logLevel); + validateInsertRequest(request, options, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.InsertRequest = { + vaultID: this.client.vaultId, + tableName: request.tableName, + records: request.records.map((record) => { + const genRecord: Gen.InsertRecordData = { data: record.data }; + if (record.tokens) genRecord.tokens = record.tokens; // BYOT + return genRecord; + }), + }; + + const upsert = options?.getUpsert(); + if (upsert) { + genReq.upsert = { + updateType: upsert.updateType as Gen.UpsertUpdateType, + uniqueColumns: upsert.uniqueColumns, + }; + } + + const { data, rawResponse } = await this.client.records + .insertRecords(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.INSERT_DATA_SUCCESS, MessageType.LOG, this.logLevel); + + const records = (data.records ?? []).map((record) => + this.mapInsertRecord(record, requestId), + ); + return new InsertResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + // eslint-disable-next-line class-methods-use-this + private mapInsertRecord( + record: Gen.RecordResponseObject, + requestId?: string, + ): InsertResponseRecord { + const hasError = record.error !== undefined && record.error !== null; + const result: InsertResponseRecord = { + tableName: record.tableName ?? null, + skyflowId: record.skyflowID ?? null, + fields: (record.tokens ?? record.data) ?? null, + hashedData: record.hashedData ?? null, + httpCode: record.httpCode, + error: hasError ? record.error : null, + }; + if (hasError) { + result.requestId = requestId ?? null; + } + return result; + } + + // ------------------------------------------------------------------- get + async get(request: GetRequest, options?: GetOptions): Promise { + try { + printLog(logs.infoLogs.GET_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_GET_INPUT, MessageType.LOG, this.logLevel); + validateGetRequest(request, options, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.GetRequest = { vaultID: this.client.vaultId }; + if (request.records && request.records.length > 0) { + genReq.records = request.records.map((record) => ({ + tableName: record.tableName, + skyflowIDs: record.ids, + })); + } else { + genReq.tableName = request.table; + genReq.skyflowIDs = request.ids; + } + + const fields = options?.getFields(); + if (fields) genReq.columns = fields; + const redactions = options?.getColumnRedactions(); + if (redactions) { + genReq.columnRedactions = redactions.map((redaction) => ({ + columnName: redaction.columnName, + redaction: redaction.redaction, + })); + } + const uniqueValues = options?.getUniqueValues(); + if (uniqueValues) { + genReq.uniqueValues = uniqueValues.map((data) => ({ data })); + } + const limit = options?.getLimit(); + if (limit !== undefined) genReq.limit = limit; + const offset = options?.getOffset(); + if (offset !== undefined) genReq.offset = offset; + + const { data, rawResponse } = await this.client.records + .getRecords(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.GET_SUCCESS, MessageType.LOG, this.logLevel); + + const records = (data.records ?? []).map((record) => this.mapGetRecord(record, requestId)); + return new GetResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + // eslint-disable-next-line class-methods-use-this + private mapGetRecord(record: Gen.RecordResponseObject, requestId?: string): GetResponseRecord { + const hasError = record.error !== undefined && record.error !== null; + const result: GetResponseRecord = { + skyflowId: record.skyflowID ?? null, + tableName: record.tableName ?? null, + data: record.data ?? null, + hashedData: record.hashedData ?? null, + tokens: record.tokens ?? null, + httpCode: record.httpCode, + error: hasError ? record.error : null, + }; + if (hasError) { + result.requestId = requestId ?? null; + } + return result; + } + + // ---------------------------------------------------------------- update + async update(request: UpdateRequest, options?: UpdateOptions): Promise { + try { + printLog(logs.infoLogs.UPDATE_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_UPDATE_INPUT, MessageType.LOG, this.logLevel); + validateUpdateRequest(request, options, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.UpdateRequest = { + vaultID: this.client.vaultId, + tableName: request.tableName, + records: request.records.map((record) => { + const genRecord: Gen.UpdateRecordData = { skyflowID: record.skyflowId, data: record.data }; + if (record.tokens) genRecord.tokens = record.tokens; // BYOT + return genRecord; + }), + }; + + const { data, rawResponse } = await this.client.records + .updateRecords(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.UPDATE_SUCCESS, MessageType.LOG, this.logLevel); + + const records = (data.records ?? []).map((record) => + this.mapUpdateRecord(record, requestId), + ); + return new UpdateResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + // eslint-disable-next-line class-methods-use-this + private mapUpdateRecord( + record: Gen.RecordResponseObject, + requestId?: string, + ): UpdateResponseRecord { + const hasError = record.error !== undefined && record.error !== null; + const result: UpdateResponseRecord = { + skyflowId: record.skyflowID ?? null, + tableName: record.tableName ?? null, + fields: (record.tokens ?? record.data) ?? null, + hashedData: record.hashedData ?? null, + httpCode: record.httpCode, + error: hasError ? record.error : null, + }; + if (hasError) { + result.requestId = requestId ?? null; + } + return result; + } + + // ---------------------------------------------------------------- delete + async delete(request: DeleteRequest): Promise { + try { + printLog(logs.infoLogs.DELETE_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_DELETE_INPUT, MessageType.LOG, this.logLevel); + validateDeleteRequest(request, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.DeleteRequest = { + vaultID: this.client.vaultId, + tableName: request.table, + }; + if (request.ids && request.ids.length > 0) { + genReq.skyflowIDs = request.ids; + } + if (request.uniqueValues && request.uniqueValues.length > 0) { + // Each { col: value, ... } map becomes one wire { data: {...} } entry. + genReq.uniqueValues = request.uniqueValues.map((data) => ({ data })); + } + + const { data, rawResponse } = await this.client.records + .deleteRecords(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.DELETE_SUCCESS, MessageType.LOG, this.logLevel); + + const records = (data.records ?? []).map((record) => + this.mapDeleteRecord(record, requestId), + ); + return new DeleteResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + // eslint-disable-next-line class-methods-use-this + private mapDeleteRecord( + record: Gen.DeleteResponseObject, + requestId?: string, + ): DeleteResponseRecord { + const hasError = record.error !== undefined && record.error !== null; + const result: DeleteResponseRecord = { + skyflowId: record.skyflowID ?? null, + httpCode: record.httpCode, + error: hasError ? record.error : null, + }; + if (hasError) { + result.requestId = requestId ?? null; + } + return result; + } + + // ----------------------------------------------------------------- query + async query(request: QueryRequest): Promise { + try { + printLog(logs.infoLogs.QUERY_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_QUERY_INPUT, MessageType.LOG, this.logLevel); + validateQueryRequest(request, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.ExecuteQueryRequest = { + vaultID: this.client.vaultId, + query: request.query, + }; + + const { data, rawResponse } = await this.client.query + .executeQuery(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse) ?? ''; + printLog(logs.infoLogs.QUERY_SUCCESS, MessageType.LOG, this.logLevel); + + const fields = (data.records ?? []).map((record) => record.data ?? {}); + // NOTE: generated ExecuteQueryResponse has no per-record errors block. + return new QueryResponse({ + fields, + errors: null, + metadata: { columns: data.metadata?.columns ?? [] }, + requestId, + }); + } catch (error) { + throw this.handleError(error); + } + } + + // ------------------------------------------------------------ detokenize + async detokenize( + request: DetokenizeRequest, + options?: DetokenizeOptions, + ): Promise { + try { + printLog(logs.infoLogs.DETOKENIZE_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_DETOKENIZE_INPUT, MessageType.LOG, this.logLevel); + validateDetokenizeRequest(request, options, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.DetokenizeRequest = { + vaultID: this.client.vaultId, + tokens: request.tokens, + }; + const redactions = request.tokenGroupRedactions; + if (redactions) { + genReq.tokenGroupRedactions = redactions.map((redaction) => ({ + tokenGroupName: redaction.tokenGroupName, + redaction: redaction.redaction, + })); + } + + const { data, rawResponse } = await this.client.tokens + .detokenize(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.DETOKENIZE_SUCCESS, MessageType.LOG, this.logLevel); + + // NOTE: generated DetokenizeResponse exposes records under `response`. + const records = (data.response ?? []).map((record) => + this.mapDetokenizeRecord(record, requestId), + ); + return new DetokenizeResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + // eslint-disable-next-line class-methods-use-this + private mapDetokenizeRecord( + record: Gen.DetokenizeResponseObject, + requestId?: string, + ): DetokenizeResponseRecord { + const hasError = record.error !== undefined && record.error !== null; + const metadata = record.metadata as Record | undefined; + const result: DetokenizeResponseRecord = { + token: record.token, + tokenGroupName: record.tokenGroupName ?? null, + value: record.value, + metadata: metadata + ? { + skyflowId: (metadata.skyflowID ?? metadata.skyflowId ?? null) as string | null, + tableName: (metadata.tableName ?? null) as string | null, + } + : null, + httpCode: record.httpCode ?? 200, + error: hasError ? record.error : null, + }; + if (hasError) { + result.requestId = requestId ?? null; + } + return result; + } + + // ------------------------------------------------------------- getTokens + async getTokens(request: GetTokensRequest): Promise { + try { + printLog(logs.infoLogs.TOKENIZE_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_TOKENIZE_INPUT, MessageType.LOG, this.logLevel); + validateGetTokensRequest(request, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.GetTokensFromValuesRequest = { + vaultID: this.client.vaultId, + records: request.records.map((record) => ({ + value: record.value, + tokenGroupName: record.tokenGroupName, + })), + }; + + const { data, rawResponse } = await this.client.tokens + .getTokens(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.TOKENIZE_SUCCESS, MessageType.LOG, this.logLevel); + + const records = (data.records ?? []).map((record) => + this.mapGetTokensRecord(record, requestId), + ); + return new GetTokensResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + // eslint-disable-next-line class-methods-use-this + private mapGetTokensRecord( + record: Gen.TokenizeResponseObject, + requestId?: string, + ): GetTokensResponseRecord { + const hasError = record.error !== undefined && record.error !== null; + const result: GetTokensResponseRecord = { + value: record.value, + tokenGroupName: record.tokenGroupName, + token: record.token ?? null, + httpCode: record.httpCode ?? 200, + error: hasError ? record.error : null, + }; + if (hasError) { + result.requestId = requestId ?? null; + } + return result; + } + + // ----------------------------------------------------------- uploadFiles + async uploadFiles(request: UploadFilesRequest): Promise { + try { + printLog(logs.infoLogs.UPLOAD_FILE_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_FILE_UPLOAD_INPUT, MessageType.LOG, this.logLevel); + validateUploadFilesRequest(request, undefined, this.logLevel); + await this.client.getToken(); + + // Phase A: prepare file bytes and request signed upload URLs. + const prepared: PreparedColumn[][] = []; + for (let i = 0; i < request.records.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + prepared.push(await Promise.all(request.records[i].columns.map((c) => this.prepareColumn(c)))); + } + + const genReq: Gen.FileUploadRequest = { + vaultID: this.client.vaultId, + records: request.records.map((record, index) => { + const genRecord: Gen.FileUploadRecord = { + tableName: record.tableName, + columns: prepared[index].map((column) => { + const col: Gen.FileUploadColumn = { column: column.column }; + if (column.fileName) col.fileName = column.fileName; + return col; + }), + }; + if (record.skyflowId) genRecord.skyflowID = record.skyflowId; + return genRecord; + }), + }; + + const { data, rawResponse } = await this.client.files + .uploadFiles(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.FILE_UPLOAD_DATA_SUCCESS, MessageType.LOG, this.logLevel); + + // Phase B: PUT each file's bytes to its signed URL. + const records: UploadFilesResponseRecord[] = []; + const responseRecords = data.records ?? []; + for (let i = 0; i < responseRecords.length; i += 1) { + const responseRecord = responseRecords[i]; + const preparedColumns = prepared[i] ?? []; + // eslint-disable-next-line no-await-in-loop + const columns = await this.uploadColumns(responseRecord, preparedColumns); + const hasError = responseRecord.error !== undefined && responseRecord.error !== null; + const record: UploadFilesResponseRecord = { + skyflowId: responseRecord.skyflowID ?? null, + tableName: responseRecord.tableName ?? null, + columns, + httpCode: responseRecord.httpCode, + error: hasError ? responseRecord.error : null, + }; + if (hasError) { + record.requestId = requestId ?? null; + } + records.push(record); + } + + return new UploadFilesResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + private async uploadColumns( + responseRecord: Gen.FileUploadResponseObject, + preparedColumns: PreparedColumn[], + ): Promise { + const signedUrls = (responseRecord.data ?? {}) as Record; + const recordErrored = + responseRecord.error !== undefined && responseRecord.error !== null; + + const results = await Promise.all( + preparedColumns.map(async (column) => { + const result: UploadFilesResponseColumn = { + column: column.column, + fileName: column.fileName ?? null, + uploadStatus: 'SKIPPED', + }; + if (recordErrored) { + result.error = responseRecord.error; + return result; + } + const signedUrl = signedUrls[column.column]; + if (typeof signedUrl !== 'string' || signedUrl.length === 0) { + // No signed URL returned for this column -> SKIPPED, no error + // (matches the Python flowvault SDK). + return result; + } + try { + const putResponse = await fetch(signedUrl, { + method: 'PUT', + headers: { 'Content-Type': column.contentType }, + body: column.bytes, + }); + if (putResponse.ok) { + result.uploadStatus = 'UPLOADED'; + } else { + result.uploadStatus = 'FAILED'; + result.error = `Upload failed with status ${putResponse.status}.`; + } + } catch (putError) { + result.uploadStatus = 'FAILED'; + result.error = putError instanceof Error ? putError.message : String(putError); + } + return result; + }), + ); + return results; + } + + private async prepareColumn(column: UploadFilesColumn): Promise { + let bytes: Uint8Array = new Uint8Array(); + let fileName = column.fileName; + if (column.filePath) { + const buffer = await fs.readFile(column.filePath); + bytes = new Uint8Array(buffer); + if (!fileName) fileName = path.basename(column.filePath); + } else if (column.base64 !== undefined) { + bytes = new Uint8Array(Buffer.from(column.base64, 'base64')); + } else if (column.file) { + const arrayBuffer = await column.file.arrayBuffer(); + bytes = new Uint8Array(arrayBuffer); + if (!fileName) fileName = column.file.name; + } + return { + column: column.column, + fileName, + bytes, + contentType: this.inferContentType(fileName), + }; + } + + // eslint-disable-next-line class-methods-use-this + private inferContentType(fileName?: string): string { + const ext = (fileName?.split('.').pop() || '').toLowerCase(); + const map: Record = { + pdf: 'application/pdf', + png: 'image/png', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + gif: 'image/gif', + txt: 'text/plain', + csv: 'text/csv', + json: 'application/json', + doc: 'application/msword', + docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + xls: 'application/vnd.ms-excel', + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }; + return map[ext] || 'application/octet-stream'; + } + + // ----------------------------------------------------------- deleteFiles + async deleteFiles(request: DeleteFilesRequest): Promise { + try { + printLog(logs.infoLogs.UPLOAD_FILE_TRIGGERED, MessageType.LOG, this.logLevel); + printLog(logs.infoLogs.VALIDATE_FILE_UPLOAD_INPUT, MessageType.LOG, this.logLevel); + validateDeleteFilesRequest(request, this.logLevel); + await this.client.getToken(); + + const genReq: Gen.FileDeleteRequest = { + vaultID: this.client.vaultId, + records: request.records.map((record) => { + const genRecord: Gen.FileDeleteRecord = { + tableName: record.tableName, + columns: record.columns, + }; + if (record.skyflowId) genRecord.skyflowID = record.skyflowId; + if (record.uniqueValues) { + genRecord.uniqueValues = record.uniqueValues.map((data) => ({ data })); + } + return genRecord; + }), + }; + + const { data, rawResponse } = await this.client.files + .deleteFiles(genReq, { headers: this.sdkHeaders() }) + .withRawResponse(); + const requestId = this.getRequestId(rawResponse); + printLog(logs.infoLogs.FILE_UPLOAD_DATA_SUCCESS, MessageType.LOG, this.logLevel); + + const records = (data.records ?? []).map((record) => + this.mapDeleteFilesRecord(record, requestId), + ); + return new DeleteFilesResponse({ records }); + } catch (error) { + throw this.handleError(error); + } + } + + // eslint-disable-next-line class-methods-use-this + private mapDeleteFilesRecord( + record: Gen.FileDeleteResponseObject, + requestId?: string, + ): DeleteFilesResponseRecord { + const hasError = record.error !== undefined && record.error !== null; + const columnMap = record.data as Record | undefined; + const result: DeleteFilesResponseRecord = { + skyflowId: record.skyflowID ?? null, + tableName: record.tableName ?? null, + // Status is parsed from each column's response value (object -> .status, + // else the value itself), mirroring the Python flowvault SDK. + columns: columnMap + ? Object.entries(columnMap).map(([column, value]) => ({ + column, + status: + value !== null && typeof value === 'object' + ? (((value as Record).status as string) ?? null) + : ((value as string) ?? null), + })) + : null, + httpCode: record.httpCode, + error: hasError ? record.error : null, + }; + if (hasError) { + result.requestId = requestId ?? null; + } + return result; + } +} + +export default VaultController; diff --git a/flowvault/src/index.ts b/flowvault/src/index.ts new file mode 100644 index 00000000..8c0ee810 --- /dev/null +++ b/flowvault/src/index.ts @@ -0,0 +1,67 @@ +// Re-export the shared public surface from skyflow-common so consumers can +// import everything (credentials, config, errors, enums, service-account +// helpers, validators, base classes, etc.) from skyflow-flowvault-node. +export * from 'skyflow-common'; + +// FlowVault facade. +export { default as Skyflow, default } from './skyflow'; + +// Request classes. +export { default as InsertRequest } from './model/request/insert'; +export { default as GetRequest } from './model/request/get'; +export { default as UpdateRequest } from './model/request/update'; +export { default as DeleteRequest } from './model/request/delete'; +export { default as QueryRequest } from './model/request/query'; +export { default as DetokenizeRequest } from './model/request/detokenize'; +export { default as GetTokensRequest } from './model/request/get-tokens'; +export { default as UploadFilesRequest } from './model/request/upload-files'; +export { default as DeleteFilesRequest } from './model/request/delete-files'; + +// Options classes. +export { default as InsertOptions } from './model/options/insert'; +export { default as GetOptions } from './model/options/get'; +export { default as UpdateOptions } from './model/options/update'; +export { default as DeleteOptions } from './model/options/delete'; +export { default as QueryOptions } from './model/options/query'; +export { default as DetokenizeOptions } from './model/options/detokenize'; +export { default as GetTokensOptions } from './model/options/get-tokens'; +export { default as UploadFilesOptions } from './model/options/upload-files'; +export { default as DeleteFilesOptions } from './model/options/delete-files'; + +// Response classes. +export { default as InsertResponse } from './model/response/insert'; +export { default as GetResponse } from './model/response/get'; +export { default as UpdateResponse } from './model/response/update'; +export { default as DeleteResponse } from './model/response/delete'; +export { default as QueryResponse } from './model/response/query'; +export { default as DetokenizeResponse } from './model/response/detokenize'; +export { default as GetTokensResponse } from './model/response/get-tokens'; +export { default as UploadFilesResponse } from './model/response/upload-files'; +export { default as DeleteFilesResponse } from './model/response/delete-files'; + +// Record / helper types. +export type { GetRecord } from './model/request/get'; +export type { + InsertRecord, + UpsertOptions, + ColumnRedaction, + UpdateRecord, + TokenGroupRedaction, + GetTokensRecord, + UploadFilesColumn, + UploadFilesRecord, + DeleteFilesRecord, + UploadStatus, + InsertResponseRecord, + GetResponseRecord, + UpdateResponseRecord, + DeleteResponseRecord, + DetokenizeResponseRecord, + GetTokensResponseRecord, + UploadFilesResponseColumn, + UploadFilesResponseRecord, + DeleteFilesResponseColumn, + DeleteFilesResponseRecord, + QueryResponseMetadata, + FlowVaultRecordError, +} from './types'; diff --git a/flowvault/src/model/options/delete-files/index.ts b/flowvault/src/model/options/delete-files/index.ts new file mode 100644 index 00000000..86ba71df --- /dev/null +++ b/flowvault/src/model/options/delete-files/index.ts @@ -0,0 +1,10 @@ +import { BaseOptions } from 'skyflow-common'; + +/** Options for a deleteFiles operation. */ +class DeleteFilesOptions extends BaseOptions { + constructor() { + super(); + } +} + +export default DeleteFilesOptions; diff --git a/flowvault/src/model/options/delete/index.ts b/flowvault/src/model/options/delete/index.ts new file mode 100644 index 00000000..033ebf8f --- /dev/null +++ b/flowvault/src/model/options/delete/index.ts @@ -0,0 +1,10 @@ +import { BaseOptions } from 'skyflow-common'; + +/** Options for a delete operation. */ +class DeleteOptions extends BaseOptions { + constructor() { + super(); + } +} + +export default DeleteOptions; diff --git a/flowvault/src/model/options/detokenize/index.ts b/flowvault/src/model/options/detokenize/index.ts new file mode 100644 index 00000000..f6472d9f --- /dev/null +++ b/flowvault/src/model/options/detokenize/index.ts @@ -0,0 +1,10 @@ +import { BaseOptions } from 'skyflow-common'; + +/** Options for a detokenize operation. */ +class DetokenizeOptions extends BaseOptions { + constructor() { + super(); + } +} + +export default DetokenizeOptions; diff --git a/flowvault/src/model/options/get-tokens/index.ts b/flowvault/src/model/options/get-tokens/index.ts new file mode 100644 index 00000000..71ce4d1e --- /dev/null +++ b/flowvault/src/model/options/get-tokens/index.ts @@ -0,0 +1,10 @@ +import { BaseOptions } from 'skyflow-common'; + +/** Options for a getTokens operation. */ +class GetTokensOptions extends BaseOptions { + constructor() { + super(); + } +} + +export default GetTokensOptions; diff --git a/flowvault/src/model/options/get/index.ts b/flowvault/src/model/options/get/index.ts new file mode 100644 index 00000000..034fdc7b --- /dev/null +++ b/flowvault/src/model/options/get/index.ts @@ -0,0 +1,57 @@ +import { BaseOptions } from 'skyflow-common'; +import { ColumnRedaction } from '../../../types'; + +/** Options for a get operation. */ +class GetOptions extends BaseOptions { + private fields?: string[]; + private columnRedactions?: ColumnRedaction[]; + private uniqueValues?: Record[]; + private limit?: number; + private offset?: number; + + constructor() { + super(); + } + + setFields(fields: string[]): void { + this.fields = fields; + } + + getFields(): string[] | undefined { + return this.fields; + } + + setColumnRedactions(columnRedactions: ColumnRedaction[]): void { + this.columnRedactions = columnRedactions; + } + + getColumnRedactions(): ColumnRedaction[] | undefined { + return this.columnRedactions; + } + + setUniqueValues(uniqueValues: Record[]): void { + this.uniqueValues = uniqueValues; + } + + getUniqueValues(): Record[] | undefined { + return this.uniqueValues; + } + + setLimit(limit: number): void { + this.limit = limit; + } + + getLimit(): number | undefined { + return this.limit; + } + + setOffset(offset: number): void { + this.offset = offset; + } + + getOffset(): number | undefined { + return this.offset; + } +} + +export default GetOptions; diff --git a/flowvault/src/model/options/insert/index.ts b/flowvault/src/model/options/insert/index.ts new file mode 100644 index 00000000..f59619d8 --- /dev/null +++ b/flowvault/src/model/options/insert/index.ts @@ -0,0 +1,21 @@ +import { BaseOptions } from 'skyflow-common'; +import { UpsertOptions } from '../../../types'; + +/** Options for an insert operation. */ +class InsertOptions extends BaseOptions { + private upsert?: UpsertOptions; + + constructor() { + super(); + } + + setUpsert(upsert: UpsertOptions): void { + this.upsert = upsert; + } + + getUpsert(): UpsertOptions | undefined { + return this.upsert; + } +} + +export default InsertOptions; diff --git a/flowvault/src/model/options/query/index.ts b/flowvault/src/model/options/query/index.ts new file mode 100644 index 00000000..54b4cf03 --- /dev/null +++ b/flowvault/src/model/options/query/index.ts @@ -0,0 +1,10 @@ +import { BaseOptions } from 'skyflow-common'; + +/** Options for a query operation. */ +class QueryOptions extends BaseOptions { + constructor() { + super(); + } +} + +export default QueryOptions; diff --git a/flowvault/src/model/options/update/index.ts b/flowvault/src/model/options/update/index.ts new file mode 100644 index 00000000..7a959845 --- /dev/null +++ b/flowvault/src/model/options/update/index.ts @@ -0,0 +1,10 @@ +import { BaseOptions } from 'skyflow-common'; + +/** Options for an update operation. */ +class UpdateOptions extends BaseOptions { + constructor() { + super(); + } +} + +export default UpdateOptions; diff --git a/flowvault/src/model/options/upload-files/index.ts b/flowvault/src/model/options/upload-files/index.ts new file mode 100644 index 00000000..1d2190cb --- /dev/null +++ b/flowvault/src/model/options/upload-files/index.ts @@ -0,0 +1,10 @@ +import { BaseOptions } from 'skyflow-common'; + +/** Options for an uploadFiles operation. */ +class UploadFilesOptions extends BaseOptions { + constructor() { + super(); + } +} + +export default UploadFilesOptions; diff --git a/flowvault/src/model/request/delete-files/index.ts b/flowvault/src/model/request/delete-files/index.ts new file mode 100644 index 00000000..55aad34e --- /dev/null +++ b/flowvault/src/model/request/delete-files/index.ts @@ -0,0 +1,22 @@ +import { BaseDeleteFilesRequest } from 'skyflow-common'; +import { DeleteFilesRecord } from '../../../types'; + +/** Request to delete files from one or more file columns. */ +class DeleteFilesRequest extends BaseDeleteFilesRequest { + private _records: DeleteFilesRecord[]; + + constructor(records: DeleteFilesRecord[]) { + super(); + this._records = records; + } + + public get records(): DeleteFilesRecord[] { + return this._records; + } + + public set records(value: DeleteFilesRecord[]) { + this._records = value; + } +} + +export default DeleteFilesRequest; diff --git a/flowvault/src/model/request/delete/index.ts b/flowvault/src/model/request/delete/index.ts new file mode 100644 index 00000000..0748dcba --- /dev/null +++ b/flowvault/src/model/request/delete/index.ts @@ -0,0 +1,43 @@ +import { BaseDeleteRequest } from 'skyflow-common'; + +/** Request to delete records by Skyflow ID or by unique column values. */ +class DeleteRequest extends BaseDeleteRequest { + private _table: string; + private _ids: string[]; + // A list of { columnName: value, ... } maps (mirrors Java/Python flowvault); + // each is sent on the wire as { data: {...} }. + private _uniqueValues?: Record[]; + + constructor(table: string, ids: string[], uniqueValues?: Record[]) { + super(); + this._table = table; + this._ids = ids; + this._uniqueValues = uniqueValues; + } + + public get table(): string { + return this._table; + } + + public set table(value: string) { + this._table = value; + } + + public get ids(): string[] { + return this._ids; + } + + public set ids(value: string[]) { + this._ids = value; + } + + public get uniqueValues(): Record[] | undefined { + return this._uniqueValues; + } + + public set uniqueValues(value: Record[] | undefined) { + this._uniqueValues = value; + } +} + +export default DeleteRequest; diff --git a/flowvault/src/model/request/detokenize/index.ts b/flowvault/src/model/request/detokenize/index.ts new file mode 100644 index 00000000..347479e3 --- /dev/null +++ b/flowvault/src/model/request/detokenize/index.ts @@ -0,0 +1,32 @@ +import { BaseDetokenizeRequest } from 'skyflow-common'; +import { TokenGroupRedaction } from '../../../types'; + +/** Request to detokenize a list of tokens. */ +class DetokenizeRequest extends BaseDetokenizeRequest { + private _tokens: string[]; + private _tokenGroupRedactions?: TokenGroupRedaction[]; + + constructor(tokens: string[], tokenGroupRedactions?: TokenGroupRedaction[]) { + super(); + this._tokens = tokens; + this._tokenGroupRedactions = tokenGroupRedactions; + } + + public get tokens(): string[] { + return this._tokens; + } + + public set tokens(value: string[]) { + this._tokens = value; + } + + public get tokenGroupRedactions(): TokenGroupRedaction[] | undefined { + return this._tokenGroupRedactions; + } + + public set tokenGroupRedactions(value: TokenGroupRedaction[] | undefined) { + this._tokenGroupRedactions = value; + } +} + +export default DetokenizeRequest; diff --git a/flowvault/src/model/request/get-tokens/index.ts b/flowvault/src/model/request/get-tokens/index.ts new file mode 100644 index 00000000..a603057d --- /dev/null +++ b/flowvault/src/model/request/get-tokens/index.ts @@ -0,0 +1,22 @@ +import { BaseGetTokensRequest } from 'skyflow-common'; +import { GetTokensRecord } from '../../../types'; + +/** Request to fetch deterministic tokens for a list of values. */ +class GetTokensRequest extends BaseGetTokensRequest { + private _records: GetTokensRecord[]; + + constructor(records: GetTokensRecord[]) { + super(); + this._records = records; + } + + public get records(): GetTokensRecord[] { + return this._records; + } + + public set records(value: GetTokensRecord[]) { + this._records = value; + } +} + +export default GetTokensRequest; diff --git a/flowvault/src/model/request/get/index.ts b/flowvault/src/model/request/get/index.ts new file mode 100644 index 00000000..7446453f --- /dev/null +++ b/flowvault/src/model/request/get/index.ts @@ -0,0 +1,47 @@ +import { BaseGetRequest } from 'skyflow-common'; + +/** A single table/ids entry for a multi-table get request. */ +export interface GetRecord { + tableName: string; + ids: string[]; +} + +/** Request to fetch records by Skyflow ID, optionally across tables. */ +class GetRequest extends BaseGetRequest { + private _table: string; + private _ids: string[]; + private _records?: GetRecord[]; + + constructor(table: string, ids: string[], records?: GetRecord[]) { + super(); + this._table = table; + this._ids = ids; + this._records = records; + } + + public get table(): string { + return this._table; + } + + public set table(value: string) { + this._table = value; + } + + public get ids(): string[] { + return this._ids; + } + + public set ids(value: string[]) { + this._ids = value; + } + + public get records(): GetRecord[] | undefined { + return this._records; + } + + public set records(value: GetRecord[] | undefined) { + this._records = value; + } +} + +export default GetRequest; diff --git a/flowvault/src/model/request/insert/index.ts b/flowvault/src/model/request/insert/index.ts new file mode 100644 index 00000000..217c2bd3 --- /dev/null +++ b/flowvault/src/model/request/insert/index.ts @@ -0,0 +1,32 @@ +import { BaseInsertRequest } from 'skyflow-common'; +import { InsertRecord } from '../../../types'; + +/** Request to insert one or more records into a table. */ +class InsertRequest extends BaseInsertRequest { + private _tableName: string; + private _records: InsertRecord[]; + + constructor(tableName: string, records: InsertRecord[]) { + super(); + this._tableName = tableName; + this._records = records; + } + + public get tableName(): string { + return this._tableName; + } + + public set tableName(value: string) { + this._tableName = value; + } + + public get records(): InsertRecord[] { + return this._records; + } + + public set records(value: InsertRecord[]) { + this._records = value; + } +} + +export default InsertRequest; diff --git a/flowvault/src/model/request/query/index.ts b/flowvault/src/model/request/query/index.ts new file mode 100644 index 00000000..366ce659 --- /dev/null +++ b/flowvault/src/model/request/query/index.ts @@ -0,0 +1,21 @@ +import { BaseQueryRequest } from 'skyflow-common'; + +/** Request to run a SQL-like query against a vault. */ +class QueryRequest extends BaseQueryRequest { + private _query: string; + + constructor(query: string) { + super(); + this._query = query; + } + + public get query(): string { + return this._query; + } + + public set query(value: string) { + this._query = value; + } +} + +export default QueryRequest; diff --git a/flowvault/src/model/request/update/index.ts b/flowvault/src/model/request/update/index.ts new file mode 100644 index 00000000..c6eb4110 --- /dev/null +++ b/flowvault/src/model/request/update/index.ts @@ -0,0 +1,32 @@ +import { BaseUpdateRequest } from 'skyflow-common'; +import { UpdateRecord } from '../../../types'; + +/** Request to update one or more records in a table. */ +class UpdateRequest extends BaseUpdateRequest { + private _tableName: string; + private _records: UpdateRecord[]; + + constructor(tableName: string, records: UpdateRecord[]) { + super(); + this._tableName = tableName; + this._records = records; + } + + public get tableName(): string { + return this._tableName; + } + + public set tableName(value: string) { + this._tableName = value; + } + + public get records(): UpdateRecord[] { + return this._records; + } + + public set records(value: UpdateRecord[]) { + this._records = value; + } +} + +export default UpdateRequest; diff --git a/flowvault/src/model/request/upload-files/index.ts b/flowvault/src/model/request/upload-files/index.ts new file mode 100644 index 00000000..1c7ef273 --- /dev/null +++ b/flowvault/src/model/request/upload-files/index.ts @@ -0,0 +1,22 @@ +import { BaseUploadFilesRequest } from 'skyflow-common'; +import { UploadFilesRecord } from '../../../types'; + +/** Request to upload files into one or more file columns. */ +class UploadFilesRequest extends BaseUploadFilesRequest { + private _records: UploadFilesRecord[]; + + constructor(records: UploadFilesRecord[]) { + super(); + this._records = records; + } + + public get records(): UploadFilesRecord[] { + return this._records; + } + + public set records(value: UploadFilesRecord[]) { + this._records = value; + } +} + +export default UploadFilesRequest; diff --git a/flowvault/src/model/response/delete-files/index.ts b/flowvault/src/model/response/delete-files/index.ts new file mode 100644 index 00000000..58606ab5 --- /dev/null +++ b/flowvault/src/model/response/delete-files/index.ts @@ -0,0 +1,14 @@ +import { BaseDeleteFilesResponse } from 'skyflow-common'; +import { DeleteFilesResponseRecord } from '../../../types'; + +/** Response from a deleteFiles operation. */ +class DeleteFilesResponse extends BaseDeleteFilesResponse { + readonly records: DeleteFilesResponseRecord[]; + + constructor({ records }: { records: DeleteFilesResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default DeleteFilesResponse; diff --git a/flowvault/src/model/response/delete/index.ts b/flowvault/src/model/response/delete/index.ts new file mode 100644 index 00000000..38e8c90d --- /dev/null +++ b/flowvault/src/model/response/delete/index.ts @@ -0,0 +1,14 @@ +import { BaseDeleteResponse } from 'skyflow-common'; +import { DeleteResponseRecord } from '../../../types'; + +/** Response from a delete operation. */ +class DeleteResponse extends BaseDeleteResponse { + readonly records: DeleteResponseRecord[]; + + constructor({ records }: { records: DeleteResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default DeleteResponse; diff --git a/flowvault/src/model/response/detokenize/index.ts b/flowvault/src/model/response/detokenize/index.ts new file mode 100644 index 00000000..1ba9cb6c --- /dev/null +++ b/flowvault/src/model/response/detokenize/index.ts @@ -0,0 +1,14 @@ +import { BaseDetokenizeResponse } from 'skyflow-common'; +import { DetokenizeResponseRecord } from '../../../types'; + +/** Response from a detokenize operation. */ +class DetokenizeResponse extends BaseDetokenizeResponse { + readonly records: DetokenizeResponseRecord[]; + + constructor({ records }: { records: DetokenizeResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default DetokenizeResponse; diff --git a/flowvault/src/model/response/get-tokens/index.ts b/flowvault/src/model/response/get-tokens/index.ts new file mode 100644 index 00000000..d77b7f94 --- /dev/null +++ b/flowvault/src/model/response/get-tokens/index.ts @@ -0,0 +1,14 @@ +import { BaseGetTokensResponse } from 'skyflow-common'; +import { GetTokensResponseRecord } from '../../../types'; + +/** Response from a getTokens operation. */ +class GetTokensResponse extends BaseGetTokensResponse { + readonly records: GetTokensResponseRecord[]; + + constructor({ records }: { records: GetTokensResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default GetTokensResponse; diff --git a/flowvault/src/model/response/get/index.ts b/flowvault/src/model/response/get/index.ts new file mode 100644 index 00000000..de17c27d --- /dev/null +++ b/flowvault/src/model/response/get/index.ts @@ -0,0 +1,14 @@ +import { BaseGetResponse } from 'skyflow-common'; +import { GetResponseRecord } from '../../../types'; + +/** Response from a get operation. */ +class GetResponse extends BaseGetResponse { + readonly records: GetResponseRecord[]; + + constructor({ records }: { records: GetResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default GetResponse; diff --git a/flowvault/src/model/response/insert/index.ts b/flowvault/src/model/response/insert/index.ts new file mode 100644 index 00000000..ba146ff7 --- /dev/null +++ b/flowvault/src/model/response/insert/index.ts @@ -0,0 +1,14 @@ +import { BaseInsertResponse } from 'skyflow-common'; +import { InsertResponseRecord } from '../../../types'; + +/** Response from an insert operation. */ +class InsertResponse extends BaseInsertResponse { + readonly records: InsertResponseRecord[]; + + constructor({ records }: { records: InsertResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default InsertResponse; diff --git a/flowvault/src/model/response/query/index.ts b/flowvault/src/model/response/query/index.ts new file mode 100644 index 00000000..503efbf5 --- /dev/null +++ b/flowvault/src/model/response/query/index.ts @@ -0,0 +1,30 @@ +import { BaseQueryResponse } from 'skyflow-common'; +import { FlowVaultRecordError, QueryResponseMetadata } from '../../../types'; + +/** Response from a query operation. */ +class QueryResponse extends BaseQueryResponse { + readonly fields: Record[]; + readonly errors: FlowVaultRecordError[] | null; + readonly metadata: QueryResponseMetadata; + readonly requestId: string; + + constructor({ + fields, + errors, + metadata, + requestId, + }: { + fields: Record[]; + errors: FlowVaultRecordError[] | null; + metadata: QueryResponseMetadata; + requestId: string; + }) { + super(); + this.fields = fields; + this.errors = errors; + this.metadata = metadata; + this.requestId = requestId; + } +} + +export default QueryResponse; diff --git a/flowvault/src/model/response/update/index.ts b/flowvault/src/model/response/update/index.ts new file mode 100644 index 00000000..9a2867e6 --- /dev/null +++ b/flowvault/src/model/response/update/index.ts @@ -0,0 +1,14 @@ +import { BaseUpdateResponse } from 'skyflow-common'; +import { UpdateResponseRecord } from '../../../types'; + +/** Response from an update operation. */ +class UpdateResponse extends BaseUpdateResponse { + readonly records: UpdateResponseRecord[]; + + constructor({ records }: { records: UpdateResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default UpdateResponse; diff --git a/flowvault/src/model/response/upload-files/index.ts b/flowvault/src/model/response/upload-files/index.ts new file mode 100644 index 00000000..ddb9e089 --- /dev/null +++ b/flowvault/src/model/response/upload-files/index.ts @@ -0,0 +1,14 @@ +import { BaseUploadFilesResponse } from 'skyflow-common'; +import { UploadFilesResponseRecord } from '../../../types'; + +/** Response from an uploadFiles operation. */ +class UploadFilesResponse extends BaseUploadFilesResponse { + readonly records: UploadFilesResponseRecord[]; + + constructor({ records }: { records: UploadFilesResponseRecord[] }) { + super(); + this.records = records; + } +} + +export default UploadFilesResponse; diff --git a/flowvault/src/skyflow/index.ts b/flowvault/src/skyflow/index.ts new file mode 100644 index 00000000..e4a19f6a --- /dev/null +++ b/flowvault/src/skyflow/index.ts @@ -0,0 +1,56 @@ +import { + BaseSkyflow, + BaseVaultEntry, + Env, + MessageType, + SkyflowConfig, + VaultConfig, + getVaultURL, + logs, + printLog, + setSdkIdentity, +} from 'skyflow-common'; +import VaultClient from '../client'; +import VaultController from '../controller/vault'; +import { SDK_NAME, SDK_VERSION } from '../version'; + +// Stamp the SDK identity so telemetry and errors self-report FlowVault. +setSdkIdentity({ name: SDK_NAME, version: SDK_VERSION }); + +interface ClientEntry extends BaseVaultEntry { + vaultController: VaultController; +} + +/** + * Skyflow is the FlowVault SDK facade. Vault-config / credential / log-level + * management comes from the shared `BaseSkyflow` in `common`; this layer only + * builds its own VaultClient + VaultController and exposes `vault(vaultId?)`. + * (FlowVault has no connections or detect.) + */ +class Skyflow extends BaseSkyflow { + constructor(config: SkyflowConfig) { + super(); + this.initializeSkyflow(config); + printLog(logs.infoLogs.CLIENT_INITIALIZED, MessageType.LOG, this.logLevel); + } + + protected buildVaultEntry(config: VaultConfig): ClientEntry { + const env = config.env || Env.PROD; + const vaultUrl = getVaultURL(config.clusterId, env); + const client = new VaultClient( + vaultUrl, + config.vaultId, + config?.credentials, + this.commonCredentials, + this.logLevel, + ); + const vaultController = new VaultController(client); + return { config, client, vaultController }; + } + + vault(vaultId?: string): VaultController { + return this.resolveVaultEntry(vaultId).vaultController; + } +} + +export default Skyflow; diff --git a/flowvault/src/types/index.ts b/flowvault/src/types/index.ts new file mode 100644 index 00000000..66dda433 --- /dev/null +++ b/flowvault/src/types/index.ts @@ -0,0 +1,175 @@ +// Shared FlowVault types: record shapes for requests and the unified +// response records. Response envelopes themselves are the Response classes +// under `model/response`. + +import { SkyflowRecordError } from 'skyflow-common'; + +/** A single record to insert. `tokens` enables BYOT (bring your own token). */ +export interface InsertRecord { + data: Record; + tokens?: Record; +} + +/** Upsert configuration for insert operations. */ +export interface UpsertOptions { + updateType: string; + uniqueColumns: string[]; +} + +/** Column level redaction used by get. */ +export interface ColumnRedaction { + columnName: string; + redaction: string; +} + +/** A single record to update. */ +export interface UpdateRecord { + skyflowId: string; + data: Record; + tokens?: Record; +} + +/** Token group redaction used by detokenize. */ +export interface TokenGroupRedaction { + tokenGroupName: string; + redaction: string; +} + +/** A value / token-group pair to tokenize via getTokens. */ +export interface GetTokensRecord { + value: unknown; + tokenGroupName: string; +} + +/** A single file column to upload. Provide exactly one of filePath/base64/file. */ +export interface UploadFilesColumn { + column: string; + filePath?: string; + base64?: string; + file?: File; + fileName?: string; +} + +/** A record (row) with one or more file columns to upload. */ +export interface UploadFilesRecord { + tableName: string; + skyflowId?: string; + columns: UploadFilesColumn[]; +} + +/** A record (row) with one or more file columns to delete. */ +export interface DeleteFilesRecord { + tableName: string; + columns: string[]; + skyflowId?: string; + uniqueValues?: Record[]; +} + +/** Result of uploading a file to a single column. */ +export type UploadStatus = 'UPLOADED' | 'FAILED' | 'SKIPPED'; + +// --------------------------------------------------------------------------- +// Unified response records. Every record carries httpCode and an optional +// error. requestId is populated only when error is present. +// --------------------------------------------------------------------------- + +export interface InsertResponseRecord { + tableName?: string | null; + skyflowId?: string | null; + fields?: Record | null; + hashedData?: Record | null; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +export interface GetResponseRecord { + skyflowId?: string | null; + tableName?: string | null; + data?: Record | null; + hashedData?: Record | null; + tokens?: Record | null; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +export interface UpdateResponseRecord { + skyflowId?: string | null; + tableName?: string | null; + fields?: Record | null; + hashedData?: Record | null; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +// NOTE: the delete response record intentionally has NO tableName — the v2 +// delete API (and the Java/Python flowvault SDKs) return only skyflowId. +export interface DeleteResponseRecord { + skyflowId?: string | null; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +export interface DetokenizeResponseRecord { + token: string; + tokenGroupName?: string | null; + value?: unknown; + metadata?: { skyflowId?: string | null; tableName?: string | null } | null; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +export interface GetTokensResponseRecord { + value: unknown; + tokenGroupName: string | null; + token?: string | null; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +export interface UploadFilesResponseColumn { + column: string; + fileName?: string | null; + uploadStatus: UploadStatus; + error?: string | null; +} + +export interface UploadFilesResponseRecord { + skyflowId?: string | null; + tableName?: string | null; + columns: UploadFilesResponseColumn[]; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +export interface DeleteFilesResponseColumn { + column: string; + // Status is parsed from the API response value (e.g. "DELETED"), not assumed. + status: string | null; +} + +export interface DeleteFilesResponseRecord { + skyflowId?: string | null; + tableName?: string | null; + columns: DeleteFilesResponseColumn[] | null; + httpCode: number; + error?: string | null; + requestId?: string | null; +} + +/** Metadata block returned by query. */ +export interface QueryResponseMetadata { + columns: string[]; +} + +/** Record-level error shape re-used by query. */ +export type FlowVaultRecordError = SkyflowRecordError; + +/** Internal: registry entry for a configured vault. */ +export type { SkyflowConfig } from 'skyflow-common'; diff --git a/flowvault/src/validations/index.ts b/flowvault/src/validations/index.ts new file mode 100644 index 00000000..491f68cb --- /dev/null +++ b/flowvault/src/validations/index.ts @@ -0,0 +1,209 @@ +import { + SkyflowError, + SKYFLOW_ERROR_CODE, + LogLevel, + MessageType, + printLog, +} from 'skyflow-common'; +import InsertRequest from '../model/request/insert'; +import GetRequest from '../model/request/get'; +import UpdateRequest from '../model/request/update'; +import DeleteRequest from '../model/request/delete'; +import QueryRequest from '../model/request/query'; +import DetokenizeRequest from '../model/request/detokenize'; +import GetTokensRequest from '../model/request/get-tokens'; +import UploadFilesRequest from '../model/request/upload-files'; +import DeleteFilesRequest from '../model/request/delete-files'; + +const isNonEmptyString = (value: unknown): boolean => + typeof value === 'string' && value.trim().length > 0; + +const isNonEmptyArray = (value: unknown): boolean => + Array.isArray(value) && value.length > 0; + +const fail = (code: { httpCode: number; message: string }, logLevel?: LogLevel): never => { + printLog(code.message, MessageType.ERROR, logLevel); + throw new SkyflowError(code); +}; + +export const validateInsertRequest = ( + request: InsertRequest, + _options?: unknown, + logLevel?: LogLevel, +): void => { + if (!request || !(request instanceof InsertRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_INSERT_REQUEST, logLevel); + } + if (!isNonEmptyString(request.tableName)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME, logLevel); + } + if (!isNonEmptyArray(request.records)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_RECORD_IN_INSERT, logLevel); + } + request.records.forEach((record) => { + if (!record || typeof record.data !== 'object' || record.data === null) { + fail(SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_INSERT, logLevel); + } + }); +}; + +export const validateGetRequest = ( + request: GetRequest, + _options?: unknown, + logLevel?: LogLevel, +): void => { + if (!request || !(request instanceof GetRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_GET_REQUEST, logLevel); + } + const hasMultiTable = isNonEmptyArray(request.records); + if (!hasMultiTable) { + if (!isNonEmptyString(request.table)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME, logLevel); + } + if (!isNonEmptyArray(request.ids)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_IDS_IN_GET, logLevel); + } + } +}; + +export const validateUpdateRequest = ( + request: UpdateRequest, + _options?: unknown, + logLevel?: LogLevel, +): void => { + if (!request || !(request instanceof UpdateRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_UPDATE_REQUEST, logLevel); + } + if (!isNonEmptyString(request.tableName)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME, logLevel); + } + if (!isNonEmptyArray(request.records)) { + fail(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE, logLevel); + } + request.records.forEach((record) => { + if (!isNonEmptyString(record?.skyflowId)) { + fail(SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPDATE, logLevel); + } + if (!record || typeof record.data !== 'object' || record.data === null) { + fail(SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_UPDATE, logLevel); + } + }); +}; + +export const validateDeleteRequest = (request: DeleteRequest, logLevel?: LogLevel): void => { + if (!request || !(request instanceof DeleteRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_DELETE_REQUEST, logLevel); + } + if (!isNonEmptyString(request.table)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME, logLevel); + } + const hasUniqueValues = isNonEmptyArray(request.uniqueValues); + if (!hasUniqueValues && !isNonEmptyArray(request.ids)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_DELETE_IDS, logLevel); + } +}; + +export const validateQueryRequest = (request: QueryRequest, logLevel?: LogLevel): void => { + if (!request || !(request instanceof QueryRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_QUERY_REQUEST, logLevel); + } + if (!isNonEmptyString(request.query)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_QUERY, logLevel); + } +}; + +export const validateDetokenizeRequest = ( + request: DetokenizeRequest, + _options?: unknown, + logLevel?: LogLevel, +): void => { + if (!request || !(request instanceof DetokenizeRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_DETOKENIZE_REQUEST, logLevel); + } + if (!isNonEmptyArray(request.tokens)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_TOKENS_IN_DETOKENIZE, logLevel); + } + request.tokens.forEach((token) => { + if (!isNonEmptyString(token)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_TOKEN_IN_DETOKENIZE, logLevel); + } + }); +}; + +export const validateGetTokensRequest = ( + request: GetTokensRequest, + logLevel?: LogLevel, +): void => { + if (!request || !(request instanceof GetTokensRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_TOKENIZE_REQUEST, logLevel); + } + if (!isNonEmptyArray(request.records)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_VALUES_IN_TOKENIZE, logLevel); + } + request.records.forEach((record) => { + if (!isNonEmptyString(record?.tokenGroupName)) { + fail(SKYFLOW_ERROR_CODE.EMPTY_COLUMN_GROUP_IN_TOKENIZE, logLevel); + } + }); +}; + +export const validateUploadFilesRequest = ( + request: UploadFilesRequest, + _options?: unknown, + logLevel?: LogLevel, +): void => { + if (!request || !(request instanceof UploadFilesRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST, logLevel); + } + if (!isNonEmptyArray(request.records)) { + fail(SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST, logLevel); + } + request.records.forEach((record) => { + if (!isNonEmptyString(record?.tableName)) { + fail(SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE, logLevel); + } + if (!isNonEmptyArray(record?.columns)) { + fail(SKYFLOW_ERROR_CODE.MISSING_COLUMN_NAME_IN_UPLOAD_FILE, logLevel); + } + record.columns.forEach((column) => { + if (!isNonEmptyString(column?.column)) { + fail(SKYFLOW_ERROR_CODE.MISSING_COLUMN_NAME_IN_UPLOAD_FILE, logLevel); + } + const sources = [column.filePath, column.base64, column.file].filter( + (source) => source !== undefined && source !== null, + ); + if (sources.length !== 1) { + fail(SKYFLOW_ERROR_CODE.MISSING_FILE_SOURCE_IN_UPLOAD_FILE, logLevel); + } + if (column.base64 !== undefined && !isNonEmptyString(column.fileName)) { + fail(SKYFLOW_ERROR_CODE.MISSING_FILE_NAME_FOR_BASE64, logLevel); + } + }); + }); +}; + +export const validateDeleteFilesRequest = ( + request: DeleteFilesRequest, + logLevel?: LogLevel, +): void => { + if (!request || !(request instanceof DeleteFilesRequest)) { + fail(SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST, logLevel); + } + if (!isNonEmptyArray(request.records)) { + fail(SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST, logLevel); + } + request.records.forEach((record) => { + if (!isNonEmptyString(record?.tableName)) { + fail(SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE, logLevel); + } + if (!isNonEmptyArray(record?.columns)) { + fail(SKYFLOW_ERROR_CODE.MISSING_COLUMN_NAME_IN_UPLOAD_FILE, logLevel); + } + const hasSkyflowId = isNonEmptyString(record?.skyflowId); + const hasUniqueValues = isNonEmptyArray(record?.uniqueValues); + if (hasSkyflowId === hasUniqueValues) { + // exactly one of skyflowId / uniqueValues is required + fail(SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPLOAD_FILE, logLevel); + } + }); +}; diff --git a/flowvault/src/version.ts b/flowvault/src/version.ts new file mode 100644 index 00000000..48308455 --- /dev/null +++ b/flowvault/src/version.ts @@ -0,0 +1,4 @@ +// SDK version constant for skyflow-flowvault-node. +// Kept in sync with flowvault/package.json "version" by scripts/bump_version.sh. +export const SDK_NAME = "skyflow-flowvault-node"; +export const SDK_VERSION = "1.0.0"; diff --git a/flowvault/test/client/client.test.ts b/flowvault/test/client/client.test.ts new file mode 100644 index 00000000..fe174bc7 --- /dev/null +++ b/flowvault/test/client/client.test.ts @@ -0,0 +1,173 @@ +// Mock only getBearerToken from skyflow-common; everything else stays real so +// failureResponse / getCredentials / isExpired behave as in production. +jest.mock('skyflow-common', () => { + const actual = jest.requireActual('skyflow-common'); + return { ...actual, getBearerToken: jest.fn() }; +}); + +import { + AuthType, + LogLevel, + SkyflowError, + SKYFLOW_ERROR_CODE, + errorMessages, + getBearerToken, +} from '../../src'; +import VaultClient from '../../src/client'; +import { makeJwt, silenceConsole } from '../helpers'; + +silenceConsole(); + +const getBearerTokenMock = getBearerToken as unknown as jest.Mock; + +const URL = 'https://abc-cluster.vault.skyflowapis.com'; + +function newClient(individual?: unknown, common?: unknown): VaultClient { + return new VaultClient(URL, 'vault123', individual as never, common as never, LogLevel.ERROR); +} + +describe('VaultClient.failureResponse', () => { + it('normalizes a snake_case v2 error envelope; httpCode from statusCode, requestId from header', () => { + const client = newClient({ apiKey: 'sky-x' }); + const err = { + statusCode: 404, + body: { error: { grpc_code: 5, http_code: 404, http_status: 'Not Found', message: 'not found', details: ['d1'] } }, + rawResponse: { headers: new Map([['x-request-id', 'req-xyz']]) }, + }; + + const result = client.failureResponse(err); + + expect(result).toBeInstanceOf(SkyflowError); + expect(result.error?.httpCode).toBe(404); // from statusCode (takes priority) + expect(result.error?.grpcCode).toBe(5); + expect(result.error?.httpStatus).toBe('Not Found'); + expect(result.error?.message).toBe('not found'); + expect(result.error?.details).toEqual(['d1']); + expect(result.error?.requestId).toBe('req-xyz'); // from header (no body requestId) + }); + + it('normalizes a camelCase variant; requestId comes from the body when present', () => { + const client = newClient({ apiKey: 'sky-x' }); + const err = { + statusCode: 400, + body: { error: { grpcCode: 3, httpCode: 400, httpStatus: 'Bad Request', message: 'bad', details: [], requestId: 'rid-1' } }, + }; + + const result = client.failureResponse(err); + + expect(result.error?.httpCode).toBe(400); + expect(result.error?.grpcCode).toBe(3); + expect(result.error?.httpStatus).toBe('Bad Request'); + expect(result.error?.message).toBe('bad'); + expect(result.error?.requestId).toBe('rid-1'); + }); + + it('falls back to err.message and defaults when there is no body', () => { + const client = newClient({ apiKey: 'sky-x' }); + const result = client.failureResponse({ message: 'boom' }); + + expect(result.error?.message).toBe('boom'); + expect(result.error?.httpCode).toBe(400); // HTTP_STATUS_CODE.BAD_REQUEST default + expect(result.error?.grpcCode).toBeNull(); + expect(result.error?.httpStatus).toBe('Bad Request'); + expect(result.error?.requestId).toBeNull(); + expect(result.error?.details).toEqual([]); + }); + + it('uses the generic ERROR_OCCURRED message when nothing else is present', () => { + const client = newClient({ apiKey: 'sky-x' }); + const result = client.failureResponse({}); + expect(result.error?.message).toBe(errorMessages.ERROR_OCCURRED); + }); + + it('passes an existing SkyflowError straight through', () => { + const client = newClient({ apiKey: 'sky-x' }); + const original = new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_QUERY); + expect(client.failureResponse(original)).toBe(original); + }); +}); + +describe('VaultClient.getToken', () => { + beforeEach(() => getBearerTokenMock.mockReset()); + + it('resolves a token via getBearerToken and wires the resource clients to token/baseUrl suppliers', async () => { + getBearerTokenMock.mockResolvedValue({ key: 'tok', type: AuthType.TOKEN }); + const client = newClient({ apiKey: 'sky-x' }); + + await client.getToken(); + + expect(getBearerTokenMock).toHaveBeenCalledTimes(1); + // Each generated resource client gets baseUrl + token suppliers. + for (const api of [client.records, client.tokens, client.files, client.query]) { + const opts = (api as unknown as { _options: { baseUrl: () => string; token: () => string } })._options; + expect(opts.baseUrl()).toBe(URL); + expect(opts.token()).toBe('tok'); + } + }); +}); + +describe('VaultClient.getCredentials', () => { + it('reuses an API key from authInfo without re-reading credentials', () => { + const client = newClient({ apiKey: 'sky-individual' }); + (client as unknown as { authInfo: unknown }).authInfo = { key: 'sky-cached', type: AuthType.API_KEY }; + expect(client.getCredentials()).toEqual({ apiKey: 'sky-cached' }); + }); + + it('reuses a non-expired bearer token from authInfo', () => { + const client = newClient({ apiKey: 'sky-individual' }); + const validJwt = makeJwt(3600); + (client as unknown as { authInfo: unknown }).authInfo = { key: validJwt, type: AuthType.TOKEN }; + expect(client.getCredentials()).toEqual({ token: validJwt }); + }); + + it('falls back to individual credentials when the cached bearer token is expired', () => { + const client = newClient({ apiKey: 'sky-individual' }); + const expiredJwt = makeJwt(-3600); + (client as unknown as { authInfo: unknown }).authInfo = { key: expiredJwt, type: AuthType.TOKEN }; + expect(client.getCredentials()).toEqual({ apiKey: 'sky-individual' }); + }); + + it('returns individual over common when there is no cached authInfo', () => { + const client = newClient({ apiKey: 'sky-individual' }, { apiKey: 'sky-common' }); + expect(client.getCredentials()).toEqual({ apiKey: 'sky-individual' }); + }); + + it('returns common credentials when no individual credentials are set', () => { + const client = newClient(undefined, { apiKey: 'sky-common' }); + expect(client.getCredentials()).toEqual({ apiKey: 'sky-common' }); + }); + + it('skips reuse right after updateSkyflowCredentials (updateTriggered), then resumes', () => { + const client = newClient({ apiKey: 'sky-individual' }); + (client as unknown as { authInfo: unknown }).authInfo = { key: 'sky-cached', type: AuthType.API_KEY }; + client.updateSkyflowCredentials({ apiKey: 'sky-common' } as never); + // updateTriggered -> ignore cached authInfo, return individual ?? common. + expect(client.getCredentials()).toEqual({ apiKey: 'sky-individual' }); + // Subsequent call reuses the cached key again (updateTriggered reset). + expect(client.getCredentials()).toEqual({ apiKey: 'sky-cached' }); + }); +}); + +describe('VaultClient config updates', () => { + it('updateClientConfig rebuilds resource clients against the new URL', async () => { + getBearerTokenMock.mockResolvedValue({ key: 'tok', type: AuthType.TOKEN }); + const client = newClient({ apiKey: 'sky-x' }); + await client.getToken(); + const before = client.records; + + const newUrl = 'https://new-cluster.vault.skyflowapis.com'; + client.updateClientConfig(newUrl, 'vault123', { apiKey: 'sky-y' } as never, undefined, LogLevel.DEBUG); + + const after = client.records; + expect(after).not.toBe(before); // rebuilt + expect(client.getLogLevel()).toBe(LogLevel.DEBUG); + const opts = (after as unknown as { _options: { baseUrl: () => string } })._options; + expect(opts.baseUrl()).toBe(newUrl); + }); + + it('setLogLevel updates the stored level', () => { + const client = newClient({ apiKey: 'sky-x' }); + client.setLogLevel(LogLevel.WARN); + expect(client.getLogLevel()).toBe(LogLevel.WARN); + }); +}); diff --git a/flowvault/test/controller/delete-files.test.ts b/flowvault/test/controller/delete-files.test.ts new file mode 100644 index 00000000..d8f7d4ec --- /dev/null +++ b/flowvault/test/controller/delete-files.test.ts @@ -0,0 +1,93 @@ +import { DeleteFilesRequest, DeleteFilesResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; + +silenceConsole(); + +describe('VaultController.deleteFiles', () => { + it('builds skyflowID + columns and parses status (object->.status, else value)', async () => { + const deleteFiles = resourceMethod({ + records: [ + { + skyflowID: 'id1', + tableName: 'onboarding', + data: { resume: { status: 'DELETED' }, photo: 'DELETED' }, + httpCode: 200, + }, + ], + }); + const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); + + const req = new DeleteFilesRequest([ + { tableName: 'onboarding', columns: ['resume', 'photo'], skyflowId: 'id1' }, + ]); + const resp = await controller.deleteFiles(req); + + expect(resp).toBeInstanceOf(DeleteFilesResponse); + expect(deleteFiles).toHaveBeenCalledTimes(1); + const [genReq] = deleteFiles.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.records[0]).toEqual({ tableName: 'onboarding', columns: ['resume', 'photo'], skyflowID: 'id1' }); + + const record = resp.records[0]; + expect(record.skyflowId).toBe('id1'); + expect(record.tableName).toBe('onboarding'); + expect(record.httpCode).toBe(200); + expect(record.error).toBeNull(); + expect(record.requestId).toBeUndefined(); + expect(record.columns).toEqual([ + { column: 'resume', status: 'DELETED' }, // object -> .status + { column: 'photo', status: 'DELETED' }, // scalar -> value + ]); + }); + + it('maps uniqueValues->{data} and omits skyflowID when absent', async () => { + const deleteFiles = resourceMethod({ records: [] }); + const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); + + await controller.deleteFiles( + new DeleteFilesRequest([ + { tableName: 't', columns: ['c'], uniqueValues: [{ email: 'a@b.com' }, { ssn: '111' }] }, + ]), + ); + + const [genReq] = deleteFiles.mock.calls[0]; + expect(genReq.records[0].skyflowID).toBeUndefined(); + expect(genReq.records[0].uniqueValues).toEqual([{ data: { email: 'a@b.com' } }, { data: { ssn: '111' } }]); + }); + + it('columns is null and requestId is set from header on a failed record (no data)', async () => { + const deleteFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', httpCode: 404, error: 'not found' }], + }); + const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); + + const resp = await controller.deleteFiles( + new DeleteFilesRequest([{ tableName: 't', columns: ['c'], skyflowId: 'id1' }]), + ); + + const record = resp.records[0]; + expect(record.columns).toBeNull(); + expect(record.error).toBe('not found'); + expect(record.requestId).toBe(REQUEST_ID); + }); + + it('parses a null column value as a null status', async () => { + const deleteFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { resume: null }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); + const resp = await controller.deleteFiles( + new DeleteFilesRequest([{ tableName: 't', columns: ['resume'], skyflowId: 'id1' }]), + ); + expect(resp.records[0].columns).toEqual([{ column: 'resume', status: null }]); + }); + + it('rejects when the generated method throws', async () => { + const deleteFiles = rejectingMethod(new Error('boom-delete-files')); + const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); + await expect( + controller.deleteFiles(new DeleteFilesRequest([{ tableName: 't', columns: ['c'], skyflowId: 'id1' }])), + ).rejects.toThrow('boom-delete-files'); + }); +}); diff --git a/flowvault/test/controller/delete.test.ts b/flowvault/test/controller/delete.test.ts new file mode 100644 index 00000000..ada7308a --- /dev/null +++ b/flowvault/test/controller/delete.test.ts @@ -0,0 +1,56 @@ +import { DeleteRequest, DeleteResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; + +silenceConsole(); + +describe('VaultController.delete', () => { + it('builds skyflowIDs and maps the response (no tableName on record)', async () => { + const deleteRecords = resourceMethod({ + records: [{ skyflowID: 'id1', httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ records: { deleteRecords } })); + + const resp = await controller.delete(new DeleteRequest('cards', ['id1', 'id2'])); + + expect(resp).toBeInstanceOf(DeleteResponse); + expect(deleteRecords).toHaveBeenCalledTimes(1); + const [genReq] = deleteRecords.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.tableName).toBe('cards'); + expect(genReq.skyflowIDs).toEqual(['id1', 'id2']); + expect(genReq.uniqueValues).toBeUndefined(); + + const record = resp.records[0]; + expect(record.skyflowId).toBe('id1'); + expect(record.httpCode).toBe(200); + expect(record.error).toBeNull(); + expect(record.requestId).toBeUndefined(); + expect((record as Record).tableName).toBeUndefined(); + }); + + it('builds uniqueValues->{data} when ids are absent', async () => { + const deleteRecords = resourceMethod({ records: [] }); + const controller = new VaultController(makeFakeClient({ records: { deleteRecords } })); + + await controller.delete(new DeleteRequest('cards', [], [{ email: 'a@b.com' }, { ssn: '111' }])); + + const [genReq] = deleteRecords.mock.calls[0]; + expect(genReq.skyflowIDs).toBeUndefined(); + expect(genReq.uniqueValues).toEqual([{ data: { email: 'a@b.com' } }, { data: { ssn: '111' } }]); + }); + + it('per-record error gets requestId from the header', async () => { + const deleteRecords = resourceMethod({ records: [{ skyflowID: 'id1', httpCode: 404, error: 'missing' }] }); + const controller = new VaultController(makeFakeClient({ records: { deleteRecords } })); + const resp = await controller.delete(new DeleteRequest('cards', ['id1'])); + expect(resp.records[0].error).toBe('missing'); + expect(resp.records[0].requestId).toBe(REQUEST_ID); + }); + + it('rejects when the generated method throws', async () => { + const deleteRecords = rejectingMethod(new Error('boom-delete')); + const controller = new VaultController(makeFakeClient({ records: { deleteRecords } })); + await expect(controller.delete(new DeleteRequest('cards', ['id1']))).rejects.toThrow('boom-delete'); + }); +}); diff --git a/flowvault/test/controller/detokenize.test.ts b/flowvault/test/controller/detokenize.test.ts new file mode 100644 index 00000000..00644de3 --- /dev/null +++ b/flowvault/test/controller/detokenize.test.ts @@ -0,0 +1,83 @@ +import { DetokenizeRequest, DetokenizeResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; + +silenceConsole(); + +describe('VaultController.detokenize', () => { + it('reads the generated `response` array and maps records (metadata skyflowID->skyflowId)', async () => { + const detokenize = resourceMethod({ + response: [ + { + token: 'tok1', + value: '4111', + tokenGroupName: 'cards', + httpCode: 200, + metadata: { skyflowID: 'id1', tableName: 'cards' }, + }, + ], + }); + const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); + + const req = new DetokenizeRequest(['tok1'], [{ tokenGroupName: 'cards', redaction: 'PLAIN_TEXT' }]); + const resp = await controller.detokenize(req); + + expect(resp).toBeInstanceOf(DetokenizeResponse); + expect(detokenize).toHaveBeenCalledTimes(1); + const [genReq] = detokenize.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.tokens).toEqual(['tok1']); + expect(genReq.tokenGroupRedactions).toEqual([{ tokenGroupName: 'cards', redaction: 'PLAIN_TEXT' }]); + + const record = resp.records[0]; + expect(record.token).toBe('tok1'); + expect(record.value).toBe('4111'); + expect(record.tokenGroupName).toBe('cards'); + expect(record.metadata).toEqual({ skyflowId: 'id1', tableName: 'cards' }); + expect(record.httpCode).toBe(200); + expect(record.error).toBeNull(); + expect(record.requestId).toBeUndefined(); + }); + + it('defaults httpCode to 200, metadata to null, and omits redactions when absent', async () => { + const detokenize = resourceMethod({ + response: [{ token: 'tok1', value: '4111' }], + }); + const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); + + const resp = await controller.detokenize(new DetokenizeRequest(['tok1'])); + + const [genReq] = detokenize.mock.calls[0]; + expect(genReq.tokenGroupRedactions).toBeUndefined(); + + const record = resp.records[0]; + expect(record.httpCode).toBe(200); + expect(record.metadata).toBeNull(); + expect(record.tokenGroupName).toBeNull(); + }); + + it('accepts camelCase metadata.skyflowId as well', async () => { + const detokenize = resourceMethod({ + response: [{ token: 'tok1', value: 'v', metadata: { skyflowId: 'id9' } }], + }); + const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); + const resp = await controller.detokenize(new DetokenizeRequest(['tok1'])); + expect(resp.records[0].metadata).toEqual({ skyflowId: 'id9', tableName: null }); + }); + + it('per-record error gets requestId from the header', async () => { + const detokenize = resourceMethod({ + response: [{ token: 'tok1', httpCode: 404, error: 'token not found' }], + }); + const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); + const resp = await controller.detokenize(new DetokenizeRequest(['tok1'])); + expect(resp.records[0].error).toBe('token not found'); + expect(resp.records[0].requestId).toBe(REQUEST_ID); + }); + + it('rejects when the generated method throws', async () => { + const detokenize = rejectingMethod(new Error('boom-detok')); + const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); + await expect(controller.detokenize(new DetokenizeRequest(['tok1']))).rejects.toThrow('boom-detok'); + }); +}); diff --git a/flowvault/test/controller/get-tokens.test.ts b/flowvault/test/controller/get-tokens.test.ts new file mode 100644 index 00000000..4ac9874c --- /dev/null +++ b/flowvault/test/controller/get-tokens.test.ts @@ -0,0 +1,58 @@ +import { GetTokensRequest, GetTokensResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; + +silenceConsole(); + +describe('VaultController.getTokens', () => { + it('builds value/tokenGroupName records and maps the response', async () => { + const getTokens = resourceMethod({ + records: [{ value: '4111', tokenGroupName: 'cards', token: 'tok_1', httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ tokens: { getTokens } })); + + const resp = await controller.getTokens( + new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }]), + ); + + expect(resp).toBeInstanceOf(GetTokensResponse); + expect(getTokens).toHaveBeenCalledTimes(1); + const [genReq] = getTokens.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.records).toEqual([{ value: '4111', tokenGroupName: 'cards' }]); + + const record = resp.records[0]; + expect(record.value).toBe('4111'); + expect(record.tokenGroupName).toBe('cards'); + expect(record.token).toBe('tok_1'); + expect(record.httpCode).toBe(200); + expect(record.error).toBeNull(); + expect(record.requestId).toBeUndefined(); + }); + + it('defaults token to null and httpCode to 200 when absent', async () => { + const getTokens = resourceMethod({ records: [{ value: '4111', tokenGroupName: 'cards' }] }); + const controller = new VaultController(makeFakeClient({ tokens: { getTokens } })); + const resp = await controller.getTokens(new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }])); + expect(resp.records[0].token).toBeNull(); + expect(resp.records[0].httpCode).toBe(200); + }); + + it('per-record error gets requestId from the header', async () => { + const getTokens = resourceMethod({ + records: [{ value: '4111', tokenGroupName: 'cards', httpCode: 400, error: 'tokenize failed' }], + }); + const controller = new VaultController(makeFakeClient({ tokens: { getTokens } })); + const resp = await controller.getTokens(new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }])); + expect(resp.records[0].error).toBe('tokenize failed'); + expect(resp.records[0].requestId).toBe(REQUEST_ID); + }); + + it('rejects when the generated method throws', async () => { + const getTokens = rejectingMethod(new Error('boom-tokens')); + const controller = new VaultController(makeFakeClient({ tokens: { getTokens } })); + await expect( + controller.getTokens(new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }])), + ).rejects.toThrow('boom-tokens'); + }); +}); diff --git a/flowvault/test/controller/get.test.ts b/flowvault/test/controller/get.test.ts new file mode 100644 index 00000000..be564598 --- /dev/null +++ b/flowvault/test/controller/get.test.ts @@ -0,0 +1,110 @@ +import { GetRequest, GetOptions, GetResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; + +silenceConsole(); + +describe('VaultController.get', () => { + it('single-table: builds tableName/skyflowIDs and maps records', async () => { + const getRecords = resourceMethod({ + records: [ + { + skyflowID: 'id1', + tableName: 'cards', + data: { name: 'alice' }, + hashedData: { name: 'h' }, + tokens: { name: 'tok' }, + httpCode: 200, + }, + ], + }); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + + const resp = await controller.get(new GetRequest('cards', ['id1', 'id2'])); + + expect(resp).toBeInstanceOf(GetResponse); + expect(getRecords).toHaveBeenCalledTimes(1); + const [genReq] = getRecords.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.tableName).toBe('cards'); + expect(genReq.skyflowIDs).toEqual(['id1', 'id2']); + expect(genReq.records).toBeUndefined(); + + const record = resp.records[0]; + expect(record.skyflowId).toBe('id1'); + expect(record.data).toEqual({ name: 'alice' }); + expect(record.hashedData).toEqual({ name: 'h' }); + expect(record.tokens).toEqual({ name: 'tok' }); + expect(record.httpCode).toBe(200); + expect(record.error).toBeNull(); + expect(record.requestId).toBeUndefined(); + }); + + it('multi-table: builds the records[] array from request.records', async () => { + const getRecords = resourceMethod({ records: [] }); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + + const req = new GetRequest('', [], [ + { tableName: 't1', ids: ['a', 'b'] }, + { tableName: 't2', ids: ['c'] }, + ]); + await controller.get(req); + + const [genReq] = getRecords.mock.calls[0]; + expect(genReq.records).toEqual([ + { tableName: 't1', skyflowIDs: ['a', 'b'] }, + { tableName: 't2', skyflowIDs: ['c'] }, + ]); + expect(genReq.tableName).toBeUndefined(); + expect(genReq.skyflowIDs).toBeUndefined(); + }); + + it('maps options: columns, columnRedactions, uniqueValues->{data}, limit, offset', async () => { + const getRecords = resourceMethod({ records: [] }); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + + const options = new GetOptions(); + options.setFields(['name', 'email']); + options.setColumnRedactions([{ columnName: 'ssn', redaction: 'MASKED' }]); + options.setUniqueValues([{ email: 'a@b.com' }, { email: 'c@d.com' }]); + options.setLimit(5); + options.setOffset(1); + + await controller.get(new GetRequest('cards', ['id1']), options); + + const [genReq] = getRecords.mock.calls[0]; + expect(genReq.columns).toEqual(['name', 'email']); + expect(genReq.columnRedactions).toEqual([{ columnName: 'ssn', redaction: 'MASKED' }]); + expect(genReq.uniqueValues).toEqual([{ data: { email: 'a@b.com' } }, { data: { email: 'c@d.com' } }]); + expect(genReq.limit).toBe(5); + expect(genReq.offset).toBe(1); + }); + + it('honors limit/offset of 0 (uses !== undefined guard)', async () => { + const getRecords = resourceMethod({ records: [] }); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + + const options = new GetOptions(); + options.setLimit(0); + options.setOffset(0); + await controller.get(new GetRequest('cards', ['id1']), options); + + const [genReq] = getRecords.mock.calls[0]; + expect(genReq.limit).toBe(0); + expect(genReq.offset).toBe(0); + }); + + it('per-record error gets requestId from the header', async () => { + const getRecords = resourceMethod({ records: [{ httpCode: 404, error: 'not found' }] }); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + const resp = await controller.get(new GetRequest('cards', ['id1'])); + expect(resp.records[0].error).toBe('not found'); + expect(resp.records[0].requestId).toBe(REQUEST_ID); + }); + + it('rejects when the generated method throws', async () => { + const getRecords = rejectingMethod(new Error('boom-get')); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + await expect(controller.get(new GetRequest('cards', ['id1']))).rejects.toThrow('boom-get'); + }); +}); diff --git a/flowvault/test/controller/insert.test.ts b/flowvault/test/controller/insert.test.ts new file mode 100644 index 00000000..9f4f25c5 --- /dev/null +++ b/flowvault/test/controller/insert.test.ts @@ -0,0 +1,113 @@ +import { InsertRequest, InsertOptions, InsertResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoHeaders, + REQUEST_ID, + silenceConsole, +} from '../helpers'; + +silenceConsole(); + +describe('VaultController.insert', () => { + it('maps records, calls the generated method once, and builds the request', async () => { + const insertRecords = resourceMethod({ + records: [ + { + skyflowID: 'id1', + tokens: { card_number: 'tok_1' }, + hashedData: { card_number: 'hash_1' }, + httpCode: 200, + tableName: 'cards', + }, + ], + }); + const controller = new VaultController(makeFakeClient({ records: { insertRecords } })); + + const req = new InsertRequest('cards', [{ data: { card_number: '4111' }, tokens: { card_number: 'byot_1' } }]); + const options = new InsertOptions(); + options.setUpsert({ updateType: 'UPDATE', uniqueColumns: ['card_number'] }); + + const resp = await controller.insert(req, options); + + expect(resp).toBeInstanceOf(InsertResponse); + expect(insertRecords).toHaveBeenCalledTimes(1); + + // Request built correctly (vaultID, tableName, BYOT tokens, upsert). + const [genReq, opts] = insertRecords.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.tableName).toBe('cards'); + expect(genReq.records).toEqual([{ data: { card_number: '4111' }, tokens: { card_number: 'byot_1' } }]); + expect(genReq.upsert).toEqual({ updateType: 'UPDATE', uniqueColumns: ['card_number'] }); + expect(opts.headers).toBeDefined(); + + // Response mapped field-by-field. + const record = resp.records[0]; + expect(record.skyflowId).toBe('id1'); + expect(record.fields).toEqual({ card_number: 'tok_1' }); // tokens preferred over data + expect(record.hashedData).toEqual({ card_number: 'hash_1' }); + expect(record.tableName).toBe('cards'); + expect(record.httpCode).toBe(200); + expect(record.error).toBeNull(); + expect(record.requestId).toBeUndefined(); + }); + + it('omits BYOT tokens and upsert when not provided; fields fall back to data', async () => { + const insertRecords = resourceMethod({ + records: [{ skyflowID: 'id1', data: { card_number: '4111' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ records: { insertRecords } })); + + const resp = await controller.insert(new InsertRequest('cards', [{ data: { card_number: '4111' } }])); + + const [genReq] = insertRecords.mock.calls[0]; + expect(genReq.records[0]).toEqual({ data: { card_number: '4111' } }); + expect(genReq.records[0].tokens).toBeUndefined(); + expect(genReq.upsert).toBeUndefined(); + expect(resp.records[0].fields).toEqual({ card_number: '4111' }); // data used when no tokens + }); + + it('populates error + requestId (from header) on a per-record error', async () => { + const insertRecords = resourceMethod({ records: [{ httpCode: 400, error: 'bad record' }] }); + const controller = new VaultController(makeFakeClient({ records: { insertRecords } })); + + const resp = await controller.insert(new InsertRequest('cards', [{ data: { card_number: '4111' } }])); + + expect(resp.records[0].error).toBe('bad record'); + expect(resp.records[0].requestId).toBe(REQUEST_ID); + expect(resp.records[0].skyflowId).toBeNull(); + expect(resp.records[0].fields).toBeNull(); + }); + + it('sets requestId to null on an errored record when the raw response has no headers', async () => { + const insertRecords = resourceMethod({ records: [{ httpCode: 400, error: 'bad' }] }, rawResponseNoHeaders); + const controller = new VaultController(makeFakeClient({ records: { insertRecords } })); + const resp = await controller.insert(new InsertRequest('cards', [{ data: { a: 1 } }])); + expect(resp.records[0].error).toBe('bad'); + expect(resp.records[0].requestId).toBeNull(); + }); + + it('returns an empty records array when the response has none', async () => { + const insertRecords = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ records: { insertRecords } })); + const resp = await controller.insert(new InsertRequest('cards', [{ data: { a: 1 } }])); + expect(resp.records).toEqual([]); + }); + + it('rejects (via failureResponse) when the generated method throws', async () => { + const insertRecords = rejectingMethod(new Error('network down')); + const controller = new VaultController(makeFakeClient({ records: { insertRecords } })); + await expect( + controller.insert(new InsertRequest('cards', [{ data: { a: 1 } }])), + ).rejects.toThrow('network down'); + }); + + it('rejects on invalid input before calling the generated method', async () => { + const insertRecords = resourceMethod({ records: [] }); + const controller = new VaultController(makeFakeClient({ records: { insertRecords } })); + await expect(controller.insert(new InsertRequest('', [{ data: { a: 1 } }]))).rejects.toBeDefined(); + expect(insertRecords).not.toHaveBeenCalled(); + }); +}); diff --git a/flowvault/test/controller/query.test.ts b/flowvault/test/controller/query.test.ts new file mode 100644 index 00000000..d5858ea6 --- /dev/null +++ b/flowvault/test/controller/query.test.ts @@ -0,0 +1,52 @@ +import { QueryRequest, QueryResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoId, + REQUEST_ID, + silenceConsole, +} from '../helpers'; + +silenceConsole(); + +describe('VaultController.query', () => { + it('maps records->fields, metadata.columns and the call-level requestId', async () => { + const executeQuery = resourceMethod({ + records: [{ data: { name: 'alice' } }, { data: { name: 'bob' } }], + metadata: { columns: ['name'] }, + }); + const controller = new VaultController(makeFakeClient({ query: { executeQuery } })); + + const resp = await controller.query(new QueryRequest('SELECT name FROM cards')); + + expect(resp).toBeInstanceOf(QueryResponse); + expect(executeQuery).toHaveBeenCalledTimes(1); + const [genReq] = executeQuery.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.query).toBe('SELECT name FROM cards'); + + expect(resp.fields).toEqual([{ name: 'alice' }, { name: 'bob' }]); + expect(resp.metadata.columns).toEqual(['name']); + expect(resp.errors).toBeNull(); + expect(resp.requestId).toBe(REQUEST_ID); + }); + + it('defaults fields to {} and columns to [] when absent; requestId falls back to ""', async () => { + const executeQuery = resourceMethod({ records: [{}, { data: null }] }, rawResponseNoId); + const controller = new VaultController(makeFakeClient({ query: { executeQuery } })); + + const resp = await controller.query(new QueryRequest('SELECT 1')); + + expect(resp.fields).toEqual([{}, {}]); + expect(resp.metadata.columns).toEqual([]); + expect(resp.requestId).toBe(''); + }); + + it('rejects when the generated method throws', async () => { + const executeQuery = rejectingMethod(new Error('boom-query')); + const controller = new VaultController(makeFakeClient({ query: { executeQuery } })); + await expect(controller.query(new QueryRequest('SELECT 1'))).rejects.toThrow('boom-query'); + }); +}); diff --git a/flowvault/test/controller/update.test.ts b/flowvault/test/controller/update.test.ts new file mode 100644 index 00000000..cc3a345e --- /dev/null +++ b/flowvault/test/controller/update.test.ts @@ -0,0 +1,60 @@ +import { UpdateRequest, UpdateResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; + +silenceConsole(); + +describe('VaultController.update', () => { + it('builds skyflowID/data(+BYOT tokens) records and maps the response', async () => { + const updateRecords = resourceMethod({ + records: [{ skyflowID: 'id1', tokens: { x: 'tok' }, hashedData: { x: 'h' }, tableName: 'cards', httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ records: { updateRecords } })); + + const req = new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 1 }, tokens: { x: 'byot' } }]); + const resp = await controller.update(req); + + expect(resp).toBeInstanceOf(UpdateResponse); + expect(updateRecords).toHaveBeenCalledTimes(1); + const [genReq] = updateRecords.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.tableName).toBe('cards'); + expect(genReq.records).toEqual([{ skyflowID: 'id1', data: { x: 1 }, tokens: { x: 'byot' } }]); + + const record = resp.records[0]; + expect(record.skyflowId).toBe('id1'); + expect(record.fields).toEqual({ x: 'tok' }); // tokens preferred over data + expect(record.hashedData).toEqual({ x: 'h' }); + expect(record.tableName).toBe('cards'); + expect(record.httpCode).toBe(200); + expect(record.error).toBeNull(); + expect(record.requestId).toBeUndefined(); + }); + + it('omits BYOT tokens when not provided; fields fall back to data', async () => { + const updateRecords = resourceMethod({ records: [{ skyflowID: 'id1', data: { x: 2 }, httpCode: 200 }] }); + const controller = new VaultController(makeFakeClient({ records: { updateRecords } })); + + const resp = await controller.update(new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 2 } }])); + + const [genReq] = updateRecords.mock.calls[0]; + expect(genReq.records[0].tokens).toBeUndefined(); + expect(resp.records[0].fields).toEqual({ x: 2 }); + }); + + it('per-record error gets requestId from the header', async () => { + const updateRecords = resourceMethod({ records: [{ httpCode: 400, error: 'update failed' }] }); + const controller = new VaultController(makeFakeClient({ records: { updateRecords } })); + const resp = await controller.update(new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 1 } }])); + expect(resp.records[0].error).toBe('update failed'); + expect(resp.records[0].requestId).toBe(REQUEST_ID); + }); + + it('rejects when the generated method throws', async () => { + const updateRecords = rejectingMethod(new Error('boom-update')); + const controller = new VaultController(makeFakeClient({ records: { updateRecords } })); + await expect( + controller.update(new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 1 } }])), + ).rejects.toThrow('boom-update'); + }); +}); diff --git a/flowvault/test/controller/upload-files.test.ts b/flowvault/test/controller/upload-files.test.ts new file mode 100644 index 00000000..70da624e --- /dev/null +++ b/flowvault/test/controller/upload-files.test.ts @@ -0,0 +1,229 @@ +jest.mock('fs/promises', () => ({ readFile: jest.fn() })); + +import * as fsPromises from 'fs/promises'; +import { UploadFilesRequest, UploadFilesResponse } from '../../src'; +import VaultController from '../../src/controller/vault'; +import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; + +silenceConsole(); + +const readFileMock = fsPromises.readFile as unknown as jest.Mock; + +/** Minimal File-like with arrayBuffer() + name, as the controller consumes. */ +function fakeFile(bytes: number[], name: string): File { + return { + name, + arrayBuffer: async () => new Uint8Array(bytes).buffer, + } as unknown as File; +} + +describe('VaultController.uploadFiles', () => { + let originalFetch: typeof global.fetch; + let fetchMock: jest.Mock; + + beforeEach(() => { + originalFetch = (global as { fetch: typeof global.fetch }).fetch; + fetchMock = jest.fn().mockResolvedValue({ ok: true, status: 200 }); + (global as unknown as { fetch: unknown }).fetch = fetchMock; + readFileMock.mockReset(); + }); + + afterEach(() => { + (global as unknown as { fetch: unknown }).fetch = originalFetch; + }); + + it('filePath source: reads bytes, infers fileName + Content-Type, PUTs with no Authorization', async () => { + readFileMock.mockResolvedValue(Buffer.from('file-bytes')); + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 'onboarding', data: { doc: 'https://signed/doc' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const req = new UploadFilesRequest([ + { tableName: 'onboarding', skyflowId: 'row1', columns: [{ column: 'doc', filePath: '/tmp/report.png' }] }, + ]); + const resp = await controller.uploadFiles(req); + + expect(resp).toBeInstanceOf(UploadFilesResponse); + expect(uploadFiles).toHaveBeenCalledTimes(1); + + // Phase A request: skyflowID + columns (fileName inferred from the path). + const [genReq] = uploadFiles.mock.calls[0]; + expect(genReq.vaultID).toBe('vault123'); + expect(genReq.records[0].skyflowID).toBe('row1'); + expect(genReq.records[0].columns).toEqual([{ column: 'doc', fileName: 'report.png' }]); + + // Phase B PUT to the signed URL. + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0]; + expect(url).toBe('https://signed/doc'); + expect(init.method).toBe('PUT'); + expect(init.headers.Authorization).toBeUndefined(); + expect(init.headers['Content-Type']).toBe('image/png'); + expect(Buffer.from(init.body).toString()).toBe('file-bytes'); + + const col = resp.records[0].columns[0]; + expect(col.column).toBe('doc'); + expect(col.fileName).toBe('report.png'); + expect(col.uploadStatus).toBe('UPLOADED'); + expect(resp.records[0].skyflowId).toBe('id1'); + expect(resp.records[0].httpCode).toBe(200); + expect(resp.records[0].error).toBeNull(); + }); + + it('base64 source: uses provided fileName, decodes bytes, infers Content-Type', async () => { + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { resume: 'https://signed/resume' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const req = new UploadFilesRequest([ + { tableName: 't', columns: [{ column: 'resume', base64: Buffer.from('hello').toString('base64'), fileName: 'resume.pdf' }] }, + ]); + const resp = await controller.uploadFiles(req); + + // No skyflowId provided -> omitted from the generated record. + const [genReq] = uploadFiles.mock.calls[0]; + expect(genReq.records[0].skyflowID).toBeUndefined(); + expect(genReq.records[0].columns).toEqual([{ column: 'resume', fileName: 'resume.pdf' }]); + + const [, init] = fetchMock.mock.calls[0]; + expect(init.headers['Content-Type']).toBe('application/pdf'); + expect(Buffer.from(init.body).toString()).toBe('hello'); + expect(resp.records[0].columns[0].uploadStatus).toBe('UPLOADED'); + }); + + it('file source: reads arrayBuffer, infers fileName from file.name', async () => { + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { pic: 'https://signed/pic' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const req = new UploadFilesRequest([ + { tableName: 't', columns: [{ column: 'pic', file: fakeFile([1, 2, 3], 'photo.jpg') }] }, + ]); + const resp = await controller.uploadFiles(req); + + const [genReq] = uploadFiles.mock.calls[0]; + expect(genReq.records[0].columns).toEqual([{ column: 'pic', fileName: 'photo.jpg' }]); + const [, init] = fetchMock.mock.calls[0]; + expect(init.headers['Content-Type']).toBe('image/jpeg'); + expect(Array.from(init.body as Uint8Array)).toEqual([1, 2, 3]); + expect(resp.records[0].columns[0].fileName).toBe('photo.jpg'); + expect(resp.records[0].columns[0].uploadStatus).toBe('UPLOADED'); + }); + + it('unknown extension falls back to application/octet-stream', async () => { + readFileMock.mockResolvedValue(Buffer.from('zipbytes')); + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { archive: 'https://signed/archive' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'archive', filePath: '/tmp/a.zip' }] }]), + ); + const [, init] = fetchMock.mock.calls[0]; + expect(init.headers['Content-Type']).toBe('application/octet-stream'); + }); + + it('FAILED when the signed-URL PUT responds not-ok', async () => { + fetchMock.mockResolvedValue({ ok: false, status: 500 }); + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { resume: 'https://signed/resume' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + + const col = resp.records[0].columns[0]; + expect(col.uploadStatus).toBe('FAILED'); + expect(col.error).toBe('Upload failed with status 500.'); + }); + + it('FAILED (with thrown message) when the signed-URL PUT throws', async () => { + fetchMock.mockRejectedValue(new Error('conn reset')); + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { resume: 'https://signed/resume' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + + const col = resp.records[0].columns[0]; + expect(col.uploadStatus).toBe('FAILED'); + expect(col.error).toBe('conn reset'); + }); + + it('SKIPPED (with record error) when the response record itself errored; requestId from header', async () => { + const uploadFiles = resourceMethod({ + records: [{ tableName: 't', httpCode: 400, error: 'row rejected' }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(resp.records[0].error).toBe('row rejected'); + expect(resp.records[0].requestId).toBe(REQUEST_ID); + const col = resp.records[0].columns[0]; + expect(col.uploadStatus).toBe('SKIPPED'); + expect(col.error).toBe('row rejected'); + }); + + it('SKIPPED (no error) when no signed URL is returned for a column', async () => { + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: {}, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + + expect(fetchMock).not.toHaveBeenCalled(); + const col = resp.records[0].columns[0]; + expect(col.uploadStatus).toBe('SKIPPED'); + expect(col.error).toBeUndefined(); + }); + + it('handles multiple columns in one record (UPLOADED + SKIPPED aligned by column name)', async () => { + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { a: 'https://signed/a' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const resp = await controller.uploadFiles( + new UploadFilesRequest([ + { + tableName: 't', + columns: [ + { column: 'a', base64: 'aGk=', fileName: 'a.pdf' }, + { column: 'b', base64: 'aGk=', fileName: 'b.pdf' }, + ], + }, + ]), + ); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const cols = resp.records[0].columns; + expect(cols.find((c) => c.column === 'a')?.uploadStatus).toBe('UPLOADED'); + expect(cols.find((c) => c.column === 'b')?.uploadStatus).toBe('SKIPPED'); + }); + + it('rejects when the generated uploadFiles method throws', async () => { + const uploadFiles = rejectingMethod(new Error('boom-upload')); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + await expect( + controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ), + ).rejects.toThrow('boom-upload'); + }); +}); diff --git a/flowvault/test/helpers.ts b/flowvault/test/helpers.ts new file mode 100644 index 00000000..144f1cb2 --- /dev/null +++ b/flowvault/test/helpers.ts @@ -0,0 +1,68 @@ +// Shared test helpers for the FlowVault hand-written layer. +// NOTE: this file is intentionally NOT a *.test.ts, so Jest does not run it as +// a suite; it only provides the mocking seam the controller/client tests reuse. +import { LogLevel, SkyflowError, SKYFLOW_ERROR_CODE } from '../src'; + +/** A rawResponse stub whose headers carry an x-request-id (matches smoke test). */ +export const REQUEST_ID = 'req-abc-123'; +export const rawResponse = { headers: new Map([['x-request-id', REQUEST_ID]]) }; + +/** A rawResponse stub carrying NO x-request-id header. */ +export const rawResponseNoId = { headers: new Map() }; + +/** A rawResponse stub with no usable headers object at all. */ +export const rawResponseNoHeaders = {}; + +/** + * Builds a fake VaultClient exposing only what the controller touches. Any + * resource client (records/tokens/files/query) is supplied via `overrides`. + */ +export function makeFakeClient(overrides: Record): any { + return { + vaultId: 'vault123', + getLogLevel: () => LogLevel.ERROR, + getToken: jest.fn().mockResolvedValue(undefined), + // Pass through real Errors; otherwise wrap. The real normalizer is covered + // by the client tests. + failureResponse: (e: unknown) => + e instanceof Error ? e : new SkyflowError(SKYFLOW_ERROR_CODE.INTERNAL_SERVER_ERROR), + ...overrides, + }; +} + +/** + * A generated-resource-method stub. Returns an object with `withRawResponse()` + * resolving to `{ data, rawResponse }`, matching the generated client contract. + */ +export function resourceMethod(data: unknown, rr: unknown = rawResponse): jest.Mock { + return jest.fn().mockReturnValue({ + withRawResponse: () => Promise.resolve({ data, rawResponse: rr }), + }); +} + +/** A generated-resource-method stub whose `withRawResponse()` rejects. */ +export function rejectingMethod(err: unknown): jest.Mock { + return jest.fn().mockReturnValue({ + withRawResponse: () => Promise.reject(err), + }); +} + +/** Build a decodable (unsigned) JWT whose `exp` is `secondsFromNow` out. */ +export function makeJwt(secondsFromNow: number): string { + const header = Buffer.from(JSON.stringify({ alg: 'none', typ: 'JWT' })).toString('base64url'); + const exp = Math.floor(Date.now() / 1000) + secondsFromNow; + const payload = Buffer.from(JSON.stringify({ exp })).toString('base64url'); + return `${header}.${payload}.sig`; +} + +/** Silence console noise from negative-path printLog() calls for a test file. */ +export function silenceConsole(): void { + beforeAll(() => { + jest.spyOn(console, 'error').mockImplementation(() => undefined); + jest.spyOn(console, 'warn').mockImplementation(() => undefined); + jest.spyOn(console, 'log').mockImplementation(() => undefined); + }); + afterAll(() => { + jest.restoreAllMocks(); + }); +} diff --git a/flowvault/test/model/options.test.ts b/flowvault/test/model/options.test.ts new file mode 100644 index 00000000..b8d1ac43 --- /dev/null +++ b/flowvault/test/model/options.test.ts @@ -0,0 +1,61 @@ +import { + InsertOptions, + GetOptions, + UpdateOptions, + DeleteOptions, + QueryOptions, + DetokenizeOptions, + GetTokensOptions, + UploadFilesOptions, + DeleteFilesOptions, +} from '../../src'; + +describe('FlowVault options models', () => { + it('InsertOptions: upsert setter/getter + inherited request headers', () => { + const opts = new InsertOptions(); + expect(opts.getUpsert()).toBeUndefined(); + opts.setUpsert({ updateType: 'UPDATE', uniqueColumns: ['email'] }); + expect(opts.getUpsert()).toEqual({ updateType: 'UPDATE', uniqueColumns: ['email'] }); + + opts.setRequestHeaders({ 'x-custom': 'v' }); + expect(opts.getRequestHeaders()).toEqual({ 'x-custom': 'v' }); + }); + + it('GetOptions: all setters/getters round-trip', () => { + const opts = new GetOptions(); + expect(opts.getFields()).toBeUndefined(); + expect(opts.getColumnRedactions()).toBeUndefined(); + expect(opts.getUniqueValues()).toBeUndefined(); + expect(opts.getLimit()).toBeUndefined(); + expect(opts.getOffset()).toBeUndefined(); + + opts.setFields(['name', 'email']); + opts.setColumnRedactions([{ columnName: 'ssn', redaction: 'MASKED' }]); + opts.setUniqueValues([{ email: 'a@b.com' }]); + opts.setLimit(10); + opts.setOffset(2); + + expect(opts.getFields()).toEqual(['name', 'email']); + expect(opts.getColumnRedactions()).toEqual([{ columnName: 'ssn', redaction: 'MASKED' }]); + expect(opts.getUniqueValues()).toEqual([{ email: 'a@b.com' }]); + expect(opts.getLimit()).toBe(10); + expect(opts.getOffset()).toBe(2); + }); + + it('the remaining options classes construct empty and inherit request headers', () => { + const classes = [ + new UpdateOptions(), + new DeleteOptions(), + new QueryOptions(), + new DetokenizeOptions(), + new GetTokensOptions(), + new UploadFilesOptions(), + new DeleteFilesOptions(), + ]; + classes.forEach((opts) => { + expect(opts.getRequestHeaders()).toBeUndefined(); + opts.setRequestHeaders({ 'x-trace': 'abc' }); + expect(opts.getRequestHeaders()).toEqual({ 'x-trace': 'abc' }); + }); + }); +}); diff --git a/flowvault/test/model/requests.test.ts b/flowvault/test/model/requests.test.ts new file mode 100644 index 00000000..d3e65795 --- /dev/null +++ b/flowvault/test/model/requests.test.ts @@ -0,0 +1,123 @@ +import { + InsertRequest, + GetRequest, + UpdateRequest, + DeleteRequest, + QueryRequest, + DetokenizeRequest, + GetTokensRequest, + UploadFilesRequest, + DeleteFilesRequest, +} from '../../src'; + +describe('FlowVault request models', () => { + it('InsertRequest: ctor, getters and setters round-trip', () => { + const req = new InsertRequest('cards', [{ data: { card_number: '4111' } }]); + expect(req.tableName).toBe('cards'); + expect(req.records).toEqual([{ data: { card_number: '4111' } }]); + + req.tableName = 'people'; + req.records = [{ data: { ssn: '123' }, tokens: { ssn: 'tok' } }]; + expect(req.tableName).toBe('people'); + expect(req.records[0].tokens).toEqual({ ssn: 'tok' }); + }); + + it('GetRequest: ctor, getters and setters (table/ids/records)', () => { + const req = new GetRequest('cards', ['id1', 'id2']); + expect(req.table).toBe('cards'); + expect(req.ids).toEqual(['id1', 'id2']); + expect(req.records).toBeUndefined(); + + req.table = 'people'; + req.ids = ['id3']; + req.records = [{ tableName: 't2', ids: ['a', 'b'] }]; + expect(req.table).toBe('people'); + expect(req.ids).toEqual(['id3']); + expect(req.records).toEqual([{ tableName: 't2', ids: ['a', 'b'] }]); + + // Multi-table via ctor third arg. + const multi = new GetRequest('', [], [{ tableName: 't', ids: ['x'] }]); + expect(multi.records).toEqual([{ tableName: 't', ids: ['x'] }]); + }); + + it('UpdateRequest: ctor, getters and setters', () => { + const req = new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 1 } }]); + expect(req.tableName).toBe('cards'); + expect(req.records[0].skyflowId).toBe('id1'); + + req.tableName = 'people'; + req.records = [{ skyflowId: 'id2', data: { y: 2 }, tokens: { y: 'tok' } }]; + expect(req.tableName).toBe('people'); + expect(req.records[0].tokens).toEqual({ y: 'tok' }); + }); + + it('DeleteRequest: ctor, getters and setters (ids + uniqueValues)', () => { + const req = new DeleteRequest('cards', ['id1']); + expect(req.table).toBe('cards'); + expect(req.ids).toEqual(['id1']); + expect(req.uniqueValues).toBeUndefined(); + + req.table = 'people'; + req.ids = ['id2', 'id3']; + req.uniqueValues = [{ email: 'a@b.com' }]; + expect(req.table).toBe('people'); + expect(req.ids).toEqual(['id2', 'id3']); + expect(req.uniqueValues).toEqual([{ email: 'a@b.com' }]); + + const withUnique = new DeleteRequest('t', [], [{ ssn: '111' }]); + expect(withUnique.uniqueValues).toEqual([{ ssn: '111' }]); + }); + + it('QueryRequest: ctor, getter and setter', () => { + const req = new QueryRequest('SELECT * FROM cards'); + expect(req.query).toBe('SELECT * FROM cards'); + req.query = 'SELECT name FROM cards'; + expect(req.query).toBe('SELECT name FROM cards'); + }); + + it('DetokenizeRequest: ctor, getters and setters (tokens + redactions)', () => { + const req = new DetokenizeRequest(['tok1', 'tok2']); + expect(req.tokens).toEqual(['tok1', 'tok2']); + expect(req.tokenGroupRedactions).toBeUndefined(); + + req.tokens = ['tok3']; + req.tokenGroupRedactions = [{ tokenGroupName: 'g', redaction: 'MASKED' }]; + expect(req.tokens).toEqual(['tok3']); + expect(req.tokenGroupRedactions).toEqual([{ tokenGroupName: 'g', redaction: 'MASKED' }]); + + const withRedaction = new DetokenizeRequest(['t'], [{ tokenGroupName: 'cards', redaction: 'PLAIN_TEXT' }]); + expect(withRedaction.tokenGroupRedactions?.[0].tokenGroupName).toBe('cards'); + }); + + it('GetTokensRequest: ctor, getter and setter', () => { + const req = new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }]); + expect(req.records[0].value).toBe('4111'); + expect(req.records[0].tokenGroupName).toBe('cards'); + + req.records = [{ value: '5555', tokenGroupName: 'cvv' }]; + expect(req.records[0].value).toBe('5555'); + }); + + it('UploadFilesRequest: ctor, getter and setter', () => { + const req = new UploadFilesRequest([ + { tableName: 'onboarding', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }, + ]); + expect(req.records[0].tableName).toBe('onboarding'); + expect(req.records[0].columns[0].column).toBe('resume'); + + req.records = [{ tableName: 't', skyflowId: 'id1', columns: [{ column: 'c', filePath: '/p/x.png' }] }]; + expect(req.records[0].skyflowId).toBe('id1'); + expect(req.records[0].columns[0].filePath).toBe('/p/x.png'); + }); + + it('DeleteFilesRequest: ctor, getter and setter', () => { + const req = new DeleteFilesRequest([ + { tableName: 'onboarding', columns: ['resume'], skyflowId: 'id1' }, + ]); + expect(req.records[0].columns).toEqual(['resume']); + expect(req.records[0].skyflowId).toBe('id1'); + + req.records = [{ tableName: 't', columns: ['c'], uniqueValues: [{ a: 1 }] }]; + expect(req.records[0].uniqueValues).toEqual([{ a: 1 }]); + }); +}); diff --git a/flowvault/test/model/responses.test.ts b/flowvault/test/model/responses.test.ts new file mode 100644 index 00000000..e92a4694 --- /dev/null +++ b/flowvault/test/model/responses.test.ts @@ -0,0 +1,89 @@ +import { + InsertResponse, + GetResponse, + UpdateResponse, + DeleteResponse, + QueryResponse, + DetokenizeResponse, + GetTokensResponse, + UploadFilesResponse, + DeleteFilesResponse, +} from '../../src'; + +describe('FlowVault response models', () => { + it('InsertResponse holds its records', () => { + const records = [ + { tableName: 'cards', skyflowId: 'id1', fields: { card_number: 'tok' }, hashedData: null, httpCode: 200, error: null }, + ]; + const resp = new InsertResponse({ records }); + expect(resp.records).toBe(records); + expect(resp.records[0].skyflowId).toBe('id1'); + }); + + it('GetResponse holds its records', () => { + const records = [ + { skyflowId: 'id1', tableName: 'cards', data: { name: 'a' }, hashedData: null, tokens: null, httpCode: 200, error: null }, + ]; + const resp = new GetResponse({ records }); + expect(resp.records[0].data).toEqual({ name: 'a' }); + }); + + it('UpdateResponse holds its records', () => { + const records = [ + { skyflowId: 'id1', tableName: 'cards', fields: { x: 1 }, hashedData: null, httpCode: 200, error: null }, + ]; + const resp = new UpdateResponse({ records }); + expect(resp.records[0].fields).toEqual({ x: 1 }); + }); + + it('DeleteResponse holds its records (no tableName field)', () => { + const records = [{ skyflowId: 'id1', httpCode: 200, error: null }]; + const resp = new DeleteResponse({ records }); + expect(resp.records[0].skyflowId).toBe('id1'); + expect((resp.records[0] as Record).tableName).toBeUndefined(); + }); + + it('DetokenizeResponse holds its records', () => { + const records = [ + { token: 'tok1', tokenGroupName: 'cards', value: '4111', metadata: { skyflowId: 'id1', tableName: 'cards' }, httpCode: 200, error: null }, + ]; + const resp = new DetokenizeResponse({ records }); + expect(resp.records[0].value).toBe('4111'); + expect(resp.records[0].metadata).toEqual({ skyflowId: 'id1', tableName: 'cards' }); + }); + + it('GetTokensResponse holds its records', () => { + const records = [{ value: '4111', tokenGroupName: 'cards', token: 'tok', httpCode: 200, error: null }]; + const resp = new GetTokensResponse({ records }); + expect(resp.records[0].token).toBe('tok'); + }); + + it('UploadFilesResponse holds its records', () => { + const records = [ + { skyflowId: 'id1', tableName: 't', columns: [{ column: 'resume', fileName: 'r.pdf', uploadStatus: 'UPLOADED' as const }], httpCode: 200, error: null }, + ]; + const resp = new UploadFilesResponse({ records }); + expect(resp.records[0].columns[0].uploadStatus).toBe('UPLOADED'); + }); + + it('DeleteFilesResponse holds its records', () => { + const records = [ + { skyflowId: 'id1', tableName: 't', columns: [{ column: 'resume', status: 'DELETED' }], httpCode: 200, error: null }, + ]; + const resp = new DeleteFilesResponse({ records }); + expect(resp.records[0].columns?.[0].status).toBe('DELETED'); + }); + + it('QueryResponse holds fields/errors/metadata/requestId', () => { + const resp = new QueryResponse({ + fields: [{ name: 'alice' }, { name: 'bob' }], + errors: null, + metadata: { columns: ['name'] }, + requestId: 'req-1', + }); + expect(resp.fields).toEqual([{ name: 'alice' }, { name: 'bob' }]); + expect(resp.errors).toBeNull(); + expect(resp.metadata.columns).toEqual(['name']); + expect(resp.requestId).toBe('req-1'); + }); +}); diff --git a/flowvault/test/skyflow/skyflow.test.ts b/flowvault/test/skyflow/skyflow.test.ts new file mode 100644 index 00000000..645b4095 --- /dev/null +++ b/flowvault/test/skyflow/skyflow.test.ts @@ -0,0 +1,157 @@ +import { + Skyflow, + Env, + LogLevel, + SkyflowError, + SKYFLOW_ERROR_CODE, + getSdkIdentity, + parameterizedString, +} from '../../src'; +import VaultController from '../../src/controller/vault'; +import { silenceConsole } from '../helpers'; + +silenceConsole(); + +const vaultConfig = (vaultId: string, clusterId = 'abc-cluster') => ({ + vaultId, + clusterId, + env: Env.PROD, + credentials: { apiKey: 'sky-dummy-key' }, +}); + +function newSkyflow(vaultIds: string[] = ['vault123'], logLevel: LogLevel = LogLevel.ERROR): Skyflow { + return new Skyflow({ vaultConfigs: vaultIds.map((id) => vaultConfig(id)), logLevel }); +} + +/** Read the registry entry's VaultClient (private, accessed for assertions). */ +function clientOf(sky: Skyflow, vaultId: string): { getLogLevel: () => LogLevel; skyflowCredentials?: unknown } { + return (sky as unknown as { vaultClients: Record LogLevel; skyflowCredentials?: unknown } }> }) + .vaultClients[vaultId].client; +} + +function expectCode( + fn: () => void, + code: { httpCode: number; message: string }, + args: (string | number)[] = [], +): void { + let thrown: unknown; + try { + fn(); + } catch (e) { + thrown = e; + } + expect(thrown).toBeInstanceOf(SkyflowError); + // Parameterized messages (e.g. "%s1 is missing...") are substituted at throw time. + const expected = args.length > 0 ? parameterizedString(code.message, ...args) : code.message; + expect((thrown as SkyflowError).error?.message).toBe(expected); +} + +describe('Skyflow facade — construction and vault()', () => { + it('validates config: a config with neither vaultConfigs nor connectionConfigs throws', () => { + expectCode(() => new Skyflow({} as never), SKYFLOW_ERROR_CODE.INVALID_SKYFLOW_CONFIG); + }); + + it('throws INVALID_LOG_LEVEL for an unknown log level', () => { + expectCode( + () => new Skyflow({ vaultConfigs: [vaultConfig('vault123')], logLevel: 'BOGUS' as never }), + SKYFLOW_ERROR_CODE.INVALID_LOG_LEVEL, + ); + }); + + it('registers vaults; vault(id) and vault() (first) return a VaultController', () => { + const sky = newSkyflow(['vault123', 'vault456']); + expect(sky.vault('vault123')).toBeInstanceOf(VaultController); + expect(sky.vault('vault456')).toBeInstanceOf(VaultController); + expect(sky.vault()).toBe(sky.vault('vault123')); // no id -> first registered + }); + + it('vault() on an empty registry throws EMPTY_VAULT_CLIENTS', () => { + const sky = new Skyflow({ vaultConfigs: [], logLevel: LogLevel.ERROR }); + expectCode(() => sky.vault(), SKYFLOW_ERROR_CODE.EMPTY_VAULT_CLIENTS); + }); + + it('vault(unknownId) throws VAULT_ID_NOT_IN_CONFIG_LIST', () => { + const sky = newSkyflow(); + expectCode(() => sky.vault('nope'), SKYFLOW_ERROR_CODE.VAULT_ID_NOT_IN_CONFIG_LIST, ['nope']); + }); +}); + +describe('Skyflow facade — vault config management', () => { + it('addVaultConfig registers a new vault and rejects a duplicate id', () => { + const sky = newSkyflow(); + sky.addVaultConfig(vaultConfig('vault999')); + expect(sky.vault('vault999')).toBeInstanceOf(VaultController); + expectCode(() => sky.addVaultConfig(vaultConfig('vault999')), SKYFLOW_ERROR_CODE.VAULT_ID_EXITS_IN_CONFIG_LIST, ['vault999']); + }); + + it('addVaultConfig works with a minimal config (no env -> PROD, no credentials)', () => { + const sky = newSkyflow(); + sky.addVaultConfig({ vaultId: 'vaultMinimal', clusterId: 'min-cluster' }); + expect(sky.vault('vaultMinimal')).toBeInstanceOf(VaultController); + expect(sky.getVaultConfig('vaultMinimal').vaultId).toBe('vaultMinimal'); + }); + + it('updateVaultConfig updates an existing config and rejects an unknown id', () => { + const sky = newSkyflow(); + sky.updateVaultConfig({ vaultId: 'vault123', clusterId: 'new-cluster' }); + expect(sky.getVaultConfig('vault123').clusterId).toBe('new-cluster'); + expectCode(() => sky.updateVaultConfig({ vaultId: 'nope', clusterId: 'c' }), SKYFLOW_ERROR_CODE.VAULT_ID_NOT_IN_CONFIG_LIST, ['nope']); + }); + + it('getVaultConfig: empty id and unknown id each throw their own error', () => { + const sky = newSkyflow(); + expect(sky.getVaultConfig('vault123').vaultId).toBe('vault123'); + expectCode(() => sky.getVaultConfig(''), SKYFLOW_ERROR_CODE.EMPTY_VAULT_ID_VALIDATION); + expectCode(() => sky.getVaultConfig('nope'), SKYFLOW_ERROR_CODE.VAULT_ID_NOT_IN_CONFIG_LIST, ['nope']); + }); + + it('removeVaultConfig removes a vault; a later access and removing an unknown id throw', () => { + // Two vaults so the registry stays non-empty after one removal (otherwise + // resolveVaultEntry hits the empty-registry guard first). + const sky = newSkyflow(['vault123', 'vault456']); + sky.removeVaultConfig('vault123'); + expect(sky.vault('vault456')).toBeInstanceOf(VaultController); // the other remains + expectCode(() => sky.vault('vault123'), SKYFLOW_ERROR_CODE.VAULT_ID_NOT_IN_CONFIG_LIST, ['vault123']); + expectCode(() => sky.removeVaultConfig('vault123'), SKYFLOW_ERROR_CODE.VAULT_ID_NOT_IN_CONFIG_LIST, ['vault123']); + }); + + it('emptying the registry via removeVaultConfig makes vault() throw EMPTY_VAULT_CLIENTS', () => { + const sky = newSkyflow(); + sky.removeVaultConfig('vault123'); + expectCode(() => sky.vault('vault123'), SKYFLOW_ERROR_CODE.EMPTY_VAULT_CLIENTS); + }); +}); + +describe('Skyflow facade — log level and credentials', () => { + it('setLogLevel / updateLogLevel propagate to the vault clients; updateLogLevel returns the facade', () => { + const sky = newSkyflow(['vault123', 'vault456']); + sky.setLogLevel(LogLevel.WARN); + expect(sky.getLogLevel()).toBe(LogLevel.WARN); + expect(clientOf(sky, 'vault123').getLogLevel()).toBe(LogLevel.WARN); + + const returned = sky.updateLogLevel(LogLevel.DEBUG); + expect(returned).toBe(sky); + expect(sky.getLogLevel()).toBe(LogLevel.DEBUG); + expect(clientOf(sky, 'vault456').getLogLevel()).toBe(LogLevel.DEBUG); + }); + + it('setLogLevel rejects an invalid log level', () => { + const sky = newSkyflow(); + expectCode(() => sky.setLogLevel('NOPE' as never), SKYFLOW_ERROR_CODE.INVALID_LOG_LEVEL); + }); + + it('updateSkyflowCredentials: null throws EMPTY_CREDENTIALS; valid creds propagate and are returned', () => { + const sky = newSkyflow(); + expectCode(() => sky.updateSkyflowCredentials(null as never), SKYFLOW_ERROR_CODE.EMPTY_CREDENTIALS); + + sky.updateSkyflowCredentials({ apiKey: 'sky-new-common' } as never); + expect(sky.getSkyflowCredentials()).toEqual({ apiKey: 'sky-new-common' }); + expect(clientOf(sky, 'vault123').skyflowCredentials).toEqual({ apiKey: 'sky-new-common' }); + }); +}); + +describe('Skyflow facade — SDK identity', () => { + it('stamps the FlowVault SDK identity on import', () => { + expect(getSdkIdentity()).toEqual({ name: 'skyflow-flowvault-node', version: '1.0.0' }); + }); +}); diff --git a/flowvault/test/smoke.test.ts b/flowvault/test/smoke.test.ts new file mode 100644 index 00000000..fa3a57f6 --- /dev/null +++ b/flowvault/test/smoke.test.ts @@ -0,0 +1,232 @@ +import { + Skyflow, + InsertRequest, + InsertOptions, + InsertResponse, + GetRequest, + GetOptions, + UpdateRequest, + DeleteRequest, + QueryRequest, + QueryResponse, + DetokenizeRequest, + DetokenizeResponse, + GetTokensRequest, + UploadFilesRequest, + UploadFilesResponse, + DeleteFilesRequest, + Env, + LogLevel, +} from '../src'; +import VaultController from '../src/controller/vault'; + +// A rawResponse stub whose headers carry an x-request-id. +const rawResponse = { headers: new Map([['x-request-id', 'req-abc-123']]) }; + +// Builds a fake VaultClient exposing only what the controller touches. +function makeFakeClient(overrides: Record) { + return { + vaultId: 'vault123', + getLogLevel: () => LogLevel.ERROR, + getToken: jest.fn().mockResolvedValue(undefined), + failureResponse: (e: unknown) => (e instanceof Error ? e : new Error(String(e))), + ...overrides, + }; +} + +function resourceMethod(data: unknown) { + return jest.fn().mockReturnValue({ + withRawResponse: () => Promise.resolve({ data, rawResponse }), + }); +} + +describe('FlowVault model classes', () => { + it('construct request classes with their fields', () => { + const insertReq = new InsertRequest('cards', [{ data: { card_number: '4111' } }]); + expect(insertReq.tableName).toBe('cards'); + expect(insertReq.records).toHaveLength(1); + + const getReq = new GetRequest('cards', ['id1', 'id2']); + expect(getReq.table).toBe('cards'); + expect(getReq.ids).toEqual(['id1', 'id2']); + + const updateReq = new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 1 } }]); + expect(updateReq.records[0].skyflowId).toBe('id1'); + + const deleteReq = new DeleteRequest('cards', ['id1']); + expect(deleteReq.ids).toEqual(['id1']); + + const queryReq = new QueryRequest('SELECT * FROM cards'); + expect(queryReq.query).toContain('SELECT'); + + const detokenizeReq = new DetokenizeRequest(['tok1', 'tok2']); + expect(detokenizeReq.tokens).toHaveLength(2); + + const getTokensReq = new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }]); + expect(getTokensReq.records[0].tokenGroupName).toBe('cards'); + + const uploadReq = new UploadFilesRequest([ + { tableName: 'onboarding', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }, + ]); + expect(uploadReq.records[0].tableName).toBe('onboarding'); + + const deleteFilesReq = new DeleteFilesRequest([ + { tableName: 'onboarding', columns: ['resume'], skyflowId: 'id1' }, + ]); + expect(deleteFilesReq.records[0].columns).toEqual(['resume']); + }); + + it('construct options classes via setters/getters', () => { + const insertOptions = new InsertOptions(); + insertOptions.setUpsert({ updateType: 'UPDATE', uniqueColumns: ['email'] }); + expect(insertOptions.getUpsert()).toEqual({ updateType: 'UPDATE', uniqueColumns: ['email'] }); + + const getOptions = new GetOptions(); + getOptions.setFields(['name']); + getOptions.setLimit(10); + getOptions.setOffset(2); + expect(getOptions.getFields()).toEqual(['name']); + expect(getOptions.getLimit()).toBe(10); + expect(getOptions.getOffset()).toBe(2); + }); +}); + +describe('Skyflow facade', () => { + it('builds a client from a dummy config and returns a VaultController', () => { + const client = new Skyflow({ + vaultConfigs: [ + { + vaultId: 'vault123', + clusterId: 'abc-cluster', + env: Env.PROD, + credentials: { apiKey: 'sky-dummy-key' }, + }, + ], + logLevel: LogLevel.ERROR, + }); + expect(client.vault('vault123')).toBeInstanceOf(VaultController); + // No id defaults to the first configured vault. + expect(client.vault()).toBeInstanceOf(VaultController); + expect(client.getVaultConfig('vault123').vaultId).toBe('vault123'); + }); +}); + +describe('VaultController maps generated responses', () => { + it('insert maps records to InsertResponse', async () => { + const insertRecords = resourceMethod({ + records: [{ skyflowID: 'id1', tokens: { card_number: 'tok_1' }, httpCode: 200, tableName: 'cards' }], + }); + const controller = new VaultController( + makeFakeClient({ records: { insertRecords } }) as any, + ); + + const resp = await controller.insert(new InsertRequest('cards', [{ data: { card_number: '4111' } }])); + expect(resp).toBeInstanceOf(InsertResponse); + expect(insertRecords).toHaveBeenCalledTimes(1); + expect(resp.records[0].skyflowId).toBe('id1'); + expect(resp.records[0].fields).toEqual({ card_number: 'tok_1' }); + expect(resp.records[0].tableName).toBe('cards'); + expect(resp.records[0].httpCode).toBe(200); + expect(resp.records[0].error).toBeNull(); + expect(resp.records[0].requestId).toBeUndefined(); + }); + + it('insert attaches requestId when a record has an error', async () => { + const insertRecords = resourceMethod({ + records: [{ httpCode: 400, error: 'bad record' }], + }); + const controller = new VaultController( + makeFakeClient({ records: { insertRecords } }) as any, + ); + const resp = await controller.insert(new InsertRequest('cards', [{ data: { card_number: '4111' } }])); + expect(resp.records[0].error).toBe('bad record'); + expect(resp.records[0].requestId).toBe('req-abc-123'); + }); + + it('detokenize maps the generated `response` array to DetokenizeResponse', async () => { + const detokenize = resourceMethod({ + response: [ + { + token: 'tok1', + value: '4111', + tokenGroupName: 'cards', + httpCode: 200, + metadata: { skyflowID: 'id1', tableName: 'cards' }, + }, + ], + }); + const controller = new VaultController( + makeFakeClient({ tokens: { detokenize } }) as any, + ); + + const resp = await controller.detokenize(new DetokenizeRequest(['tok1'])); + expect(resp).toBeInstanceOf(DetokenizeResponse); + expect(resp.records[0].token).toBe('tok1'); + expect(resp.records[0].value).toBe('4111'); + expect(resp.records[0].tokenGroupName).toBe('cards'); + expect(resp.records[0].metadata).toEqual({ skyflowId: 'id1', tableName: 'cards' }); + expect(resp.records[0].httpCode).toBe(200); + }); + + it('query maps records, metadata and requestId to QueryResponse', async () => { + const executeQuery = resourceMethod({ + records: [{ data: { name: 'alice' } }, { data: { name: 'bob' } }], + metadata: { columns: ['name'] }, + }); + const controller = new VaultController( + makeFakeClient({ query: { executeQuery } }) as any, + ); + + const resp = await controller.query(new QueryRequest('SELECT name FROM cards')); + expect(resp).toBeInstanceOf(QueryResponse); + expect(resp.fields).toEqual([{ name: 'alice' }, { name: 'bob' }]); + expect(resp.metadata.columns).toEqual(['name']); + expect(resp.errors).toBeNull(); + expect(resp.requestId).toBe('req-abc-123'); + }); + + it('uploadFiles performs the two-phase upload and maps UploadFilesResponse', async () => { + const originalFetch = (global as any).fetch; + const fetchMock = jest.fn().mockResolvedValue({ ok: true, status: 200 }); + (global as any).fetch = fetchMock; + try { + const uploadFiles = resourceMethod({ + records: [ + { + skyflowID: 'id1', + tableName: 'onboarding', + data: { resume: 'https://signed.example/upload/resume' }, + httpCode: 200, + }, + ], + }); + const controller = new VaultController( + makeFakeClient({ files: { uploadFiles } }) as any, + ); + + const req = new UploadFilesRequest([ + { + tableName: 'onboarding', + columns: [{ column: 'resume', base64: Buffer.from('hello').toString('base64'), fileName: 'resume.pdf' }], + }, + ]); + const resp = await controller.uploadFiles(req); + expect(resp).toBeInstanceOf(UploadFilesResponse); + expect(uploadFiles).toHaveBeenCalledTimes(1); + // Phase B: a signed-URL PUT with no Authorization header. + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0]; + expect(url).toBe('https://signed.example/upload/resume'); + expect(init.method).toBe('PUT'); + expect(init.headers.Authorization).toBeUndefined(); + expect(init.headers['Content-Type']).toBe('application/pdf'); + + expect(resp.records[0].skyflowId).toBe('id1'); + expect(resp.records[0].columns[0].column).toBe('resume'); + expect(resp.records[0].columns[0].uploadStatus).toBe('UPLOADED'); + expect(resp.records[0].columns[0].fileName).toBe('resume.pdf'); + } finally { + (global as any).fetch = originalFetch; + } + }); +}); diff --git a/flowvault/test/validations/validations.test.ts b/flowvault/test/validations/validations.test.ts new file mode 100644 index 00000000..abaa44c3 --- /dev/null +++ b/flowvault/test/validations/validations.test.ts @@ -0,0 +1,253 @@ +import { + SkyflowError, + SKYFLOW_ERROR_CODE, + InsertRequest, + GetRequest, + UpdateRequest, + DeleteRequest, + QueryRequest, + DetokenizeRequest, + GetTokensRequest, + UploadFilesRequest, + DeleteFilesRequest, +} from '../../src'; +import { + validateInsertRequest, + validateGetRequest, + validateUpdateRequest, + validateDeleteRequest, + validateQueryRequest, + validateDetokenizeRequest, + validateGetTokensRequest, + validateUploadFilesRequest, + validateDeleteFilesRequest, +} from '../../src/validations'; +import { silenceConsole } from '../helpers'; + +silenceConsole(); + +type Code = { httpCode: number; message: string }; + +/** Assert `fn` throws a SkyflowError carrying the given error code. */ +function expectCode(fn: () => void, code: Code): void { + let thrown: unknown; + try { + fn(); + } catch (e) { + thrown = e; + } + expect(thrown).toBeInstanceOf(SkyflowError); + const err = thrown as SkyflowError; + expect(err.error?.message).toBe(code.message); + expect(err.error?.httpCode).toBe(code.httpCode); +} + +describe('validateInsertRequest', () => { + it('throws on invalid request / empty table / empty records / empty data', () => { + expectCode(() => validateInsertRequest({} as unknown as InsertRequest), SKYFLOW_ERROR_CODE.INVALID_INSERT_REQUEST); + expectCode(() => validateInsertRequest(null as unknown as InsertRequest), SKYFLOW_ERROR_CODE.INVALID_INSERT_REQUEST); + expectCode(() => validateInsertRequest(new InsertRequest('', [{ data: { a: 1 } }])), SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + expectCode(() => validateInsertRequest(new InsertRequest('t', [])), SKYFLOW_ERROR_CODE.EMPTY_RECORD_IN_INSERT); + expectCode( + () => validateInsertRequest(new InsertRequest('t', [{ data: null as unknown as Record }])), + SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_INSERT, + ); + }); + + it('does not throw on valid input', () => { + expect(() => validateInsertRequest(new InsertRequest('t', [{ data: { a: 1 } }]))).not.toThrow(); + }); +}); + +describe('validateGetRequest', () => { + it('throws on invalid request / empty table / empty ids (single-table)', () => { + expectCode(() => validateGetRequest({} as unknown as GetRequest), SKYFLOW_ERROR_CODE.INVALID_GET_REQUEST); + expectCode(() => validateGetRequest(new GetRequest('', ['id'])), SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + expectCode(() => validateGetRequest(new GetRequest('t', [])), SKYFLOW_ERROR_CODE.EMPTY_IDS_IN_GET); + }); + + it('does not throw for valid single-table or multi-table input', () => { + expect(() => validateGetRequest(new GetRequest('t', ['id']))).not.toThrow(); + expect(() => validateGetRequest(new GetRequest('', [], [{ tableName: 't', ids: ['i'] }]))).not.toThrow(); + }); +}); + +describe('validateUpdateRequest', () => { + it('throws on invalid request / empty table / empty records / missing skyflowId / empty data', () => { + expectCode(() => validateUpdateRequest({} as unknown as UpdateRequest), SKYFLOW_ERROR_CODE.INVALID_UPDATE_REQUEST); + expectCode( + () => validateUpdateRequest(new UpdateRequest('', [{ skyflowId: 'i', data: { a: 1 } }])), + SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME, + ); + expectCode(() => validateUpdateRequest(new UpdateRequest('t', [])), SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_UPDATE); + expectCode( + () => validateUpdateRequest(new UpdateRequest('t', [{ skyflowId: '', data: { a: 1 } }])), + SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPDATE, + ); + expectCode( + () => validateUpdateRequest(new UpdateRequest('t', [{ skyflowId: 'i', data: null as unknown as Record }])), + SKYFLOW_ERROR_CODE.EMPTY_DATA_IN_UPDATE, + ); + }); + + it('does not throw on valid input', () => { + expect(() => validateUpdateRequest(new UpdateRequest('t', [{ skyflowId: 'i', data: { a: 1 } }]))).not.toThrow(); + }); + + it('treats a null record entry as a missing skyflowId', () => { + expectCode( + () => validateUpdateRequest(new UpdateRequest('t', [null as unknown as { skyflowId: string; data: Record }])), + SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPDATE, + ); + }); +}); + +describe('validateDeleteRequest', () => { + it('throws on invalid request / empty table / no ids nor uniqueValues', () => { + expectCode(() => validateDeleteRequest({} as unknown as DeleteRequest), SKYFLOW_ERROR_CODE.INVALID_DELETE_REQUEST); + expectCode(() => validateDeleteRequest(new DeleteRequest('', ['id'])), SKYFLOW_ERROR_CODE.EMPTY_TABLE_NAME); + expectCode(() => validateDeleteRequest(new DeleteRequest('t', [])), SKYFLOW_ERROR_CODE.EMPTY_DELETE_IDS); + }); + + it('does not throw with ids or with uniqueValues', () => { + expect(() => validateDeleteRequest(new DeleteRequest('t', ['id']))).not.toThrow(); + expect(() => validateDeleteRequest(new DeleteRequest('t', [], [{ email: 'a@b.com' }]))).not.toThrow(); + }); +}); + +describe('validateQueryRequest', () => { + it('throws on invalid request / empty query', () => { + expectCode(() => validateQueryRequest({} as unknown as QueryRequest), SKYFLOW_ERROR_CODE.INVALID_QUERY_REQUEST); + expectCode(() => validateQueryRequest(new QueryRequest('')), SKYFLOW_ERROR_CODE.EMPTY_QUERY); + }); + + it('does not throw on valid input', () => { + expect(() => validateQueryRequest(new QueryRequest('SELECT 1'))).not.toThrow(); + }); +}); + +describe('validateDetokenizeRequest', () => { + it('throws on invalid request / empty tokens / empty token entry', () => { + expectCode(() => validateDetokenizeRequest({} as unknown as DetokenizeRequest), SKYFLOW_ERROR_CODE.INVALID_DETOKENIZE_REQUEST); + expectCode(() => validateDetokenizeRequest(new DetokenizeRequest([])), SKYFLOW_ERROR_CODE.EMPTY_TOKENS_IN_DETOKENIZE); + expectCode(() => validateDetokenizeRequest(new DetokenizeRequest([''])), SKYFLOW_ERROR_CODE.EMPTY_TOKEN_IN_DETOKENIZE); + }); + + it('does not throw on valid input', () => { + expect(() => validateDetokenizeRequest(new DetokenizeRequest(['tok']))).not.toThrow(); + }); +}); + +describe('validateGetTokensRequest', () => { + it('throws on invalid request / empty records / missing tokenGroupName', () => { + expectCode(() => validateGetTokensRequest({} as unknown as GetTokensRequest), SKYFLOW_ERROR_CODE.INVALID_TOKENIZE_REQUEST); + expectCode(() => validateGetTokensRequest(new GetTokensRequest([])), SKYFLOW_ERROR_CODE.EMPTY_VALUES_IN_TOKENIZE); + expectCode( + () => validateGetTokensRequest(new GetTokensRequest([{ value: 'v', tokenGroupName: '' }])), + SKYFLOW_ERROR_CODE.EMPTY_COLUMN_GROUP_IN_TOKENIZE, + ); + }); + + it('does not throw on valid input', () => { + expect(() => validateGetTokensRequest(new GetTokensRequest([{ value: 'v', tokenGroupName: 'g' }]))).not.toThrow(); + }); + + it('treats a null record entry as a missing tokenGroupName', () => { + expectCode( + () => validateGetTokensRequest(new GetTokensRequest([null as unknown as { value: unknown; tokenGroupName: string }])), + SKYFLOW_ERROR_CODE.EMPTY_COLUMN_GROUP_IN_TOKENIZE, + ); + }); +}); + +describe('validateUploadFilesRequest', () => { + it('throws on invalid request / empty records', () => { + expectCode(() => validateUploadFilesRequest({} as unknown as UploadFilesRequest), SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST); + expectCode(() => validateUploadFilesRequest(new UploadFilesRequest([])), SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST); + }); + + it('throws on missing table / columns / column name', () => { + expectCode( + () => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: '', columns: [{ column: 'c', base64: 'x', fileName: 'f.pdf' }] }])), + SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE, + ); + expectCode( + () => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [] }])), + SKYFLOW_ERROR_CODE.MISSING_COLUMN_NAME_IN_UPLOAD_FILE, + ); + expectCode( + () => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [{ column: '', base64: 'x', fileName: 'f.pdf' }] }])), + SKYFLOW_ERROR_CODE.MISSING_COLUMN_NAME_IN_UPLOAD_FILE, + ); + }); + + it('throws unless exactly one of filePath/base64/file is provided', () => { + expectCode( + () => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'c' }] }])), + SKYFLOW_ERROR_CODE.MISSING_FILE_SOURCE_IN_UPLOAD_FILE, + ); + expectCode( + () => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'c', base64: 'x', filePath: '/p', fileName: 'f.pdf' }] }])), + SKYFLOW_ERROR_CODE.MISSING_FILE_SOURCE_IN_UPLOAD_FILE, + ); + }); + + it('throws when base64 is provided without a fileName', () => { + expectCode( + () => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'c', base64: 'x' }] }])), + SKYFLOW_ERROR_CODE.MISSING_FILE_NAME_FOR_BASE64, + ); + }); + + it('treats a null record entry as a missing table', () => { + expectCode( + () => validateUploadFilesRequest(new UploadFilesRequest([null as unknown as { tableName: string; columns: [] }])), + SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE, + ); + }); + + it('does not throw for valid base64 / filePath / file sources', () => { + const fakeFile = { name: 'f.pdf', arrayBuffer: async () => new ArrayBuffer(0) } as unknown as File; + expect(() => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'c', base64: 'x', fileName: 'f.pdf' }] }]))).not.toThrow(); + expect(() => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'c', filePath: '/p/x.png' }] }]))).not.toThrow(); + expect(() => validateUploadFilesRequest(new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'c', file: fakeFile }] }]))).not.toThrow(); + }); +}); + +describe('validateDeleteFilesRequest', () => { + it('throws on invalid request / empty records / missing table / missing columns', () => { + expectCode(() => validateDeleteFilesRequest({} as unknown as DeleteFilesRequest), SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST); + expectCode(() => validateDeleteFilesRequest(new DeleteFilesRequest([])), SKYFLOW_ERROR_CODE.INVALID_FILE_UPLOAD_REQUEST); + expectCode( + () => validateDeleteFilesRequest(new DeleteFilesRequest([{ tableName: '', columns: ['c'], skyflowId: 'i' }])), + SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE, + ); + expectCode( + () => validateDeleteFilesRequest(new DeleteFilesRequest([{ tableName: 't', columns: [], skyflowId: 'i' }])), + SKYFLOW_ERROR_CODE.MISSING_COLUMN_NAME_IN_UPLOAD_FILE, + ); + }); + + it('requires exactly one of skyflowId / uniqueValues', () => { + expectCode( + () => validateDeleteFilesRequest(new DeleteFilesRequest([{ tableName: 't', columns: ['c'] }])), + SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPLOAD_FILE, + ); + expectCode( + () => validateDeleteFilesRequest(new DeleteFilesRequest([{ tableName: 't', columns: ['c'], skyflowId: 'i', uniqueValues: [{ a: 1 }] }])), + SKYFLOW_ERROR_CODE.MISSING_SKYFLOW_ID_IN_UPLOAD_FILE, + ); + }); + + it('treats a null record entry as a missing table', () => { + expectCode( + () => validateDeleteFilesRequest(new DeleteFilesRequest([null as unknown as { tableName: string; columns: string[] }])), + SKYFLOW_ERROR_CODE.MISSING_TABLE_IN_UPLOAD_FILE, + ); + }); + + it('does not throw with skyflowId only or uniqueValues only', () => { + expect(() => validateDeleteFilesRequest(new DeleteFilesRequest([{ tableName: 't', columns: ['c'], skyflowId: 'i' }]))).not.toThrow(); + expect(() => validateDeleteFilesRequest(new DeleteFilesRequest([{ tableName: 't', columns: ['c'], uniqueValues: [{ a: 1 }] }]))).not.toThrow(); + }); +}); diff --git a/flowvault/tsconfig.dts.json b/flowvault/tsconfig.dts.json new file mode 100644 index 00000000..1588e812 --- /dev/null +++ b/flowvault/tsconfig.dts.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "es2019", + "module": "commonjs", + "moduleResolution": "node", + "lib": ["es2015", "es2017", "es2019", "dom"], + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "allowJs": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": false, + "noImplicitAny": false, + "declaration": true, + "emitDeclarationOnly": true, + "composite": false, + "incremental": false, + "stripInternal": true, + "types": ["node"], + "rootDir": "." + }, + "include": ["src/**/*.ts", "_generated_/**/*.ts"], + "exclude": [ + "node_modules", + "lib", + "dist", + "test", + "_generated_/rest/src/Client.ts", + "_generated_/rest/src/index.ts" + ] +} diff --git a/flowvault/tsconfig.json b/flowvault/tsconfig.json new file mode 100644 index 00000000..02e52479 --- /dev/null +++ b/flowvault/tsconfig.json @@ -0,0 +1,36 @@ +{ + "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": ".", + "outDir": "lib", + "types": ["node"] + }, + "references": [{ "path": "../common" }], + "include": ["src/**/*.ts", "_generated_/**/*.ts"], + "exclude": [ + "node_modules", + "lib", + "test", + "_generated_/rest/src/Client.ts", + "_generated_/rest/src/index.ts" + ] +} diff --git a/flowvault/tsconfig.test.json b/flowvault/tsconfig.test.json new file mode 100644 index 00000000..8db19576 --- /dev/null +++ b/flowvault/tsconfig.test.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": false, + "declaration": false, + "declarationMap": false, + "incremental": false, + "noEmit": true, + "isolatedModules": true, + "rootDir": "." + }, + "include": ["src/**/*.ts", "test/**/*.ts", "_generated_/**/*.ts"], + "exclude": [ + "node_modules", + "lib", + "_generated_/rest/src/Client.ts", + "_generated_/rest/src/index.ts" + ] +} diff --git a/flowvault/tsup.config.ts b/flowvault/tsup.config.ts new file mode 100644 index 00000000..067ef014 --- /dev/null +++ b/flowvault/tsup.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'tsup'; + +// Bundles the published entry, inlining the private `skyflow-common` workspace +// package so the tarball is self-contained (no skyflow-common dependency ships). +// Real npm dependencies stay external (declared in package.json "dependencies"). +export default defineConfig({ + entry: { index: 'src/index.ts' }, + format: ['cjs'], + outDir: 'dist', + target: 'es2019', + platform: 'node', + noExternal: ['skyflow-common'], + tsconfig: 'tsconfig.dts.json', + dts: true, + sourcemap: true, + clean: true, + splitting: false, + shims: false, +}); diff --git a/package-lock.json b/package-lock.json index 5a83cb00..659f4d7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,26 +1,18 @@ { - "name": "skyflow-node", - "version": "2.1.1-dev.09ed991", + "name": "skyflow-node-sdk", + "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "skyflow-node", - "version": "2.1.1-dev.09ed991", + "name": "skyflow-node-sdk", + "version": "0.0.0", "license": "MIT", - "dependencies": { - "dotenv": "16.6.1", - "form-data": "4.0.6", - "form-data-encoder": "4.1.0", - "formdata-node": "6.0.3", - "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" - }, + "workspaces": [ + "common", + "skyvault", + "flowvault" + ], "devDependencies": { "@babel/plugin-proposal-decorators": "7.29.0", "@babel/plugin-transform-object-assign": "7.27.1", @@ -41,10 +33,45 @@ "jest": "29.7.0", "prettier": "3.6.2", "ts-jest": "29.4.11", + "tsup": "^8.5.1", "typescript": "5.7.3", "typescript-eslint": "8.59.3" } }, + "common": { + "name": "skyflow-common", + "version": "0.0.0", + "license": "MIT", + "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" + } + }, + "flowvault": { + "name": "skyflow-flowvault-node", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "dotenv": "16.6.1", + "form-data": "4.0.6", + "form-data-encoder": "4.1.0", + "formdata-node": "6.0.3", + "js-base64": "3.7.7", + "node-fetch": "2.7.0", + "qs": "6.15.2", + "readable-stream": "4.7.0", + "url-join": "4.0.1" + }, + "devDependencies": { + "skyflow-common": "*" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -2571,6 +2598,448 @@ "node": ">=20" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -3633,6 +4102,373 @@ "resolve": "~1.22.2" } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.6.tgz", + "integrity": "sha512-FkO19Fowv/OQeJZPVfdetJaI3/VpbZmBsnmAlR/rQ+b3QwfiGQiOpVegpSRH331kelvHTVw9hXYG3DwjAaRzyQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.6.tgz", + "integrity": "sha512-si5/Etm6MphPc2xYHnk455Shls/hoMjTy3mhXge4OPmvCw7TwC4KP4fKokAlKLV4XaQvLvgSHfafPcblLMYYdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.6.tgz", + "integrity": "sha512-PQCl3q7b0BgvVt72i2xrWM6I5KliwwVe4GpWW5QDGyUQawDS9aPZss/aGyFqBmxdRxlE8wDc9CaxiQ5DHRSp/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.6.tgz", + "integrity": "sha512-l9DFl1Itb1YYdSrP1K3U8ih44AOHa/p+Q4WMMDfGvH5qVWp+ajfgS1DlYo4yCRh8wJ2ZJv0Fqccf+ULAmMFjmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.6.tgz", + "integrity": "sha512-bR4GR3205N9+od0+l5Dd9/iZTDpJkIe6d6Ydp2qbcQZJLmKBdYQQRp8gbWpbMt92twKJY66o1vK4NkoKQODIrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.6.tgz", + "integrity": "sha512-XTVNaDzcFl2TeBglvxdGgsCv9JGtAGRAoJENmGBR/XvDH2mA2K0IXc2aMfP0dR8mexSXSKifJ85FixoXX1wfTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.6.tgz", + "integrity": "sha512-qWc6qbc+25ugG9vqPR9P422KJBbVfZ05mjcMggoq8tsfWP+eRfC+/tiDdIlRF5VL2pDVZWN9zAhWsmu7R1+7Dw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.6.tgz", + "integrity": "sha512-V4Nwkp8844X/Xnww3PI1LYS0YPei8XQEVG70R+5MPZGO7AhYoun4hCN7Svs4U3wbQLZbwiz+9xRFhUZZdRl8Ww==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.6.tgz", + "integrity": "sha512-xb72A1aPaaIsp+a88VtRae/ovIEXislbUMyp7b9AQQU7ZkP2m/+ik6DW9TQcJyw5R8/RIailF9zExYwkjR++UA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.6.tgz", + "integrity": "sha512-pbcs1BVBwC43hZkjhjlF8Rx8m5G0KnhLZs9Azy/gqccP6UsNSzJr0hKGIAVPqzAH1O/NdPxxC9GVGb4msQg/aw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.6.tgz", + "integrity": "sha512-EKOC8os6Vsg487ZLKUqzLF99Jsmnq6tu7O7mAELakNpcA+8KadktFf/JYdRuHe/DzCUXmN38pUKu4XBcKlqGbQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.6.tgz", + "integrity": "sha512-v/SQ68GnSlr/HyjB79sNXesGDwkOPu9JjbHfmWaymCiM+nZsQ9TstG1KfA4MuEvTr2d3PoY8PhFxj9bt0e32JA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.6.tgz", + "integrity": "sha512-G3pmliMQ7r15zFFNwBlaT/xTdSLi4lfgI7CUrQueyKYheNVhrd8JrWCE6Ll8xf4AA1RZFqq+mNJYntiElsVOyA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.6.tgz", + "integrity": "sha512-XQTB/IPoB/wG6x/wu9uomAt8YH61IAr5iZTubMiad/lu88sotq2ocz9hJd556+Jz2kIkF7ZQZIsUuWE3OWqtcA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.6.tgz", + "integrity": "sha512-O5mpokth6+ydMuwiGtY8fIjvOBfH7u6SzmDNmZ3k6GK5C6iEAC2q2ZN/T5L9jYILx0IcCT0KnfMDihQ5yQFSBw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.6.tgz", + "integrity": "sha512-aIFxbAD9v3TvIaEDsKmukjcDg1JS4/tFXw8SHyQQgzOwzWCQ4cyDK7Ju1pQKqUXDFkEu/BZCFDCDATsKhQcB/Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.6.tgz", + "integrity": "sha512-fuNo1UwBc6BxnHDOxxguQL/InhheMN3RJcJMb0b4xP8r5Q3CNCrJbVg0eCIFuJ4e/zAfjR2gYbOfbzO/G7CIyw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.6.tgz", + "integrity": "sha512-05v6wFKDKJPZs9xlytVrrRu6IbRuoSvlH9xNwEOafXsKyjBFZPvAcZz2VIAB/M+lblYeKa9iOYC4RDVdfcAAaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.6.tgz", + "integrity": "sha512-wJXKcdMjvkc/FaavZXNP1yW4i0pfOmhZAIWRSLG2yJ0PB0TWi7dkLErhX0u0vptDcXKKO/m1Y/YTeuyvb2MXwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.6.tgz", + "integrity": "sha512-cwg7/h0ThvJwy8CBkmACdiHAxRWpbsCmIsjOt+Zzx0hTnIcSUmKYEV03bGg+hD827OnwUsbKh1ZZDW754xWyBA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.6.tgz", + "integrity": "sha512-T0RkYIJbdYXJEQFw+tMBO09KDFdgmNxkHsof3wWAABrvnx15Yh8RytmrgYy9+q9cBeBNZF66MMQZC+5nzEz6vg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.6.tgz", + "integrity": "sha512-KlfpmF1AAIdAWxt8QQNClbpLFlAUfYOzqIuJ7IXEStMHpFRuH78RG6FjqPb3Rqhh0K89F5npvG+/i3Fjl6IveQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.6.tgz", + "integrity": "sha512-MrTLjd7Jlt9/ZavJZYG61KQv9Iy9bzbY+yXy73Xvwoa2uP66wvDShkyBr5Chz/yrE6lbD/zHMuTmKQVNQqBApg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.6.tgz", + "integrity": "sha512-8fWDH1CkPI8GdBKAxzHDza7UmgficsKo2+MVbvUgOGhB0629w5sHMg3Qu5y7TZsrYKHHWpz8fla4KvoT7WsO3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.6.tgz", + "integrity": "sha512-9/c9PWdr5GCIHkkxiHM1HjNBQMVdftP4O8QTcoUAaRza2FSqJ4axbf8QAnBuE6BIGQDl71SaRPeDdDYid5ecTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rushstack/node-core-library": { "version": "5.23.1", "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.23.1.tgz", @@ -4341,6 +5177,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4758,6 +5601,32 @@ "dev": true, "license": "MIT" }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -4867,6 +5736,22 @@ "node": ">=10" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -5003,6 +5888,23 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5527,6 +6429,48 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -6006,6 +6950,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -7986,6 +8942,16 @@ "dev": true, "license": "MIT" }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/js-base64": { "version": "3.7.7", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", @@ -8204,6 +9170,19 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -8211,6 +9190,16 @@ "dev": true, "license": "MIT" }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8300,6 +9289,16 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8446,12 +9445,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -8545,6 +9569,16 @@ "node": ">=8" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -8712,6 +9746,13 @@ "dev": true, "license": "MIT" }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -8811,6 +9852,61 @@ "node": ">=8" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8940,6 +10036,20 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -9073,6 +10183,52 @@ "node": ">=10" } }, + "node_modules/rollup": { + "version": "4.64.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.6.tgz", + "integrity": "sha512-fX1Vy+UNKdXn+xKDnu/HBXbHVr1AY8cSdQj9BU4kLv5aFCMqGpvrVbyUMmjN3DdzWhSAuTbcX8PCNEVaumthYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.64.6", + "@rollup/rollup-android-arm64": "4.64.6", + "@rollup/rollup-darwin-arm64": "4.64.6", + "@rollup/rollup-darwin-x64": "4.64.6", + "@rollup/rollup-freebsd-arm64": "4.64.6", + "@rollup/rollup-freebsd-x64": "4.64.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.64.6", + "@rollup/rollup-linux-arm-musleabihf": "4.64.6", + "@rollup/rollup-linux-arm64-gnu": "4.64.6", + "@rollup/rollup-linux-arm64-musl": "4.64.6", + "@rollup/rollup-linux-loong64-gnu": "4.64.6", + "@rollup/rollup-linux-loong64-musl": "4.64.6", + "@rollup/rollup-linux-ppc64-gnu": "4.64.6", + "@rollup/rollup-linux-ppc64-musl": "4.64.6", + "@rollup/rollup-linux-riscv64-gnu": "4.64.6", + "@rollup/rollup-linux-riscv64-musl": "4.64.6", + "@rollup/rollup-linux-s390x-gnu": "4.64.6", + "@rollup/rollup-linux-x64-gnu": "4.64.6", + "@rollup/rollup-linux-x64-musl": "4.64.6", + "@rollup/rollup-openbsd-x64": "4.64.6", + "@rollup/rollup-openharmony-arm64": "4.64.6", + "@rollup/rollup-win32-arm64-msvc": "4.64.6", + "@rollup/rollup-win32-ia32-msvc": "4.64.6", + "@rollup/rollup-win32-x64-gnu": "4.64.6", + "@rollup/rollup-win32-x64-msvc": "4.64.6", + "fsevents": "~2.3.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9212,6 +10368,18 @@ "dev": true, "license": "MIT" }, + "node_modules/skyflow-common": { + "resolved": "common", + "link": true + }, + "node_modules/skyflow-flowvault-node": { + "resolved": "flowvault", + "link": true + }, + "node_modules/skyflow-node": { + "resolved": "skyvault", + "link": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -9390,6 +10558,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9465,6 +10666,36 @@ "node": "*" } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -9502,6 +10733,16 @@ "node": ">=8.0" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -9515,6 +10756,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/ts-jest": { "version": "29.4.11", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", @@ -9594,6 +10842,69 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9668,6 +10979,13 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -10004,6 +11322,27 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "skyvault": { + "name": "skyflow-node", + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "dotenv": "16.6.1", + "form-data": "4.0.6", + "form-data-encoder": "4.1.0", + "formdata-node": "6.0.3", + "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" + }, + "devDependencies": { + "skyflow-common": "*" + } } } } diff --git a/package.json b/package.json index 662f66e4..be6621ae 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,25 @@ { - "name": "skyflow-node", - "version": "2.1.2", - "description": "Skyflow SDK for Node.js", - "main": "./lib/index.js", - "module": "./lib/index.js", + "name": "skyflow-node-sdk", + "version": "0.0.0", + "private": true, + "description": "Skyflow Node SDK monorepo (common / skyvault / flowvault)", + "workspaces": [ + "common", + "skyvault", + "flowvault" + ], "scripts": { - "test": "jest --coverage", - "contract-snapshot": "npm run build && npx api-extractor run --local", - "contract-snapshot-verify": "npm run build && npx api-extractor run", - "build": "tsc", + "build": "tsc -b", + "build:clean": "tsc -b --clean", + "build:dist": "npm run build:dist --workspace skyvault && npm run build:dist --workspace flowvault", + "contract-snapshot": "npm run build && npm run build:dist && npm run contract-snapshot --workspace skyvault && npm run contract-snapshot --workspace flowvault", + "contract-snapshot-verify": "npm run build && npm run build:dist && npm run contract-snapshot-verify --workspace skyvault && npm run contract-snapshot-verify --workspace flowvault", + "test": "npm run test --workspaces --if-present", "eslint": "eslint '**/*.{js,ts}' --max-warnings 0", "prettier": "prettier --list-different '**/*.{js,ts}'", "lint": "npm run eslint", "lint-fix": "prettier --write '**/*.{js,ts}' && eslint --fix '**/*.{js,ts}'", "spellcheck": "cspell '**/*.{ts,js,md}'", - "docs-gen": "typedoc && node scripts/docs-script/markdown-gen.js && npx ts-node scripts/docs-script/processMarkdown.ts", "patch-generated-secrets": "node scripts/patch-generated-secrets.js", "prepare": "git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git config core.hooksPath .githooks || true" }, @@ -22,9 +27,6 @@ "type": "git", "url": "https://github.com/skyflowapi/skyflow-node-sdk.git" }, - "browser": { - "child_process": false - }, "author": "", "license": "MIT", "keywords": [ @@ -36,19 +38,6 @@ "PII", "tokenization" ], - "dependencies": { - "dotenv": "16.6.1", - "form-data": "4.0.6", - "form-data-encoder": "4.1.0", - "formdata-node": "6.0.3", - "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" - }, "devDependencies": { "@babel/plugin-proposal-decorators": "7.29.0", "@babel/plugin-transform-object-assign": "7.27.1", @@ -69,6 +58,7 @@ "jest": "29.7.0", "prettier": "3.6.2", "ts-jest": "29.4.11", + "tsup": "^8.5.1", "typescript": "5.7.3", "typescript-eslint": "8.59.3" } diff --git a/scripts/bump_version.sh b/scripts/bump_version.sh index 9fe7e66b..fcfc0b13 100755 --- a/scripts/bump_version.sh +++ b/scripts/bump_version.sh @@ -1,19 +1,63 @@ -Version=$1 -SEMVER=$Version +#!/usr/bin/env bash +# +# Bump the version of one published SDK in the monorepo. +# +# Usage: +# bump_version.sh [sha] +# +# skyflow-node -> bumps skyvault/package.json +# skyflow-flowvault-node -> bumps flowvault/package.json +# semver, e.g. 2.1.3 or 1.0.0 +# [sha] optional short commit SHA; when present the version becomes +# "-dev." (internal/JFrog pre-release builds) +# +set -euo pipefail -if [ -z $2 ] -then - echo "Bumping package version to $1" +PRODUCT=$1 +SEMVER=$2 +SHA=${3:-} - sed -E "s/\"version\": .+/\"version\": \"$SEMVER\",/g" package.json > tempfile && cat tempfile > package.json && rm -f tempfile - echo -------------------------- - echo "Done, Package now at $1" +case "$PRODUCT" in + skyflow-node) + PKG_DIR="skyvault" + ;; + skyflow-flowvault-node) + PKG_DIR="flowvault" + ;; + *) + echo "Unknown product '$PRODUCT'. Expected 'skyflow-node' or 'skyflow-flowvault-node'." >&2 + exit 1 + ;; +esac +PKG_JSON="$PKG_DIR/package.json" + +if [ ! -f "$PKG_JSON" ]; then + echo "Cannot find $PKG_JSON" >&2 + exit 1 +fi + +if [ -z "$SHA" ]; then + TARGET="$SEMVER" else - echo "Bumping package version to $1-dev.$2" + TARGET="$SEMVER-dev.$SHA" +fi + +echo "Bumping $PRODUCT ($PKG_JSON) to $TARGET" + +# Replace only the first "version" line (the package's own version field). +sed -E "0,/\"version\": .+/s//\"version\": \"$TARGET\",/" "$PKG_JSON" > "$PKG_JSON.tmp" \ + && cat "$PKG_JSON.tmp" > "$PKG_JSON" \ + && rm -f "$PKG_JSON.tmp" - sed -E "s/\"version\": .+/\"version\": \"$SEMVER-dev.$2\",/g" package.json > tempfile && cat tempfile > package.json && rm -f tempfile +# Keep the in-source SDK version constant in sync (self-reported identity). +VERSION_TS="$PKG_DIR/src/version.ts" +if [ -f "$VERSION_TS" ]; then + sed -E "s/export const SDK_VERSION = \".+\";/export const SDK_VERSION = \"$TARGET\";/" "$VERSION_TS" > "$VERSION_TS.tmp" \ + && cat "$VERSION_TS.tmp" > "$VERSION_TS" \ + && rm -f "$VERSION_TS.tmp" + echo "Updated $VERSION_TS to $TARGET" +fi - echo -------------------------- - echo "Done, Package now at $1-dev.$2" -fi \ No newline at end of file +echo -------------------------- +echo "Done, $PRODUCT now at $TARGET" diff --git a/skyvault/.babelrc b/skyvault/.babelrc new file mode 100644 index 00000000..eb19681f --- /dev/null +++ b/skyvault/.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/skyvault/LICENSE b/skyvault/LICENSE new file mode 100644 index 00000000..189f34b5 --- /dev/null +++ b/skyvault/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Skyflow Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/skyvault/README.md b/skyvault/README.md new file mode 100644 index 00000000..5f765097 --- /dev/null +++ b/skyvault/README.md @@ -0,0 +1,1325 @@ +# Skyflow Node.js SDK + +> **This is the current, recommended version of the Skyflow SDK.** V2.1.0 brings flexible auth, multi-vault support, builder patterns, native data types, and rich error diagnostics. +> +> Migrating from v1? See the **[Migration Guide](docs/migrate_to_v2.md)** for step-by-step instructions. V1 is in maintenance mode and will reach End of Life on October 31, 2026. + +Securely handle sensitive data at rest, in-transit, and in-use with the Skyflow SDK for Node.js, Deno, Bun, and Cloudflare Workers. + +[![CI](https://img.shields.io/static/v1?label=CI&message=passing&color=green?style=plastic&logo=github)](https://github.com/skyflowapi/skyflow-node/actions) +[![GitHub release](https://badge.fury.io/js/skyflow-node.svg)](https://www.npmjs.com/package/skyflow-node) +[![License](https://img.shields.io/github/license/skyflowapi/skyflow-node)](https://github.com/skyflowapi/skyflow-node/blob/main/LICENSE) + +## Table of contents + +- [Skyflow Node.js SDK](#skyflow-nodejs-sdk) + - [Table of contents](#table-of-contents) + - [Overview](#overview) + - [Installation](#installation) + - [Require](#require) + - [ES modules](#es-modules) + - [All imports](#all-imports) + - [Quickstart](#quickstart) + - [Before you begin](#before-you-begin) + - [Authenticate](#authenticate) + - [API Key](#api-key) + - [Bearer Token (static)](#bearer-token-static) + - [Initialize the client](#initialize-the-client) + - [Insert data into the vault, get tokens back](#insert-data-into-the-vault-get-tokens-back) + - [Upgrade from v1 to v2](#upgrade-from-v1-to-v2) + - [Vault](#vault) + - [Insert and tokenize data: `.insert(request)`](#insert-and-tokenize-data-insertrequest) + - [Insert example with `continueOnError` option](#insert-example-with-continueonerror-option) + - [Upsert request](#upsert-request) + - [Detokenize: `.detokenize(request, options)`](#detokenize-detokenizerequest-options) + - [Construct a detokenize request](#construct-a-detokenize-request) + - [Get Record(s): `.get(request, options)`](#get-records-getrequest-options) + - [Construct a get request](#construct-a-get-request) + - [Get by Skyflow IDs](#get-by-skyflow-ids) + - [Get tokens for records](#get-tokens-for-records) + - [Get by column name and column values](#get-by-column-name-and-column-values) + - [Redaction Types](#redaction-types) + - [Update Records](#update-records) + - [Construct an update request](#construct-an-update-request) + - [Delete Records](#delete-records) + - [Query](#query) + - [Upload File](#upload-file) + - [Retrieve Existing Tokens: `.tokenize(request)`](#retrieve-existing-tokens-tokenizerequest) + - [Construct a `.tokenize()` request](#construct-a-tokenize-request) + - [Detect](#detect) + - [De-identify Text: `.deidentifyText(request, options)`](#de-identify-text-deidentifytextrequest-options) + - [Re-identify Text: `.reidentifyText(request, options)`](#re-identify-text-reidentifytextrequest-options) + - [De-identify File: `.deidentifyFile(fileReq, options)`](#de-identify-file-deidentifyfilefilereq-options) + - [Get Run: `.getDetectRun(request)`](#get-run-getdetectrunrequest) + - [Connections](#connections) + - [Configure a connection](#configure-a-connection) + - [Invoke a connection](#invoke-a-connection) + - [Construct an invoke connection request](#construct-an-invoke-connection-request) + - [Authentication \& authorization](#authentication--authorization) + - [Types of `credentials`](#types-of-credentials) + - [Generate bearer tokens for authentication \& authorization](#generate-bearer-tokens-for-authentication--authorization) + - [Generate a bearer token](#generate-a-bearer-token) + - [`generateBearerToken(filepath)`](#generatebearertokenfilepath) + - [`generateBearerTokenFromCreds(credentialsString)`](#generatebearertokenfromcredscredentialsstring) + - [Generate bearer tokens scoped to certain roles](#generate-bearer-tokens-scoped-to-certain-roles) + - [Generate bearer tokens with `ctx` for context-aware authorization](#generate-bearer-tokens-with-ctx-for-context-aware-authorization) + - [Generate signed data tokens](#generate-signed-data-tokens) + - [Client management](#client-management) + - [Using the client in production](#using-the-client-in-production) + - [Logging](#logging) + - [Example `skyflowConfig.logLevel: LogLevel.INFO`](#example-skyflowconfigloglevel-loglevelinfo) + - [Error handling](#error-handling) + - [Catching `SkyflowError` instances](#catching-skyflowerror-instances) + - [Per-record errors (`SkyflowRecordError`)](#per-record-errors-skyflowrecorderror) + - [Bearer token expiration edge cases](#bearer-token-expiration-edge-cases) + - [Troubleshooting](#troubleshooting) + - [TypeScript types reference](#typescript-types-reference) + - [Security](#security) + - [Reporting a Vulnerability](#reporting-a-vulnerability) + +## Overview + +The Skyflow SDK enables you to connect to your Skyflow Vault(s) to securely handle sensitive data at rest, in-transit, and in-use. + +> [!TIP] +> Looking for the full list of request parameters, options setters, response fields, enums, and Detect helper classes? See the **[API Reference](docs/api_reference.md)**. + +> [!IMPORTANT] +> This readme documents SDK version 2. +> For version 1 see the [v1.14.2 README](https://github.com/skyflowapi/skyflow-node/tree/1.14.2). +> For more information on how to migrate see [MIGRATE_TO_V2.md](docs/migrate_to_v2.md). + +## Installation + +Requires Node v12.22.12 and above. + +```sh +npm install skyflow-node +``` + +Depending on your project setup, you may use either the `require` method (common in Node.js projects) or the `import` statement (common in projects using ES modules). + +### Require + +```typescript +const { Skyflow } = require("skyflow-node"); +``` + +### ES modules + +```typescript +import { Skyflow } from "skyflow-node"; +``` + +### All imports + +```typescript +import { + Skyflow, // Vault client + isExpired, // JWT auth helpers + LogLevel, // logging options +} from "skyflow-node"; +``` + +## Quickstart + +Get started quickly with the essential steps: authenticate, initialize the client, and perform a basic vault operation. This section shows you a minimal working example. + +### Before you begin + +You need a Skyflow account and a few values from Skyflow Studio. If you don't have an account, [request a demo](https://www.skyflow.com/get-demo). + +| Value | Where to find it | +|---|---| +| `vaultId` | Your vault's details page in Skyflow Studio. | +| `clusterId` | The first segment of your vault URL: `https://{clusterId}.vault.skyflowapis.com`. | +| `env` | The environment your vault runs in — `Env.PROD`, `Env.SANDBOX`, `Env.DEV`, or `Env.STAGE` (defaults to `PROD`). | +| Credentials | Create a **service account** in Studio. Choose **API key** during creation for the simplest setup, or download the `credentials.json` file for token-based auth. See [Authentication & authorization](#authentication--authorization). | + +The quickstart below assumes a table named `table1` with `card_number` and `cardholder_name` columns. Adjust the table and column names to match your vault schema. See the [Skyflow docs](https://docs.skyflow.com/) for creating vaults, tables, and service accounts. + +### Authenticate + +You can use an API key or a personal bearer token to directly authenticate and authorize requests with the SDK. Use API keys for long-term service authentication. Use bearer tokens for optimal security. + +### API Key + +```typescript +import { Credentials } from 'skyflow-node'; + +const credentials: Credentials = { + apiKey: "", +}; +``` + +### Bearer Token (static) + +```typescript +import { Credentials } from 'skyflow-node'; + +const credentials: Credentials = { + token: "", +}; +``` + +For authenticating via generated bearer tokens including support for scoped tokens, context-aware access tokens, and more, refer to the [Authentication & Authorization](#authentication--authorization) section. + +### Initialize the client + +Initialize the Skyflow client first. You can specify different credential types during initialization. + +```javascript +import { Skyflow, SkyflowConfig, VaultConfig, Env, LogLevel } from 'skyflow-node'; + +// Create a credentials object. We'll use an API key. +const skyflowCredentials = { + apiKey: "" +}; + +// Configure vault +const vaultConfig: VaultConfig = { + vaultId: '', // required: vault identifier + clusterId: '', // required: from vault URL + env: Env.PROD, // optional: PROD (default), SANDBOX, DEV, STAGE + // credentials: vaultCredentials // optional: per-vault credentials; overrides skyflowCredentials +}; + +// Initialize Skyflow client +const skyflowConfig: SkyflowConfig = { + vaultConfigs: [vaultConfig], + skyflowCredentials: skyflowCredentials, + logLevel: LogLevel.ERROR +}; + +const skyflowClient: Skyflow = new Skyflow(skyflowConfig); +``` + +See [docs/advanced_initialization.md](docs/advanced_initialization.md) for advanced initialization examples including multiple vaults and different credential types. + +### Insert data into the vault, get tokens back + +Insert data into your vault using the `insert` method. Set `insertOptions.setReturnTokens(true)` to ensure values are tokenized in the response. + +Create an insert request with the [`InsertRequest`](docs/api_reference.md#insertrequest) class, which includes the values to be inserted as a list of records. + +Below is a simple example to get started. See the [Insert and tokenize data](#insert-and-tokenize-data-insertrequest) section for advanced options. + +```javascript +import { InsertRequest, InsertOptions } from "skyflow-node"; + +// Insert sensitive data into the vault +const insertData = [ + { card_number: "4111111111111112", cardholder_name: "John Doe" }, +]; +const insertReq = new InsertRequest("table1", insertData); + +const insertOptions = new InsertOptions(); +insertOptions.setReturnTokens(true); + +const insertResponse = await skyflowClient + .vault(vaultId) + .insert(insertReq, insertOptions); + +console.log("Insert response:", insertResponse); +``` + +Returns an [`InsertResponse`](docs/api_reference.md#insertresponse) (`insertedFields`, `errors`). With `setReturnTokens(true)`, each entry includes the `skyflowId` and a token per column: + +```text +Insert response: { insertedFields: [{ skyflowId: 'a8f0c2e1-7b3d-4f9a-8c21-1d2e3f4a5b6c', card_number: '5391-4629-3722-7102', cardholder_name: '0f6b8a2c-90ab-4cde-9def-567890abcdef' }], errors: null } +``` + +## Upgrade from v1 to v2 + +Upgrade from `skyflow-node` v1 using the dedicated guide in [docs/migrate_to_v2.md](docs/migrate_to_v2.md). + +## Vault + +The [Vault](https://docs.skyflow.com/docs/vaults) performs operations on the vault such as inserting records, detokenizing tokens, retrieving tokens for list of `skyflowId`s and to invoke the Connection. + +### Insert and tokenize data: `.insert(request)` + +Pass options to the `insert` method to enable additional functionality such as returning tokenized data, upserting records, or allowing bulk operations to continue despite errors. See [Quickstart](#quickstart) for a basic example. + +```typescript +import { InsertRequest, InsertResponse } from 'skyflow-node'; + +const insertRequest = new InsertRequest('table1', [ + { + : '', + : '', + }, + { + : '', + : '', + }, +]); + +const response: InsertResponse = await skyflowClient + .vault('') + .insert(insertRequest); + +console.log('Insert response:', response); +``` + +Returns an [`InsertResponse`](docs/api_reference.md#insertresponse) (`insertedFields`, `errors`). With `setContinueOnError(true)`, `insertedFields` contains successful records and `errors` contains per-record failures: + +```text +Insert response: { insertedFields: [{ skyflowId: 'a8f0c2e1-7b3d-4f9a-8c21-1d2e3f4a5b6c', '': '', '': '' }], errors: null } +``` + +> **Note:** The response key is `skyflowId`. The legacy `skyflow_id` key is deprecated and will be removed in an upcoming release. + +#### Insert example with `continueOnError` option + +Set the `continueOnError` flag to `true` to allow insert operations to proceed despite encountering partial errors. + +> [!TIP] +> See the full example in the samples directory: [insert-continue-on-error.ts](samples/vault-api/insert-continue-on-error.ts) + +#### Upsert request + +Turn an insert into an 'update-or-insert' operation using the upsert option. The vault checks for an existing record with the same value in the specified column. If a match exists, the record updates; otherwise, a new record inserts. + +```typescript +// Specify the column to use as the index for the upsert. +// Note: The column must have the `unique` constraint configured in the vault. +insertOptions.setUpsertColumn("cardholder_name"); +``` + +For all `InsertOptions` setters and `TokenMode` values, see [InsertOptions](docs/api_reference.md#insertoptions) in the API reference. + +### Detokenize: `.detokenize(request, options)` + +Convert tokens back into plaintext values (or masked values) using the `.detokenize()` method. Detokenization accepts tokens and returns values. + +Create a detokenization request with the [`DetokenizeRequest`](docs/api_reference.md#detokenizerequest) class, which requires a list of tokens and column groups as input. + +Provide optional parameters such as the redaction type and the option to continue on error. + +#### Construct a detokenize request + +```typescript +import { + DetokenizeOptions, + DetokenizeRequest, + DetokenizeResponse, +} from "skyflow-node"; + +const detokenizeRequest = new DetokenizeRequest([ + { token: "token1", redactionType: RedactionType.PLAIN_TEXT }, + { token: "token2", redactionType: RedactionType.PLAIN_TEXT }, +]); + +const detokenizeOptions = new DetokenizeOptions(); +detokenizeOptions.setContinueOnError(true); +detokenizeOptions.setDownloadUrl(false); + +const response: DetokenizeResponse = await skyflowClient + .vault(primaryVaultConfig.vaultId) + .detokenize(detokenizeRequest, detokenizeOptions); + +console.log("Detokenization response:", response); +``` + +Returns a [`DetokenizeResponse`](docs/api_reference.md#detokenizeresponse) (`detokenizedFields`, `errors`): + +```text +Detokenization response: { detokenizedFields: [{ token: '5391-4629-3722-7102', value: '4111111111111112', type: 'STRING' }], errors: null } +``` + +> [!TIP] +> See the full example in the samples directory: [detokenzie-records.ts](samples/vault-api/detokenzie-records.ts) + +### Get Record(s): `.get(request, options)` + +Retrieve data using Skyflow IDs or unique column values with the get method. Create a get request with the [`GetRequest`](docs/api_reference.md#getrequest) class, specifying parameters such as the table name, redaction type, Skyflow IDs, column names, and column values. + +> [!NOTE] +> You can't use both Skyflow IDs and column name/value pairs in the same request. Use the `GetOptions` class to specify whether to return tokens. + +#### Construct a get request + +```typescript +import { GetRequest, GetOptions, GetResponse } from "skyflow-node"; + +const getRequest = new GetRequest("table1", ["", ""]); + +const getOptions = new GetOptions(); +getOptions.setReturnTokens(false); + +const response: GetResponse = await skyflowClient + .vault("") + .get(getRequest, getOptions); + +console.log("Get response:", response); +``` + +Returns a [`GetResponse`](docs/api_reference.md#getresponse) (`data`, `errors`): + +```text +Get response: { data: [{ skyflowId: 'a8f0c2e1-7b3d-4f9a-8c21-1d2e3f4a5b6c', card_number: '4111111111111112', cardholder_name: 'John Doe' }], errors: null } +``` + +> **Note:** The response key is `skyflowId`. The legacy `skyflow_id` key is deprecated and will be removed in an upcoming release. + +#### Get by Skyflow IDs + +Retrieve specific records using Skyflow IDs. Use this method when you know the exact record IDs. + +```typescript +import { + GetOptions, + GetRequest, + SkyflowError, + GetResponse, + RedactionType, +} from "skyflow-node"; + +// Initialize a list of Skyflow IDs to retrieve records (replace with actual Skyflow IDs) +const getIds: Array = [ + "a581d205-1969-4350-acbe-a2a13eb871a6", + "5ff887c3-b334-4294-9acc-70e78ae5164a", +]; + +// Step 2: Create a GetRequest to retrieve records by Skyflow ID +const getRequest: GetRequest = new GetRequest( + "table1", // Replace with your actual table name + getIds, +); + +// Step 3: Configure Get Options and specify not to return tokens and redaction type +const getOptions: GetOptions = new GetOptions(); +getOptions.setReturnTokens(false); // Optional: Set to false to avoid returning tokens +getOptions.setRedactionType(RedactionType.PLAIN_TEXT); + +// Step 4: Send the request to the Skyflow vault and retrieve the records +const getResponse: GetResponse = await skyflowClient + .vault(primaryVaultConfig.vaultId) + .get(getRequest, getOptions); +// Replace with your actual Skyflow vault ID + +console.log("Data retrieval successful:", getResponse); +``` + +#### Get tokens for records + +Return tokens for records to securely process sensitive data while maintaining data privacy. + +```ts +getOptions.setReturnTokens(true); // Set to `true` to get tokens +``` + +> [!TIP] +> See the full example in the samples directory: [get-records.ts](samples/vault-api/get-records.ts) + +#### Get by column name and column values + +Retrieve records by unique column values when you don't know the Skyflow IDs. Use this method to query data with alternate unique identifiers. + +```ts +const getRequest: GetColumnRequest = new GetColumnRequest( + tableName, + columnName, + columnValues, // Column values of the records to return +); +``` + +> [!TIP] +> See the full example in the samples directory: [get-column-values.ts](samples/vault-api/get-column-values.ts) + +#### Redaction Types + +Use redaction types to control how sensitive data displays when retrieved from the vault. + +**Available Redaction Types** + +- `DEFAULT`: Applies the vault-configured default redaction setting. +- `REDACTED`: Completely removes sensitive data from view. +- `MASKED`: Partially obscures sensitive information. +- `PLAIN_TEXT`: Displays the full, unmasked data. + +**Choosing the Right Redaction Type** + +- Use `REDACTED` for scenarios requiring maximum data protection to prevent exposure of sensitive information. +- Use `MASKED` to provide partial visibility of sensitive data for less critical use cases. +- Use `PLAIN_TEXT` for internal, authorized access where full data visibility is necessary. + +For all `GetOptions` setters and `OrderByEnum` values, see [GetOptions](docs/api_reference.md#getoptions) in the API reference. + +### Update Records + +Update data in your vault using the `update` method. Create an update request with the [`UpdateRequest`](docs/api_reference.md#updaterequest) class, specifying parameters such as the table name and data (as a dictionary). + +Configure update options using the `UpdateOptions` class to control returnTokens, tokens, and tokenMode. When `returnTokens` is `true`, Skyflow returns tokens for the updated records. When `false`, Skyflow returns IDs for the updated records. + +#### Construct an update request + +```typescript +import { UpdateRequest, UpdateOptions, UpdateResponse, TokenMode } from 'skyflow-node'; + +const updateRequest = new UpdateRequest('table1', { + skyflowId: '', + : '', + : '' +}); + +const updateOptions = new UpdateOptions(); +updateOptions.setReturnTokens(true); // return tokens for updated fields + +const response: UpdateResponse = await skyflowClient + .vault('') + .update(updateRequest, updateOptions); + +console.log('Update response:', response); +``` + +Returns an [`UpdateResponse`](docs/api_reference.md#updateresponse) (`updatedField`, `errors`). With the default `setReturnTokens(false)`, `updatedField` contains only `skyflowId`: + +```text +Update response: { updatedField: { skyflowId: 'a8f0c2e1-7b3d-4f9a-8c21-1d2e3f4a5b6c' }, errors: null } +``` + +> **Note:** The response key is `skyflowId`. The legacy `skyflow_id` key is deprecated and will be removed in an upcoming release. + +For all `UpdateOptions` setters and `TokenMode` values, see [UpdateOptions](docs/api_reference.md#updateoptions) in the API reference. + +> [!TIP] +> See the full example in the samples directory: [update-record.ts](samples/vault-api/update-record.ts) + +### Delete Records + +Delete records using Skyflow IDs with the `delete` method. Create a delete request with the [`DeleteRequest`](docs/api_reference.md#deleterequest) class, which accepts a list of Skyflow IDs: + +```typescript +import { DeleteRequest, DeleteResponse } from "skyflow-node"; + +const deleteRequest = new DeleteRequest("table1", [ + "", + "", + "", +]); + +const response: DeleteResponse = await skyflowClient + .vault("") + .delete(deleteRequest); + +console.log("Delete response:", response); +``` + +Returns a [`DeleteResponse`](docs/api_reference.md#deleteresponse) (`deletedIds`, `errors`): + +```text +Delete response: { deletedIds: ['a8f0c2e1-7b3d-4f9a-8c21-1d2e3f4a5b6c'], errors: null } +``` + +> [!TIP] +> See the full example in the samples directory: [delete-records.ts](samples/vault-api/delete-records.ts) + +### Query + +Retrieve data with SQL queries using the `query` method. Create a query request with the [`QueryRequest`](docs/api_reference.md#queryrequest) class, which takes the `query` parameter as follows: + + + +```typescript +import { QueryRequest, QueryResponse } from "skyflow-node"; + +const queryRequest = new QueryRequest( + "SELECT * FROM table1 WHERE column1 = 'value'", +); + +const response: QueryResponse = await skyflowClient + .vault("") + .query(queryRequest); + +console.log("Query response:", response); +``` + +Returns a [`QueryResponse`](docs/api_reference.md#queryresponse) (`fields`, `errors`). Each record also includes a `tokenizedData` map: + +```text +Query response: { fields: [{ skyflowId: 'a8f0c2e1-7b3d-4f9a-8c21-1d2e3f4a5b6c', card_number: '4111111111111112', tokenizedData: { card_number: '5391-4629-3722-7102' } }], errors: null } +``` + +> [!TIP] +> See the full example in the samples directory: [query-records.ts](samples/vault-api/query-records.ts) + +Refer to [Query your data](https://docs.skyflow.com/query-data/) and [Execute Query](https://docs.skyflow.com/record/#QueryService_ExecuteQuery) for guidelines and restrictions on supported SQL statements, operators, and keywords. + +### Upload File + +Upload files to a Skyflow vault using the `uploadFile` method. Requires Node.js v20 or above. Create a [`FileUploadRequest`](docs/api_reference.md#fileuploadrequest) with the table name and column name, then configure a `FileUploadOptions` with the file source and optionally a Skyflow ID. + +**Attach a file to an existing record:** + +```typescript +import { FileUploadRequest, FileUploadResponse, FileUploadOptions } from "skyflow-node"; + +const uploadReq = new FileUploadRequest("", ""); + +const uploadOptions = new FileUploadOptions(); +uploadOptions.setSkyflowId(""); // attach to this record +uploadOptions.setFilePath("path/to/file.pdf"); + +const response: FileUploadResponse = await skyflowClient + .vault("") + .uploadFile(uploadReq, uploadOptions); + +console.log("File upload:", response); +``` + +**Upload a file and create a new record (omit `setSkyflowId`):** + +```typescript +const uploadReq = new FileUploadRequest("documents", "attachment"); + +const uploadOptions = new FileUploadOptions(); +uploadOptions.setFilePath("path/to/file.pdf"); // no setSkyflowId — creates a new record + +const response: FileUploadResponse = await skyflowClient + .vault("") + .uploadFile(uploadReq, uploadOptions); + +console.log("File upload:", response); +``` + +Both forms return a [`FileUploadResponse`](docs/api_reference.md#fileuploadresponse) (`skyflowId`, `errors`): + +```text +File upload: { skyflowId: 'a8f0c2e1-7b3d-4f9a-8c21-1d2e3f4a5b6c', errors: null } +``` + +For all `FileUploadOptions` setters, see [FileUploadOptions](docs/api_reference.md#fileuploadoptions) in the API reference. + +> [!TIP] +> See the full example in the samples directory: [file-upload.ts](samples/vault-api/file-upload.ts) + +### Retrieve Existing Tokens: `.tokenize(request)` + +Retrieve tokens for values that already exist in the vault using the `.tokenize()` method. This method returns existing tokens only and does not generate new tokens. Build the request with the [`TokenizeRequest`](docs/api_reference.md#tokenizerequest) class. + +#### Construct a `.tokenize()` request + +```typescript +import { TokenizeRequest, TokenizeResponse } from "skyflow-node"; + +const tokenizeRequest = new TokenizeRequest([ + { value: "", columnGroup: "" }, + { value: "", columnGroup: "" }, +]); + +const response: TokenizeResponse = await skyflowClient + .vault("") + .tokenize(tokenizeRequest); + +console.log("Tokenization Result:", response); +``` + +Returns a [`TokenizeResponse`](docs/api_reference.md#tokenizeresponse) (`tokens`, `errors`): + +```text +Tokenization Result: { tokens: ['5391-4629-3722-7102', '0f6b8a2c-90ab-4cde-9def-567890abcdef'], errors: null } +``` + +> [!TIP] +> See the full example in the samples directory: [tokenize-records.ts](samples/vault-api/tokenize-records.ts) + +## Detect + +De-identify and reidentify sensitive data in text and files using Skyflow Detect, which supports advanced privacy-preserving workflows. + +### De-identify Text: `.deidentifyText(request, options)` + +De-identify or anonymize text using the `deidentifyText` method. + +Create a de-identify text request with the [`DeidentifyTextRequest`](docs/api_reference.md#deidentifytextrequest) class, which includes the text to be deidentified. Provide optional parameters using the [`DeidentifyTextOptions`](docs/api_reference.md#deidentifytextoptions) class. + +```typescript +import { + DeidentifyTextRequest, + DeidentifyTextOptions, + SkyflowError, + TokenFormat, + TokenType, + Transformations, + DetectEntities, +} from "skyflow-node"; + +// Prepare the text to be deidentified +const deidentifyTextRequest = new DeidentifyTextRequest( + "", +); + +// Configure DeidentifyTextOptions +const options = new DeidentifyTextOptions(); +options.setEntities([DetectEntities.ACCOUNT_NUMBER, DetectEntities.SSN]); // Entities to de-identify +options.setAllowRegexList([""]); // Allowlist regex patterns +options.setRestrictRegexList([""]); // Restrict regex patterns + +const tokenFormat = new TokenFormat(); +tokenFormat.setDefault(TokenType.VAULT_TOKEN); // default format +options.setTokenFormat(tokenFormat); + +const transformations = new Transformations(); +transformations.setShiftDays({ + max: 30, // maximum days to shift + min: 10, // minimum days to shift + entities: [DetectEntities.ACCOUNT_NUMBER, DetectEntities.SSN], // entity types to apply the shift +}); +options.setTransformations(transformations); + +// Call deidentifyText +const response = await skyflowClient + .detect(primaryVaultConfig.vaultId) + .deidentifyText(deidentifyTextRequest, options); + +console.log("De-identify Text Response:", response); +``` + +Returns a [`DeidentifyTextResponse`](docs/api_reference.md#deidentifytextresponse) (`processedText`, `entities`, `wordCount`, `charCount`, `errors`): + +```text +De-identify Text Response: { processedText: 'Hello, [PERSON_1]! My SSN is [SSN_1].', entities: [{ token: '[PERSON_1]', value: 'John', entity: 'NAME', scores: { NAME: 0.98 } }], wordCount: 8, charCount: 37, errors: null } +``` + +> [!TIP] +> See the full example in the samples directory: [deidentify-text.ts](samples/detect-api/deidentify-text.ts) + +### Re-identify Text: `.reidentifyText(request, options)` + +Re-identify text using the `reidentifyText` method. Create a reidentify text request with the [`ReidentifyTextRequest`](docs/api_reference.md#reidentifytextrequest) class, which includes the redacted or de-identified text to be re-identified. Provide optional parameters using the [`ReidentifyTextOptions`](docs/api_reference.md#reidentifytextoptions) class to control how specific entities are returned (as redacted, masked, or plain text). + +```typescript +import { + ReidentifyTextRequest, + ReidentifyTextOptions, + SkyflowError, + DetectEntities, + ReidentifyTextResponse, +} from "skyflow-node"; + +// Prepare the redacted text to be re-identified +const request = new ReidentifyTextRequest(""); + +// Configure ReidentifyTextOptions +const options = new ReidentifyTextOptions(); +options.setRedactedEntities([DetectEntities.SSN]); // Entities to keep redacted +options.setMaskedEntities([DetectEntities.CREDIT_CARD_NUMBER]); // Entities to mask +options.setPlainTextEntities([DetectEntities.NAME]); // Entities to return as plain text + +// Call reidentifyText +const response: ReidentifyTextResponse = await skyflowClient + .detect(primaryVaultConfig.vaultId) + .reidentifyText(request, options); + +console.log("Reidentify Text Response:", response); +``` + +Returns a [`ReidentifyTextResponse`](docs/api_reference.md#reidentifytextresponse) (`processedText`): + +```text +Reidentify Text Response: { processedText: 'Hello, John! My SSN is 123-45-6789.' } +``` + +> [!TIP] +> See the full example in the samples directory: [reidentify-text.ts](samples/detect-api/reidentify-text.ts) + +### De-identify File: `.deidentifyFile(fileReq, options)` + +De-identify files using the `deidentifyFile` method. Create a de-identify file request with the [`DeidentifyFileRequest`](docs/api_reference.md#deidentifyfilerequest) class, which includes the file to be deidentified (such as images, PDFs, audio, documents, spreadsheets, or presentations). Provide optional parameters using the [`DeidentifyFileOptions`](docs/api_reference.md#deidentifyfileoptions) class to control how entities are detected and deidentified, as well as how the output is generated for different file types. + +> [!NOTE] +> File de-identification requires Node.js v20.x or above. + +```typescript +import { + DeidentifyFileRequest, + DeidentifyFileOptions, + DeidentifyFileResponse + SkyflowError, + DetectEntities, + MaskingMethod, + DetectOutputTranscription, + Bleep +} from 'skyflow-node'; + +// Prepare the file to be deidentified +const filePath: string = ''; +const buffer = fs.readFileSync(filePath); +const file = new File([buffer], filePath); + +// Construct the file input by providing either a file object or file path, but not both +const fileInput: FileInput = { file: file } // OR const fileInput: FileInput = { filePath: filePath } +const fileReq = new DeidentifyFileRequest(fileInput); + +// Configure DeidentifyFileOptions +const options = new DeidentifyFileOptions(); +options.setEntities([DetectEntities.SSN, DetectEntities.ACCOUNT_NUMBER]); + +const tokenFormat = new TokenFormat(); // Token format for deidentified entities +tokenFormat.setDefault(TokenType.ENTITY_ONLY); +options.setTokenFormat(tokenFormat); + + +options.setOutputDirectory(''); // Output directory for saving the deidentified file. Not supported in Cloudflare workers. +options.setWaitTime(64); // Wait time for polling (max 64 seconds; returns runId + status if exceeded) + + +tifyFile +const response: DeidentifyFileResponse = await skyflowClient + .detect(primaryVaultConfig.vaultId) + .deidentifyFile(fileReq, options); + +console.log('De-identify File Response:', response); +``` + +Returns a [`DeidentifyFileResponse`](docs/api_reference.md#deidentifyfileresponse) (`fileBase64`, `file`, `runId`, `status`, `errors`). If processing exceeds `setWaitTime`, only `runId` and `status` are populated — poll with `getDetectRun`: + +```text +De-identify File Response: { runId: 'run-abc123', status: 'IN_PROGRESS', fileBase64: undefined, errors: null } +``` + +**Supported file types:** + +- Documents: `doc`, `docx`, `pdf` +- PDFs: `pdf` +- Images: `bmp`, `jpeg`, `jpg`, `png`, `tif`, `tiff` +- Structured text: `json`, `xml` +- Spreadsheets: `csv`, `xls`, `xlsx` +- Presentations: `ppt`, `pptx` +- Audio: `mp3`, `wav` + +**Notes:** +- Transformations can't be applied to Documents, Images, or PDFs. +- `setWaitTime` must be ≤ 64 seconds; otherwise, an error is thrown. +- If the API takes more than `waitTime` seconds, the response contains only `runId` and `status`. Use `.getDetectRun()` to poll for the result. + +For all `DeidentifyFileOptions` setters and the `Bleep` class, see [DeidentifyFileOptions](docs/api_reference.md#deidentifyfileoptions) in the API reference. + +> [!TIP] +> See the full example in the samples directory: [deidentify-file.ts](samples/detect-api/deidentify-file.ts) + +### Get Run: `.getDetectRun(request)` + +Retrieve the results of a previously started file de-identification operation (or 'run') using the `getDetectRun(...)` method. Initialize the [`GetDetectRunRequest`](docs/api_reference.md#getdetectrunrequest) class with the `runId` returned from a prior `.deidentifyFile(fileReq, options)` call. Fetch the final results of the file de-identification operation once they are available. + +```typescript +import { + GetDetectRunRequest, + DeidentifyFileResponse, + DeidentifyFileResponse + SkyflowError +} from 'skyflow-node'; + +// Prepare the GetDetectRunRequest with the runId from a previous deidentifyFile call +const request = new GetDetectRunRequest({ + runId: '', // Replace with the runId you received earlier +}); + +// Step 2: Call getDetectRun +const response: DeidentifyFileResponse = await skyflowClient + .detect(primaryVaultConfig.vaultId) + .getDetectRun(request); + +// Step 3: Handle the response +console.log('Get Detect Run Response:', response); +``` + +Returns a [`DeidentifyFileResponse`](docs/api_reference.md#deidentifyfileresponse) with the current `status` (and the processed file once complete): + +```text +Get Detect Run Response: { status: 'SUCCESS', runId: 'run-abc123', fileBase64: '', errors: null } +``` + +> [!TIP] +> See the full example in the samples directory: [get-detect-run.ts](samples/detect-api/get-detect-run.ts) + +## Connections + +Securely send and receive data between your systems and first- or third-party services using Skyflow Connections, a gateway service that uses tokenization. The [connections](https://github.com/skyflowapi/skyflow-node/tree/v2/src/vault/controller/connections) module invokes both inbound and outbound connections. + +- **Inbound connections**: Act as intermediaries between your client and server, tokenizing sensitive data before it reaches your backend, ensuring downstream services handle only tokenized data. +- **Outbound connections**: Enable secure extraction of data from the vault and transfer it to third-party services via your backend server, such as processing checkout or card issuance flows. + +### Configure a connection + +Add a `ConnectionConfig` to `SkyflowConfig.connectionConfigs` during initialization, or call `addConnectionConfig()` at runtime: + +```typescript +import { ConnectionConfig, Credentials } from 'skyflow-node'; + +const connectionConfig: ConnectionConfig = { + connectionId: '', // required: unique connection identifier + connectionUrl: '', // required: gateway URL for this connection + // credentials: connectionCredentials // optional: per-connection credentials; overrides skyflowCredentials +}; +``` + +### Invoke a connection + +Invoke a connection using the `invoke` method of the Skyflow client. Build the request with the [`InvokeConnectionRequest`](docs/api_reference.md#invokeconnectionrequest) class. + +#### Construct an invoke connection request + +```typescript +import { + InvokeConnectionRequest, + RequestMethod, + InvokeConnectionResponse, +} from "skyflow-node"; + +const invokeRequest = new InvokeConnectionRequest( + RequestMethod.POST, + { : "" }, + { : "" }, + { : "" }, + { : "" } +); + +const response: InvokeConnectionResponse = await skyflowClient + .connection() + .invoke(invokeRequest); + +console.log("Invoke connection response:", response); +``` + +Returns an [`InvokeConnectionResponse`](docs/api_reference.md#invokeconnectionresponse) (`data`, `metadata`, `errors`): + +```text +Invoke connection response: { data: { status: 'success', transaction_id: 'txn_abc123' }, metadata: { requestId: 'req-xyz-456' }, errors: null } +``` + +`method` supports the following methods (see [`RequestMethod`](docs/api_reference.md#requestmethod)): + +- `GET` +- `POST` +- `PUT` +- `PATCH` + +**pathParams, queryParams, header, body** are the JSON objects represented as dictionaries that will be sent through the connection integration url. + +> [!TIP] +> See the full example in the samples directory: [scoped-token-generation-example.ts](samples/vault-api/invoke-connection.ts) +> See [docs.skyflow.com](https://docs.skyflow.com) for more details on integrations with Connections, Functions, and Pipelines. + +## Authentication & authorization + +### Types of `credentials` + +The SDK accepts one of several types of credentials object. + +1. **API keys** + A unique identifier used to authenticate and authorize requests to an API. Use for long-term service authentication. To create an API key, first create a 'Service Account' in Skyflow and choose the 'API key' option during creation. + + ```ts + const credentials: Credentials = { + apiKey: "" + }; + ``` + +2. **Bearer tokens** + A temporary access token used to authenticate API requests. Use for optimal security. As a developer with the right access, you can generate a temporary personal bearer token in Skyflow in the user menu. + + ```ts + const credentials: Credentials = { + token: "" + }; + ``` + +3. **Service account credentials file path** + The file path pointing to a JSON file containing credentials for a service account. Use when credentials are managed externally or stored in secure file systems. + + ```ts + const credentials: Credentials = { + path: "" + }; + ``` + +4. **Service account credentials string** + JSON-formatted string containing service account credentials. Use when integrating with secret management systems or when credentials are passed programmatically. + + ```ts + const credentials: Credentials = { + credentialsString: JSON.stringify(process.env.SKYFLOW_CREDENTIALS) + }; + ``` + +5. **Environment variables** + If no credentials are explicitly provided, the SDK automatically looks for the SKYFLOW_CREDENTIALS environment variable. Use to avoid hardcoding credentials in source code. This variable must return an object like one of the examples above. + +> [!NOTE] +> Only one type of credential can be used at a time. If multiple credentials are provided, the last one added will take precedence. + +### Generate bearer tokens for authentication & authorization + +Generate and manage bearer tokens to authenticate API calls. This section covers options for scoping to certain roles, passing context, and signing data tokens. + +#### Generate a bearer token + +Generate service account tokens using the [Service Account](https://github.com/skyflowapi/skyflow-node/tree/v2/src/service-account) Node package with a service account credentials file provided when a service account is created. Tokens generated by this module are valid for 60 minutes and can be used to make API calls to the [Data](https://docs.skyflow.com/record/) and [Management](https://docs.skyflow.com/management/) APIs, depending on the permissions assigned to the service account. + +##### `generateBearerToken(filepath)` + +The `generateBearerToken(filepath)` function takes the `credentials.json` file path for token generation. + +```ts +import { generateBearerToken, BearerTokenOptions } from 'skyflow-node'; + +const options: BearerTokenOptions = { + roleIds: ['roleId1'], // optional: scope to specific roles + ctx: 'user_12345', // optional: embed context + logLevel: LogLevel.ERROR, +}; + +generateBearerToken('path/to/credentials.json', options) + .then(response => { + const bearerToken = response.accessToken; + }) + .catch(error => { /* handle error */ }); +``` + +##### `generateBearerTokenFromCreds(credentialsString)` + +Use `generateBearerTokenFromCreds` when your credentials JSON is available as a string (e.g. from a secret manager) rather than a file on disk. It accepts the same `BearerTokenOptions` as `generateBearerToken`. + +```ts +import { generateBearerTokenFromCreds, BearerTokenOptions } from 'skyflow-node'; + +const credentialsString = JSON.stringify(require('./credentials.json')); +const options: BearerTokenOptions = { roleIds: ['roleId1'] }; + +const response = await generateBearerTokenFromCreds(credentialsString, options); +const bearerToken = response.accessToken; +``` + +**`BearerTokenOptions` type:** + +```ts +type BearerTokenOptions = { + roleIds?: string[]; // Scope the token to specific service-account roles + ctx?: string | Record; // Context value(s) embedded in the token + logLevel?: LogLevel; // Override SDK log level for this call + tokenUri?: string; // Override the token endpoint URL from the credentials file +}; +``` + + +**Checking whether a token has expired — `isExpired`:** + +```ts +import { isExpired } from 'skyflow-node'; + +// Returns true if the JWT bearer token is expired or will expire within the threshold +const expired: boolean = isExpired(bearerToken); +if (expired) { + // regenerate before making API calls + const { accessToken } = await generateBearerToken('path/to/credentials.json'); +} +``` + +> [!TIP] +> See the full example in the samples directory: [token-generation-example.ts](http://github.com/skyflowapi/skyflow-node/blob/v2/samples/service-account/token-generation-example.ts) + +#### Generate bearer tokens scoped to certain roles + +Generate bearer tokens with access limited to a specific role by specifying the appropriate `roleIds` when using a service account with multiple roles. Use this to limit access for services with multiple responsibilities, such as segregating access for billing and analytics. Generated bearer tokens are valid for 60 minutes and can only execute operations permitted by the permissions associated with the designated role. + +```ts +const options: BearerTokenOptions = { + roleIds: ['roleId1', 'roleId2'], +}; +``` + +> [!TIP] +> See the full example in the samples directory: [scoped-token-generation-example.ts](samples/service-account/scoped-token-generation-example.ts) +> See [docs.skyflow.com](https://docs.skyflow.com) for more details on authentication, access control, and governance for Skyflow. + +#### Generate bearer tokens with `ctx` for context-aware authorization + +Embed context values into a bearer token during generation so you can reference those values in your policies. This enables more flexible access controls, such as tracking end-user identity when making API calls using service accounts, and facilitates using signed data tokens during detokenization. + +Generate bearer tokens containing context information using a service account with the `context_id` identifier. Context information is represented as a JWT claim in a Skyflow-generated bearer token. Tokens generated from such service accounts include a `context_identifier` claim, are valid for 60 minutes, and can be used to make API calls to the Data and Management APIs, depending on the service account's permissions. + +The `ctx` parameter accepts either a **string** or a **JSON object**: + +**String context** — use when your policy references a single context value: + +```typescript +const options = { + ctx: 'user_12345', +}; +const response = await generateBearerToken(filepath, options); +``` + +**JSON object context** — use when your policy needs multiple context values for conditional data access. Each key in the `ctx` object maps to a Skyflow CEL policy variable under `request.context.*`: + +```typescript +const options = { + ctx: { + role: 'admin', + department: 'finance', + user_id: 'user_12345', + }, +}; +const response = await generateBearerToken(filepath, options); +``` + +With the object above, your Skyflow policies can reference `request.context.role`, `request.context.department`, and `request.context.user_id` to make conditional access decisions. + +You can also set the `context` field on credentials for automatic token generation: + +```typescript +// String context on credentials +const credentials: PathCredentials = { + path: 'path/to/credentials.json', + context: 'user_12345', +}; + +// JSON object context on credentials +const credentials: PathCredentials = { + path: 'path/to/credentials.json', + context: { + role: 'admin', + department: 'finance', + }, +}; +``` + +> [!TIP] +> See the full example in the samples directory: [token-generation-with-context-example.ts](samples/service-account/token-generation-with-context-example.ts) +> See Skyflow's [context-aware authorization](https://docs.skyflow.com) and [conditional data access](https://docs.skyflow.com) docs for policy variable syntax like `request.context.*`. + +#### Generate signed data tokens + +Digitally sign data tokens with a service account's private key to add an extra layer of protection. Skyflow generates data tokens when sensitive data is inserted into the vault. Detokenize signed tokens only by providing the signed data token along with a bearer token generated from the service account's credentials. The service account must have the necessary permissions and context to successfully detokenize the signed data tokens. + +Two variants are available — choose based on how you supply credentials: + +```ts +import { + generateSignedDataTokens, + generateSignedDataTokensFromCreds, + SignedDataTokensOptions, +} from 'skyflow-node'; + +const options: SignedDataTokensOptions = { + dataTokens: ['dataToken1', 'dataToken2'], // required: tokens to sign + timeToLive: 90, // optional: seconds until expiry (default 60) + ctx: 'user_12345', // optional: context embedded in token +}; + +// From a credentials file on disk: +const response = await generateSignedDataTokens('path/to/credentials.json', options); + +// From a credentials JSON string (e.g. from a secret manager): +const credentialsString = JSON.stringify(require('./credentials.json')); +const response = await generateSignedDataTokensFromCreds(credentialsString, options); + +response.forEach(({ token, signedToken }) => { + console.log('Original token:', token); + console.log('Signed token:', signedToken); +}); +``` + +The `ctx` parameter also accepts a JSON object for multi-value context: + +```typescript +const options: SignedDataTokensOptions = { + dataTokens: ['dataToken1'], + ctx: { role: 'analyst', department: 'research' }, +}; +``` + +**`SignedDataTokensOptions` type:** + +```ts +type SignedDataTokensOptions = { + dataTokens: string[]; // Data tokens to sign (required) + timeToLive?: number; // Seconds until token expiry (default 60) + ctx?: string | Record; // Context value(s) embedded in the token + logLevel?: LogLevel; // Override SDK log level for this call + tokenUri?: string; // Override the token endpoint URL from credentials +}; +``` + +> [!TIP] +> See the full example in the samples directory: [signed-token-generation-example.ts](samples/service-account/signed-token-generation-example.ts) +> See [docs.skyflow.com](https://docs.skyflow.com) for more details on authentication, access control, and governance for Skyflow. + +## Client management + +After initializing the `Skyflow` client, you can add, update, retrieve, and remove vault and connection configurations at any time without recreating the client. For the full list of methods, see [Client management methods](docs/api_reference.md#client-management-methods) in the API reference. + +## Using the client in production + +**Initialize once and reuse.** `new Skyflow(config)` returns a long-lived client that caches tokens per vault. Construct it once at startup (e.g. as a module-level singleton) and reuse it across requests. Recreating the client on every call discards these caches and forces unnecessary token regeneration. + +```ts +// At application startup +const skyflowClient = new Skyflow(skyflowConfig); + +// Reuse `skyflowClient` for the lifetime of the process +``` + +**Bearer token refresh is automatic.** When you authenticate with a service-account credentials file/string (or API key), the SDK caches the generated bearer token and regenerates it automatically once it expires. You don't need to manage token lifecycle yourself. (For the rare expire-mid-request case, see [Bearer token expiration edge cases](#bearer-token-expiration-edge-cases).) + +**Runtime configuration changes are not concurrency-safe.** Methods that mutate client state at runtime — `addVaultConfig`, `updateVaultConfig`, `removeVaultConfig`, the `*ConnectionConfig` methods, and `updateSkyflowCredentials` — should be called during setup, not concurrently with in-flight requests. Once configured, the client is safe to reuse across concurrent calls. + +**Timeouts and retries.** The SDK does not currently expose request timeout or automatic-retry configuration. Wrap your SDK calls with your own timeout/retry logic at the application layer if needed. + +## Logging + +The SDK provides useful logging. By default, the logging level is set to `LogLevel.ERROR`. Change this by setting the `logLevel` in Skyflow Config while creating the Skyflow Client as shown below: + +Currently, the following five log levels are supported: + +- `DEBUG`: + When `LogLevel.DEBUG` is passed, logs at all levels will be printed (DEBUG, INFO, WARN, ERROR). +- `INFO`: + When `LogLevel.INFO` is passed, INFO logs for every event that occurs during SDK flow execution will be printed, along with WARN and ERROR logs. +- `WARN`: + When `LogLevel.WARN` is passed, only WARN and ERROR logs will be printed. +- `ERROR`: + When `LogLevel.ERROR` is passed, only ERROR logs will be printed. +- `OFF`: + `LogLevel.OFF` can be used to turn off all logging from the Skyflow Node SDK. + +> [!NOTE] +> The ranking of logging levels is as follows: `DEBUG` < `INFO` < `WARN` < `ERROR` < `OFF`. + +### Example `skyflowConfig.logLevel: LogLevel.INFO` + +```ts +const skyflowConfig: SkyflowConfig = { + vaultConfigs: [vaultConfig], // Add the Vault configuration + skyflowCredentials: skyflowCredentials, // Use Skyflow credentials if no token is passed + logLevel: LogLevel.INFO, // Recommended to use LogLevel.ERROR in production environment. +}; + +const skyflowClient: Skyflow = new Skyflow(skyflowConfig); +``` + +## Error handling + +### Catching `SkyflowError` instances + +Wrap your calls to the Skyflow SDK in try/catch blocks as a best practice. Use the `SkyflowError` type to identify errors coming from Skyflow versus general request/response errors. + +```ts +try { + // ...call the Skyflow SDK +} catch (error) { + // catch an error, identify if it is a SkyflowError + if (error instanceof SkyflowError) { + console.error("Skyflow Specific Error:", { + code: error.error?.httpCode, + message: error.message, + requestId: error.error?.requestId, + details: error.error?.details, + }); + } else { + console.error("Unexpected Error:", JSON.stringify(error)); + } +} +``` + +### Per-record errors (`SkyflowRecordError`) + +Some operations — such as `insert` with `continueOnError: true` and `detokenize` — can partially succeed. In these cases the response object contains an `errors` array alongside the successful results. Each entry in `errors` is a `SkyflowRecordError`: + +```ts +import { InsertResponse, SkyflowRecordError } from 'skyflow-node'; + +const response: InsertResponse = await skyflowClient.vault(vaultId).insert(request, options); + +if (response.errors && response.errors.length > 0) { + response.errors.forEach((err: SkyflowRecordError) => { + console.error({ + requestId: err.requestId, // x-request-id from the server response + httpCode: err.httpCode, // HTTP status code for this record + requestIndex: err.requestIndex, // Index of the failed record in the input array + error: err.error, // Error description string + token: err.token, // Token (detokenize errors only) + }); + }); +} +``` + +`SkyflowRecordError` shape: + +```ts +interface SkyflowRecordError { + error: string; + requestId: string | null; + httpCode?: string | number | null; + requestIndex?: number | null; + token?: string | null; // present on detokenize errors +} +``` + +### Bearer token expiration edge cases + +When using bearer tokens for authentication and API requests, a token may expire after verification but before the actual API call completes. This causes the request to fail unexpectedly. An error from this edge case looks like this: + +```txt +message: Authentication failed. Bearer token is expired. Use a valid bearer token. See https://docs.skyflow.com/api-authentication/ +``` + +If you encounter this kind of error, retry the request. During the retry the SDK detects that the previous bearer token has expired and generates a new one for the current and subsequent requests. + +> [!TIP] +> See the full example in the samples directory: [bearer-token-expiry-example.ts](samples/service-account/bearer-token-expiry-example.ts) +> See [docs.skyflow.com](https://docs.skyflow.com) for more details on authentication, access control, and governance for Skyflow. + +## Troubleshooting + +Most first-run problems come from configuration mismatches. Every error thrown by the SDK is a `SkyflowError` exposing `httpCode`, `message`, and `details` — inspect these first (see [Error handling](#error-handling)). + +| Symptom | Likely cause | Fix | +|---|---|---| +| `npm install skyflow-node` fails | Unsupported Node version | Requires Node v12.22.12 or above. | +| Connection/DNS failures, or 404 on every call | Wrong `clusterId` | `clusterId` is the first segment of your vault URL: `https://{clusterId}.vault.skyflowapis.com`. | +| Requests hit the wrong host / unexpected auth failures | Wrong `env` | Match `env` to where your vault runs (`Env.PROD`, `Env.SANDBOX`, `Env.DEV`, `Env.STAGE`). | +| `401 Unauthorized` | Invalid or expired credentials | Verify your API key or service-account credentials. Regenerate if needed. | +| `403 Forbidden` | Service account lacks permission | Grant the service account a role with the required permissions, or use a [scoped token](#generate-bearer-tokens-scoped-to-certain-roles). | +| `404` referencing a table or column | Name mismatch | Confirm table and column names match your vault schema exactly (case-sensitive). | +| Vault not found / 404 with a valid `clusterId` | Wrong `vaultId` | Copy `vaultId` from the vault's details page in Skyflow Studio. | +| `Authentication failed. Bearer token is expired.` | Token expired mid-request | Retry the request; the SDK regenerates the token. See [Bearer token expiration edge cases](#bearer-token-expiration-edge-cases). | +| Unexpected credential is used | Multiple credential types provided | Only one credential type is used at a time; the last one added takes precedence. | + +If you're stuck, set `logLevel: LogLevel.DEBUG` during development for detailed SDK logs (see [Logging](#logging)). + +## TypeScript types reference + +For the full reference of all exported types, interfaces, enums, request/response fields, and options setters, see the **[API Reference](docs/api_reference.md)**. + +The most commonly imported TypeScript types are: + +```ts +import { + // Credential sub-types (for stricter typing) + ApiKeyCredentials, + TokenCredentials, + PathCredentials, // supports optional tokenUri + StringCredentials, // supports optional tokenUri + + // Request item shapes + DetokenizeData, // { token: string; redactionType?: RedactionType } + TokenizeRequestType, // { value: string; columnGroup: string } + FileInput, // { file: File } | { filePath: string } + + // Response record shapes + InsertResponseType, // { skyflowId: string; [field: string]: unknown } + GetResponseData, // { [field: string]: unknown } + QueryResponseType, // { [field: string]: unknown } + SkyflowRecordError, // shape of entries in response.errors[] +} from 'skyflow-node'; +``` + +## Security +### Reporting a Vulnerability + +If you discover a potential security issue in this project, reach out to us at [security@skyflow.com](mailto:security@skyflow.com). + +Don't create public GitHub issues or Pull Requests, as malicious actors could potentially view them. diff --git a/skyvault/api-extractor.json b/skyvault/api-extractor.json new file mode 100644 index 00000000..0f7db7c9 --- /dev/null +++ b/skyvault/api-extractor.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "projectFolder": ".", + "mainEntryPointFilePath": "/dist/index.d.ts", + "bundledPackages": [], + "compiler": { + "tsconfigFilePath": "/tsconfig.dts.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" }, + "ae-forgotten-export": { "logLevel": "none" } + }, + "tsdocMessageReporting": { + "default": { "logLevel": "none" } + } + } +} diff --git a/api-report/skyflow-node.api.md b/skyvault/api-report/skyflow-node.api.md similarity index 59% rename from api-report/skyflow-node.api.md rename to skyvault/api-report/skyflow-node.api.md index b22142fa..39b00709 100644 --- a/api-report/skyflow-node.api.md +++ b/skyvault/api-report/skyflow-node.api.md @@ -4,23 +4,69 @@ ```ts +import { ApiKeyCredentials } from 'skyflow-common'; +import { AuthInfo } from 'skyflow-common'; +import { BaseDeleteRequest } from 'skyflow-common'; +import { BaseDeleteResponse } from 'skyflow-common'; +import { BaseDetokenizeRequest } from 'skyflow-common'; +import { BaseDetokenizeResponse } from 'skyflow-common'; +import { BaseFileUploadRequest } from 'skyflow-common'; +import { BaseFileUploadResponse } from 'skyflow-common'; +import { BaseGetRequest } from 'skyflow-common'; +import { BaseGetResponse } from 'skyflow-common'; +import { BaseInsertRequest } from 'skyflow-common'; +import { BaseInsertResponse } from 'skyflow-common'; +import { BaseInvokeConnectionRequest } from 'skyflow-common'; +import { BaseInvokeConnectionResponse } from 'skyflow-common'; +import { BaseOptions } from 'skyflow-common'; +import { BaseQueryRequest } from 'skyflow-common'; +import { BaseQueryResponse } from 'skyflow-common'; +import { BaseSkyflow } from 'skyflow-common'; +import { BaseTokenizeRequest } from 'skyflow-common'; +import { BaseTokenizeResponse } from 'skyflow-common'; +import { BaseUpdateRequest } from 'skyflow-common'; +import { BaseUpdateResponse } from 'skyflow-common'; +import { BaseVaultEntry } from 'skyflow-common'; +import { BearerTokenOptions } from 'skyflow-common'; import { Blob as Blob_2 } from 'buffer'; +import { ConnectionConfig } from 'skyflow-common'; +import { Credentials } from 'skyflow-common'; +import { DetectEntities } from 'skyflow-common'; +import { DetectOutputTranscription } from 'skyflow-common'; +import { Env } from 'skyflow-common'; +import { FileInput } from 'skyflow-common'; import * as fs from 'fs'; - -// @public (undocumented) -export interface ApiKeyCredentials { - // (undocumented) - apiKey: string; -} - -// @public (undocumented) -export type BearerTokenOptions = { - ctx?: string | Record; - roleIDs?: string[]; - roleIds?: string[]; - logLevel?: LogLevel; - tokenUri?: string; -}; +import { generateBearerToken } from 'skyflow-common'; +import { generateBearerTokenFromCreds } from 'skyflow-common'; +import { generateSignedDataTokens } from 'skyflow-common'; +import { generateSignedDataTokensFromCreds } from 'skyflow-common'; +import { GenerateTokenOptions } from 'skyflow-common'; +import { GetResponseData } from 'skyflow-common'; +import { IndexRange } from 'skyflow-common'; +import { InsertResponseType } from 'skyflow-common'; +import { isExpired } from 'skyflow-common'; +import { LogLevel } from 'skyflow-common'; +import { MaskingMethod } from 'skyflow-common'; +import { OrderByEnum } from 'skyflow-common'; +import { PathCredentials } from 'skyflow-common'; +import { QueryResponseType } from 'skyflow-common'; +import { RedactionType } from 'skyflow-common'; +import { RequestMethod } from 'skyflow-common'; +import { SignedDataTokensOptions } from 'skyflow-common'; +import { SkyflowConfig } from 'skyflow-common'; +import { SkyflowError } from 'skyflow-common'; +import { SkyflowRecordError } from 'skyflow-common'; +import { StringCredentials } from 'skyflow-common'; +import { StringKeyValueMapType } from 'skyflow-common'; +import { TokenCredentials } from 'skyflow-common'; +import { TokenizeRequestType } from 'skyflow-common'; +import { TokenMode } from 'skyflow-common'; +import { TokenType } from 'skyflow-common'; +import { VaultConfig } from 'skyflow-common'; + +export { ApiKeyCredentials } + +export { BearerTokenOptions } // @public (undocumented) export class Bleep { @@ -42,18 +88,9 @@ export class Bleep { setStopPadding(stopPadding: number): void; } -// @public (undocumented) -export interface ConnectionConfig { - // (undocumented) - connectionId: string; - // (undocumented) - connectionUrl: string; - // (undocumented) - credentials?: Credentials; -} +export { ConnectionConfig } -// @public (undocumented) -export type Credentials = TokenCredentials | PathCredentials | StringCredentials | ApiKeyCredentials; +export { Credentials } // @public (undocumented) export class DeidentifyFileOptions { @@ -251,7 +288,7 @@ export class DeidentifyTextResponse { } // @public (undocumented) -export class DeleteRequest { +export class DeleteRequest extends BaseDeleteRequest { constructor(table: string, deleteIds: Array); // (undocumented) get ids(): Array; @@ -262,7 +299,7 @@ export class DeleteRequest { } // @public (undocumented) -export class DeleteResponse { +export class DeleteResponse extends BaseDeleteResponse { // @deprecated constructor(input: { deletedIds: Array; @@ -274,161 +311,9 @@ export class DeleteResponse { errors: Array | null; } -// @public (undocumented) -export enum DetectEntities { - // (undocumented) - ACCOUNT_NUMBER = "account_number", - // (undocumented) - AGE = "age", - // (undocumented) - ALL = "all", - // (undocumented) - BANK_ACCOUNT = "bank_account", - // (undocumented) - BLOOD_TYPE = "blood_type", - // (undocumented) - CONDITION = "condition", - // (undocumented) - CORPORATE_ACTION = "corporate_action", - // (undocumented) - CREDIT_CARD = "credit_card", - // (undocumented) - CREDIT_CARD_EXPIRATION = "credit_card_expiration", - // (undocumented) - CVV = "cvv", - // (undocumented) - DATE = "date", - // (undocumented) - DATE_INTERVAL = "date_interval", - // (undocumented) - DAY = "day", - // (undocumented) - DOB = "dob", - // (undocumented) - DOSE = "dose", - // (undocumented) - DRIVER_LICENSE = "driver_license", - // (undocumented) - DRUG = "drug", - // (undocumented) - DURATION = "duration", - // (undocumented) - EFFECT = "effect", - // (undocumented) - EMAIL_ADDRESS = "email_address", - // (undocumented) - EVENT = "event", - // (undocumented) - FILENAME = "filename", - // (undocumented) - FINANCIAL_METRIC = "financial_metric", - // (undocumented) - GENDER = "gender", - // (undocumented) - HEALTHCARE_NUMBER = "healthcare_number", - // (undocumented) - INJURY = "injury", - // (undocumented) - IP_ADDRESS = "ip_address", - // (undocumented) - LANGUAGE = "language", - // (undocumented) - LOCATION = "location", - // (undocumented) - LOCATION_ADDRESS = "location_address", - // (undocumented) - LOCATION_ADDRESS_STREET = "location_address_street", - // (undocumented) - LOCATION_CITY = "location_city", - // (undocumented) - LOCATION_COORDINATE = "location_coordinate", - // (undocumented) - LOCATION_COUNTRY = "location_country", - // (undocumented) - LOCATION_STATE = "location_state", - // (undocumented) - LOCATION_ZIP = "location_zip", - // (undocumented) - MARITAL_STATUS = "marital_status", - // (undocumented) - MEDICAL_CODE = "medical_code", - // (undocumented) - MEDICAL_PROCESS = "medical_process", - // (undocumented) - MONEY = "money", - // (undocumented) - MONTH = "month", - // (undocumented) - NAME = "name", - // (undocumented) - NAME_FAMILY = "name_family", - // (undocumented) - NAME_GIVEN = "name_given", - // (undocumented) - NAME_MEDICAL_PROFESSIONAL = "name_medical_professional", - // (undocumented) - NUMERICAL_PII = "numerical_pii", - // (undocumented) - OCCUPATION = "occupation", - // (undocumented) - ORGANIZATION = "organization", - // (undocumented) - ORGANIZATION_ID = "organization_id", - // (undocumented) - ORGANIZATION_MEDICAL_FACILITY = "organization_medical_facility", - // (undocumented) - ORIGIN = "origin", - // (undocumented) - PASSPORT_NUMBER = "passport_number", - // (undocumented) - PASSWORD = "password", - // (undocumented) - PHONE_NUMBER = "phone_number", - // (undocumented) - PHYSICAL_ATTRIBUTE = "physical_attribute", - // (undocumented) - POLITICAL_AFFILIATION = "political_affiliation", - // (undocumented) - PRODUCT = "product", - // (undocumented) - PROJECT = "project", - // (undocumented) - RELIGION = "religion", - // (undocumented) - ROUTING_NUMBER = "routing_number", - // (undocumented) - SEXUALITY = "sexuality", - // (undocumented) - SSN = "ssn", - // (undocumented) - STATISTICS = "statistics", - // (undocumented) - TIME = "time", - // (undocumented) - TREND = "trend", - // (undocumented) - URL = "url", - // (undocumented) - USERNAME = "username", - // (undocumented) - VEHICLE_ID = "vehicle_id", - // (undocumented) - ZODIAC_SIGN = "zodiac_sign" -} +export { DetectEntities } -// @public (undocumented) -export enum DetectOutputTranscription { - // (undocumented) - DIARIZED_TRANSCRIPTION = "diarized_transcription", - // (undocumented) - MEDICAL_DIARIZED_TRANSCRIPTION = "medical_diarized_transcription", - // (undocumented) - MEDICAL_TRANSCRIPTION = "medical_transcription", - // (undocumented) - PLAINTEXT_TRANSCRIPTION = "plaintext_transcription", - // (undocumented) - TRANSCRIPTION = "transcription" -} +export { DetectOutputTranscription } // @public (undocumented) export interface DetokenizeData { @@ -439,7 +324,7 @@ export interface DetokenizeData { } // @public (undocumented) -export class DetokenizeOptions { +export class DetokenizeOptions extends BaseOptions { constructor(); // (undocumented) getContinueOnError(): boolean | undefined; @@ -456,7 +341,7 @@ export class DetokenizeOptions { } // @public (undocumented) -export class DetokenizeRequest { +export class DetokenizeRequest extends BaseDetokenizeRequest { constructor(data: DetokenizeData[]); // (undocumented) get data(): DetokenizeData[]; @@ -464,7 +349,7 @@ export class DetokenizeRequest { } // @public (undocumented) -export class DetokenizeResponse { +export class DetokenizeResponse extends BaseDetokenizeResponse { constructor(input: { detokenizedFields: Array | null; errors: Array | null; @@ -477,26 +362,12 @@ export class DetokenizeResponse { errors: Array | null; } -// @public (undocumented) -export enum Env { - // (undocumented) - DEV = "DEV", - // (undocumented) - PROD = "PROD", - // (undocumented) - SANDBOX = "SANDBOX", - // (undocumented) - STAGE = "STAGE" -} +export { Env } -// Warning: (ae-forgotten-export) The symbol "Filepath" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "FileObject" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export type FileInput = Filepath | FileObject; +export { FileInput } // @public (undocumented) -export class FileUploadOptions { +export class FileUploadOptions extends BaseOptions { constructor(); // (undocumented) getBase64(): string | undefined; @@ -521,13 +392,11 @@ export class FileUploadOptions { } // @public (undocumented) -export class FileUploadRequest { +export class FileUploadRequest extends BaseFileUploadRequest { constructor(table: string, columnNameOrSkyflowId: string, columnName?: string); // (undocumented) get columnName(): string; set columnName(value: string); - // @internal (undocumented) - getLegacySkyflowId(): string | undefined; // @deprecated (undocumented) get skyflowId(): string; set skyflowId(value: string); @@ -537,7 +406,7 @@ export class FileUploadRequest { } // @public (undocumented) -export class FileUploadResponse { +export class FileUploadResponse extends BaseFileUploadResponse { constructor(input: { skyflowId: string; errors: Array | null; @@ -548,26 +417,15 @@ export class FileUploadResponse { skyflowId: string; } -// Warning: (ae-forgotten-export) The symbol "TokenResponse" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export function generateBearerToken(credentialsFilePath: string, options?: BearerTokenOptions): Promise; +export { generateBearerToken } -// @public (undocumented) -export function generateBearerTokenFromCreds(credentials: any, options?: BearerTokenOptions): Promise; +export { generateBearerTokenFromCreds } -// Warning: (ae-forgotten-export) The symbol "SignedDataTokensResponse" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export function generateSignedDataTokens(credentialsFilePath: string, options: SignedDataTokensOptions): Promise; +export { generateSignedDataTokens } -// @public (undocumented) -export function generateSignedDataTokensFromCreds(credentials: any, options: SignedDataTokensOptions): Promise; +export { generateSignedDataTokensFromCreds } -// @public (undocumented) -export type GenerateTokenOptions = { - logLevel?: LogLevel; -}; +export { GenerateTokenOptions } // @public (undocumented) export class GetColumnRequest { @@ -592,7 +450,7 @@ export class GetDetectRunRequest { } // @public (undocumented) -export class GetOptions { +export class GetOptions extends BaseOptions { constructor(); // (undocumented) getColumnName(): string | undefined; @@ -637,7 +495,7 @@ export class GetOptions { } // @public (undocumented) -export class GetRequest { +export class GetRequest extends BaseGetRequest { constructor(table: string, _ids: Array); // (undocumented) get ids(): Array; @@ -648,7 +506,7 @@ export class GetRequest { } // @public (undocumented) -export class GetResponse { +export class GetResponse extends BaseGetResponse { constructor(input: { data: Array; errors: Array | null; @@ -659,22 +517,12 @@ export class GetResponse { errors: Array | null; } -// @public (undocumented) -export interface GetResponseData { - // (undocumented) - [key: string]: unknown; -} +export { GetResponseData } -// @public (undocumented) -export interface IndexRange { - // (undocumented) - end?: number; - // (undocumented) - start?: number; -} +export { IndexRange } // @public (undocumented) -export class InsertOptions { +export class InsertOptions extends BaseOptions { constructor(); // (undocumented) getContinueOnError(): boolean | undefined; @@ -703,7 +551,7 @@ export class InsertOptions { } // @public (undocumented) -export class InsertRequest { +export class InsertRequest extends BaseInsertRequest { constructor(table: string, data: Record[]); // (undocumented) get data(): Record[]; @@ -714,7 +562,7 @@ export class InsertRequest { } // @public (undocumented) -export class InsertResponse { +export class InsertResponse extends BaseInsertResponse { // @deprecated constructor(input: { insertedFields: Array; @@ -726,18 +574,10 @@ export class InsertResponse { insertedFields: Array; } -// @public (undocumented) -export interface InsertResponseType { - // (undocumented) - [key: string]: unknown; - // @deprecated (undocumented) - skyflow_id?: string; - // (undocumented) - skyflowId: string; -} +export { InsertResponseType } // @public (undocumented) -export class InvokeConnectionRequest { +export class InvokeConnectionRequest extends BaseInvokeConnectionRequest { constructor(method: RequestMethod, body?: StringKeyValueMapType, headers?: StringKeyValueMapType, pathParams?: StringKeyValueMapType, queryParams?: StringKeyValueMapType); // (undocumented) body?: StringKeyValueMapType; @@ -752,7 +592,7 @@ export class InvokeConnectionRequest { } // @public (undocumented) -export class InvokeConnectionResponse { +export class InvokeConnectionResponse extends BaseInvokeConnectionResponse { constructor(input: { data?: object; metadata?: Record; @@ -766,55 +606,18 @@ export class InvokeConnectionResponse { metadata?: Record; } -// @public (undocumented) -export function isExpired(token: string): boolean; +export { isExpired } -// @public (undocumented) -export enum LogLevel { - // (undocumented) - DEBUG = "DEBUG", - // (undocumented) - ERROR = "ERROR", - // (undocumented) - INFO = "INFO", - // (undocumented) - OFF = "OFF", - // (undocumented) - WARN = "WARN" -} +export { LogLevel } -// @public (undocumented) -export enum MaskingMethod { - // (undocumented) - Blackbox = "blackbox", - // (undocumented) - Blur = "blur" -} +export { MaskingMethod } -// @public (undocumented) -export enum OrderByEnum { - // (undocumented) - ASCENDING = "ASCENDING", - // (undocumented) - DESCENDING = "DESCENDING", - // (undocumented) - NONE = "NONE" -} +export { OrderByEnum } -// @public (undocumented) -export interface PathCredentials { - // (undocumented) - context?: string | Record; - // (undocumented) - path: string; - // (undocumented) - roles?: Array; - // (undocumented) - tokenUri?: string; -} +export { PathCredentials } // @public (undocumented) -export class QueryRequest { +export class QueryRequest extends BaseQueryRequest { constructor(query: string); // (undocumented) get query(): string; @@ -822,7 +625,7 @@ export class QueryRequest { } // @public (undocumented) -export class QueryResponse { +export class QueryResponse extends BaseQueryResponse { constructor(input: { fields: Array; errors: Array | null; @@ -833,23 +636,9 @@ export class QueryResponse { fields: Array; } -// @public (undocumented) -export interface QueryResponseType { - // (undocumented) - [key: string]: unknown; -} +export { QueryResponseType } -// @public (undocumented) -export enum RedactionType { - // (undocumented) - DEFAULT = "DEFAULT", - // (undocumented) - MASKED = "MASKED", - // (undocumented) - PLAIN_TEXT = "PLAIN_TEXT", - // (undocumented) - REDACTED = "REDACTED" -} +export { RedactionType } // @public (undocumented) export class ReidentifyTextOptions { @@ -884,34 +673,23 @@ export class ReidentifyTextResponse { processedText: string; } -// @public (undocumented) -export enum RequestMethod { - // (undocumented) - GET = "GET", - // (undocumented) - PATCH = "PATCH", - // (undocumented) - POST = "POST", - // (undocumented) - PUT = "PUT" -} +export { RequestMethod } -// @public (undocumented) -export type SignedDataTokensOptions = { - dataTokens: string[]; - timeToLive?: number; - ctx?: string | Record; - logLevel?: LogLevel; - tokenUri?: string; -}; +export { SignedDataTokensOptions } -// @public (undocumented) -export class Skyflow { +// Warning: (ae-forgotten-export) The symbol "VaultEntry" needs to be exported by the entry point index.d.ts +// +// @public +export class Skyflow extends BaseSkyflow { constructor(config: SkyflowConfig); // (undocumented) addConnectionConfig(config: ConnectionConfig): void; // (undocumented) - addVaultConfig(config: VaultConfig): void; + protected applyCredentials(): void; + // (undocumented) + protected applyLogLevel(): void; + // (undocumented) + protected buildVaultEntry(config: VaultConfig): VaultEntry; // Warning: (ae-forgotten-export) The symbol "ConnectionController" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -921,93 +699,28 @@ export class Skyflow { // (undocumented) detect(vaultId?: string): DetectController; // (undocumented) - getConnectionConfig(connectionId: string): ConnectionConfig | VaultConfig; - // (undocumented) - getLogLevel(): LogLevel; - // (undocumented) - getSkyflowCredentials(): Credentials | undefined; - // (undocumented) - getVaultConfig(vaultId: string): ConnectionConfig | VaultConfig; + getConnectionConfig(connectionId: string): ConnectionConfig; // (undocumented) removeConnectionConfig(connectionId: string): void; // (undocumented) - removeVaultConfig(vaultId: string): void; - // (undocumented) - setLogLevel(logLevel: LogLevel): void; - // (undocumented) updateConnectionConfig(config: ConnectionConfig): void; - // (undocumented) - updateLogLevel(logLevel: LogLevel): Skyflow; - // (undocumented) - updateSkyflowCredentials(credentials: Credentials): void; - // (undocumented) - updateVaultConfig(config: VaultConfig): void; // Warning: (ae-forgotten-export) The symbol "VaultController" needs to be exported by the entry point index.d.ts // // (undocumented) vault(vaultId?: string): VaultController; } -// @public (undocumented) -export interface SkyflowConfig { - // (undocumented) - connectionConfigs?: ConnectionConfig[]; - // (undocumented) - logLevel?: LogLevel; - // (undocumented) - skyflowCredentials?: Credentials; - // (undocumented) - vaultConfigs?: VaultConfig[]; -} +export { SkyflowConfig } -// @public (undocumented) -export class SkyflowError extends Error { - constructor(errorCode: ISkyflowError, args?: Array); - // Warning: (ae-forgotten-export) The symbol "ISkyflowError" needs to be exported by the entry point index.d.ts - // - // (undocumented) - error?: ISkyflowError; -} +export { SkyflowError } -// @public (undocumented) -export interface SkyflowRecordError { - // (undocumented) - error: string; - // (undocumented) - httpCode?: string | number | null; - // @deprecated (undocumented) - request_ID?: string | null; - // (undocumented) - requestId: string | null; - // (undocumented) - requestIndex?: number | null; - // (undocumented) - token?: string | null; -} +export { SkyflowRecordError } -// @public (undocumented) -export interface StringCredentials { - // (undocumented) - context?: string | Record; - // (undocumented) - credentialsString: string; - // (undocumented) - roles?: Array; - // (undocumented) - tokenUri?: string; -} +export { StringCredentials } -// @public (undocumented) -export interface StringKeyValueMapType { - // (undocumented) - [key: string]: object | string; -} +export { StringKeyValueMapType } -// @public (undocumented) -export interface TokenCredentials { - // (undocumented) - token: string; -} +export { TokenCredentials } // @public (undocumented) export class TokenFormat { @@ -1030,23 +743,17 @@ export class TokenFormat { } // @public (undocumented) -export class TokenizeRequest { +export class TokenizeRequest extends BaseTokenizeRequest { constructor(values: Array); // (undocumented) get values(): Array; set values(value: Array); } -// @public (undocumented) -export interface TokenizeRequestType { - // (undocumented) - columnGroup: string; - // (undocumented) - value: string; -} +export { TokenizeRequestType } // @public (undocumented) -export class TokenizeResponse { +export class TokenizeResponse extends BaseTokenizeResponse { constructor(input: { tokens: Array; errors: Array | null; @@ -1057,25 +764,9 @@ export class TokenizeResponse { tokens: Array; } -// @public (undocumented) -export enum TokenMode { - // (undocumented) - DISABLE = "DISABLE", - // (undocumented) - ENABLE = "ENABLE", - // (undocumented) - ENABLE_STRICT = "ENABLE_STRICT" -} +export { TokenMode } -// @public (undocumented) -export enum TokenType { - // (undocumented) - ENTITY_ONLY = "entity_only", - // (undocumented) - ENTITY_UNIQUE_COUNTER = "entity_unq_counter", - // (undocumented) - VAULT_TOKEN = "vault_token" -} +export { TokenType } // @public (undocumented) export class Transformations { @@ -1094,7 +785,7 @@ export class Transformations { } // @public (undocumented) -export class UpdateOptions { +export class UpdateOptions extends BaseOptions { constructor(); // (undocumented) getReturnTokens(): boolean | undefined; @@ -1111,7 +802,7 @@ export class UpdateOptions { } // @public (undocumented) -export class UpdateRequest { +export class UpdateRequest extends BaseUpdateRequest { constructor(table: string, data: Record); // (undocumented) get data(): Record; @@ -1122,7 +813,7 @@ export class UpdateRequest { } // @public (undocumented) -export class UpdateResponse { +export class UpdateResponse extends BaseUpdateResponse { constructor(input: { updatedField: InsertResponseType; errors: Array | null; @@ -1133,17 +824,7 @@ export class UpdateResponse { updatedField: InsertResponseType; } -// @public (undocumented) -export interface VaultConfig { - // (undocumented) - clusterId: string; - // (undocumented) - credentials?: Credentials; - // (undocumented) - env?: Env; - // (undocumented) - vaultId: string; -} +export { VaultConfig } // (No @packageDocumentation comment for this package) diff --git a/skyvault/babel.config.js b/skyvault/babel.config.js new file mode 100644 index 00000000..7bcee91b --- /dev/null +++ b/skyvault/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/skyvault/jest.config.js b/skyvault/jest.config.js new file mode 100644 index 00000000..5f07cf94 --- /dev/null +++ b/skyvault/jest.config.js @@ -0,0 +1,19 @@ +module.exports = { + transform: { + '^.+\\.(ts|tsx)?$': 'ts-jest', + '^.+\\.(js|jsx)$': 'babel-jest', + }, + verbose: true, + collectCoverage: true, + coveragePathIgnorePatterns: [ + "/node_modules/", + "src/ _generated_", + "src/.*/model/request", + "src/.*/model/response", + "src/.*/model/options", + ], + testEnvironmentOptions: { + "url": "https://skyflow-test.com" + }, + testEnvironment: "node", + }; diff --git a/skyvault/package.json b/skyvault/package.json new file mode 100644 index 00000000..ef0681de --- /dev/null +++ b/skyvault/package.json @@ -0,0 +1,60 @@ +{ + "name": "skyflow-node", + "version": "2.1.2", + "description": "Skyflow SDK for Node.js", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "tsc -b", + "build:dist": "tsup", + "test": "jest --coverage", + "contract-snapshot": "api-extractor run --local", + "contract-snapshot-verify": "api-extractor run" + }, + "repository": { + "type": "git", + "url": "https://github.com/skyflowapi/skyflow-node-sdk.git" + }, + "browser": { + "child_process": false + }, + "author": "", + "license": "MIT", + "keywords": [ + "skyflow", + "privacy", + "api", + "security", + "vault", + "PII", + "tokenization" + ], + "dependencies": { + "dotenv": "16.6.1", + "form-data": "4.0.6", + "form-data-encoder": "4.1.0", + "formdata-node": "6.0.3", + "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" + }, + "devDependencies": { + "skyflow-common": "*" + } +} diff --git a/src/ _generated_/rest/Client.ts b/skyvault/src/ _generated_/rest/Client.ts similarity index 100% rename from src/ _generated_/rest/Client.ts rename to skyvault/src/ _generated_/rest/Client.ts diff --git a/src/ _generated_/rest/api/errors/BadRequestError.ts b/skyvault/src/ _generated_/rest/api/errors/BadRequestError.ts similarity index 100% rename from src/ _generated_/rest/api/errors/BadRequestError.ts rename to skyvault/src/ _generated_/rest/api/errors/BadRequestError.ts diff --git a/skyvault/src/ _generated_/rest/api/errors/InternalServerError.ts b/skyvault/src/ _generated_/rest/api/errors/InternalServerError.ts new file mode 100644 index 00000000..4141147e --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/errors/InternalServerError.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index"; +import * as Skyflow from "../index"; +import * as core from "../../core"; + +export class InternalServerError extends errors.SkyflowError { + constructor(body: Skyflow.ErrorResponse, rawResponse?: core.RawResponse) { + super({ + message: "InternalServerError", + statusCode: 500, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, InternalServerError.prototype); + } +} diff --git a/src/ _generated_/rest/api/errors/NotFoundError.ts b/skyvault/src/ _generated_/rest/api/errors/NotFoundError.ts similarity index 100% rename from src/ _generated_/rest/api/errors/NotFoundError.ts rename to skyvault/src/ _generated_/rest/api/errors/NotFoundError.ts diff --git a/src/ _generated_/rest/api/errors/UnauthorizedError.ts b/skyvault/src/ _generated_/rest/api/errors/UnauthorizedError.ts similarity index 100% rename from src/ _generated_/rest/api/errors/UnauthorizedError.ts rename to skyvault/src/ _generated_/rest/api/errors/UnauthorizedError.ts diff --git a/src/ _generated_/rest/api/errors/index.ts b/skyvault/src/ _generated_/rest/api/errors/index.ts similarity index 100% rename from src/ _generated_/rest/api/errors/index.ts rename to skyvault/src/ _generated_/rest/api/errors/index.ts diff --git a/src/ _generated_/rest/api/index.ts b/skyvault/src/ _generated_/rest/api/index.ts similarity index 100% rename from src/ _generated_/rest/api/index.ts rename to skyvault/src/ _generated_/rest/api/index.ts diff --git a/src/ _generated_/rest/api/resources/audit/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/audit/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/client/Client.ts diff --git a/src/ _generated_/rest/api/resources/strings/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/audit/client/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/client/index.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/client/index.ts diff --git a/src/ _generated_/rest/api/resources/audit/client/requests/AuditServiceListAuditEventsRequest.ts b/skyvault/src/ _generated_/rest/api/resources/audit/client/requests/AuditServiceListAuditEventsRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/client/requests/AuditServiceListAuditEventsRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/client/requests/AuditServiceListAuditEventsRequest.ts diff --git a/src/ _generated_/rest/api/resources/audit/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/audit/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/client/requests/index.ts diff --git a/src/ _generated_/rest/api/resources/files/index.ts b/skyvault/src/ _generated_/rest/api/resources/audit/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/index.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/index.ts diff --git a/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsActionType.ts b/skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsActionType.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsActionType.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsActionType.ts diff --git a/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAccessType.ts b/skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAccessType.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAccessType.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAccessType.ts diff --git a/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextActorType.ts b/skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextActorType.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextActorType.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextActorType.ts diff --git a/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAuthMode.ts b/skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAuthMode.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAuthMode.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsContextAuthMode.ts diff --git a/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsResourceType.ts b/skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsResourceType.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsResourceType.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestFilterOpsResourceType.ts diff --git a/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestSortOpsOrderBy.ts b/skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestSortOpsOrderBy.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestSortOpsOrderBy.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/types/AuditServiceListAuditEventsRequestSortOpsOrderBy.ts diff --git a/src/ _generated_/rest/api/resources/audit/types/index.ts b/skyvault/src/ _generated_/rest/api/resources/audit/types/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/audit/types/index.ts rename to skyvault/src/ _generated_/rest/api/resources/audit/types/index.ts diff --git a/src/ _generated_/rest/api/resources/authentication/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/authentication/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/authentication/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/authentication/client/Client.ts diff --git a/src/ _generated_/rest/api/resources/tokens/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/authentication/client/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/tokens/client/index.ts rename to skyvault/src/ _generated_/rest/api/resources/authentication/client/index.ts diff --git a/src/ _generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts b/skyvault/src/ _generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/authentication/client/requests/V1GetAuthTokenRequest.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/authentication/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/authentication/client/requests/index.ts new file mode 100644 index 00000000..635fca22 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/authentication/client/requests/index.ts @@ -0,0 +1 @@ +export { type V1GetAuthTokenRequest } from "./V1GetAuthTokenRequest"; diff --git a/src/ _generated_/rest/api/resources/tokens/index.ts b/skyvault/src/ _generated_/rest/api/resources/authentication/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/tokens/index.ts rename to skyvault/src/ _generated_/rest/api/resources/authentication/index.ts diff --git a/src/ _generated_/rest/api/resources/binLookup/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/binLookup/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/binLookup/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/binLookup/client/Client.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/binLookup/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/binLookup/client/index.ts new file mode 100644 index 00000000..415726b7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/binLookup/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/src/ _generated_/rest/api/resources/binLookup/client/requests/V1BinListRequest.ts b/skyvault/src/ _generated_/rest/api/resources/binLookup/client/requests/V1BinListRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/binLookup/client/requests/V1BinListRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/binLookup/client/requests/V1BinListRequest.ts diff --git a/src/ _generated_/rest/api/resources/binLookup/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/binLookup/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/binLookup/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/binLookup/client/requests/index.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/binLookup/index.ts b/skyvault/src/ _generated_/rest/api/resources/binLookup/index.ts new file mode 100644 index 00000000..5ec76921 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/binLookup/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/src/ _generated_/rest/api/resources/files/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/Client.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/files/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/index.ts new file mode 100644 index 00000000..415726b7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/files/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyAudioRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyAudioRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyAudioRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyAudioRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyDocumentRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyDocumentRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyDocumentRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyDocumentRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyFileRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyFileRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyFileRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyFileRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyImageRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyImageRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyImageRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyImageRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPdfRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPdfRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPdfRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPdfRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPresentationRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPresentationRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPresentationRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyPresentationRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifySpreadsheetRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifySpreadsheetRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifySpreadsheetRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifySpreadsheetRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyStructuredTextRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyStructuredTextRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyStructuredTextRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyStructuredTextRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyTextRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyTextRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/DeidentifyTextRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/DeidentifyTextRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/GetRunRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/GetRunRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/GetRunRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/GetRunRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/ReidentifyFileRequest.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/ReidentifyFileRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/ReidentifyFileRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/ReidentifyFileRequest.ts diff --git a/src/ _generated_/rest/api/resources/files/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/files/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/files/client/requests/index.ts diff --git a/src/ _generated_/rest/api/resources/records/index.ts b/skyvault/src/ _generated_/rest/api/resources/files/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/index.ts rename to skyvault/src/ _generated_/rest/api/resources/files/index.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestOutputTranscription.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestOutputTranscription.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestOutputTranscription.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyAudioRequestOutputTranscription.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyDocumentRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyFileRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestMaskingMethod.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestMaskingMethod.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestMaskingMethod.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyImageRequestMaskingMethod.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyPdfRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyPdfRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyPdfRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyPdfRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyPresentationRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifySpreadsheetRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyStructuredTextRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/DeidentifyTextRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyTextRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/DeidentifyTextRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/DeidentifyTextRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFile.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFile.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFile.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFile.ts diff --git a/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFileDataFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFileDataFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFileDataFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFileDataFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFormat.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/ReidentifyFileRequestFormat.ts diff --git a/src/ _generated_/rest/api/resources/files/types/index.ts b/skyvault/src/ _generated_/rest/api/resources/files/types/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/files/types/index.ts rename to skyvault/src/ _generated_/rest/api/resources/files/types/index.ts diff --git a/src/ _generated_/rest/api/resources/guardrails/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/guardrails/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/guardrails/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/guardrails/client/Client.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/guardrails/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/guardrails/client/index.ts new file mode 100644 index 00000000..415726b7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/guardrails/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/src/ _generated_/rest/api/resources/guardrails/client/requests/CheckGuardrailsRequest.ts b/skyvault/src/ _generated_/rest/api/resources/guardrails/client/requests/CheckGuardrailsRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/guardrails/client/requests/CheckGuardrailsRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/guardrails/client/requests/CheckGuardrailsRequest.ts diff --git a/src/ _generated_/rest/api/resources/guardrails/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/guardrails/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/guardrails/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/guardrails/client/requests/index.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/guardrails/index.ts b/skyvault/src/ _generated_/rest/api/resources/guardrails/index.ts new file mode 100644 index 00000000..5ec76921 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/guardrails/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/src/ _generated_/rest/api/resources/index.ts b/skyvault/src/ _generated_/rest/api/resources/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/index.ts rename to skyvault/src/ _generated_/rest/api/resources/index.ts diff --git a/src/ _generated_/rest/api/resources/query/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/query/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/query/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/query/client/Client.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/query/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/query/client/index.ts new file mode 100644 index 00000000..415726b7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/query/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/src/ _generated_/rest/api/resources/query/client/requests/QueryServiceExecuteQueryBody.ts b/skyvault/src/ _generated_/rest/api/resources/query/client/requests/QueryServiceExecuteQueryBody.ts similarity index 100% rename from src/ _generated_/rest/api/resources/query/client/requests/QueryServiceExecuteQueryBody.ts rename to skyvault/src/ _generated_/rest/api/resources/query/client/requests/QueryServiceExecuteQueryBody.ts diff --git a/src/ _generated_/rest/api/resources/query/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/query/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/query/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/query/client/requests/index.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/query/index.ts b/skyvault/src/ _generated_/rest/api/resources/query/index.ts new file mode 100644 index 00000000..5ec76921 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/query/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/src/ _generated_/rest/api/resources/records/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/Client.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/records/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/index.ts new file mode 100644 index 00000000..415726b7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/records/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/src/ _generated_/rest/api/resources/records/client/requests/FileServiceUploadFileRequest.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/FileServiceUploadFileRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/FileServiceUploadFileRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/FileServiceUploadFileRequest.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBatchOperationBody.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBatchOperationBody.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBatchOperationBody.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBatchOperationBody.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkDeleteRecordBody.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkDeleteRecordBody.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkDeleteRecordBody.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkDeleteRecordBody.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkGetRecordRequest.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkGetRecordRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkGetRecordRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceBulkGetRecordRequest.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceGetRecordRequest.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceGetRecordRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/RecordServiceGetRecordRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceGetRecordRequest.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceInsertRecordBody.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceInsertRecordBody.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/RecordServiceInsertRecordBody.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceInsertRecordBody.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceUpdateRecordBody.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceUpdateRecordBody.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/RecordServiceUpdateRecordBody.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/RecordServiceUpdateRecordBody.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/UploadFileV2Request.ts diff --git a/src/ _generated_/rest/api/resources/records/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/records/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/records/client/requests/index.ts diff --git a/src/ _generated_/rest/api/resources/strings/index.ts b/skyvault/src/ _generated_/rest/api/resources/records/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/index.ts rename to skyvault/src/ _generated_/rest/api/resources/records/index.ts diff --git a/src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestOrderBy.ts b/skyvault/src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestOrderBy.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestOrderBy.ts rename to skyvault/src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestOrderBy.ts diff --git a/src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestRedaction.ts b/skyvault/src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestRedaction.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestRedaction.ts rename to skyvault/src/ _generated_/rest/api/resources/records/types/RecordServiceBulkGetRecordRequestRedaction.ts diff --git a/src/ _generated_/rest/api/resources/records/types/RecordServiceGetRecordRequestRedaction.ts b/skyvault/src/ _generated_/rest/api/resources/records/types/RecordServiceGetRecordRequestRedaction.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/types/RecordServiceGetRecordRequestRedaction.ts rename to skyvault/src/ _generated_/rest/api/resources/records/types/RecordServiceGetRecordRequestRedaction.ts diff --git a/src/ _generated_/rest/api/resources/records/types/index.ts b/skyvault/src/ _generated_/rest/api/resources/records/types/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/records/types/index.ts rename to skyvault/src/ _generated_/rest/api/resources/records/types/index.ts diff --git a/src/ _generated_/rest/api/resources/strings/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/strings/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/strings/client/Client.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/strings/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/strings/client/index.ts new file mode 100644 index 00000000..415726b7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/strings/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/src/ _generated_/rest/api/resources/strings/client/requests/DeidentifyStringRequest.ts b/skyvault/src/ _generated_/rest/api/resources/strings/client/requests/DeidentifyStringRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/client/requests/DeidentifyStringRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/strings/client/requests/DeidentifyStringRequest.ts diff --git a/src/ _generated_/rest/api/resources/strings/client/requests/ReidentifyStringRequest.ts b/skyvault/src/ _generated_/rest/api/resources/strings/client/requests/ReidentifyStringRequest.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/client/requests/ReidentifyStringRequest.ts rename to skyvault/src/ _generated_/rest/api/resources/strings/client/requests/ReidentifyStringRequest.ts diff --git a/src/ _generated_/rest/api/resources/strings/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/strings/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/strings/client/requests/index.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/strings/index.ts b/skyvault/src/ _generated_/rest/api/resources/strings/index.ts new file mode 100644 index 00000000..c9240f83 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/strings/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./client"; diff --git a/src/ _generated_/rest/api/resources/strings/types/ReidentifyStringRequestFormat.ts b/skyvault/src/ _generated_/rest/api/resources/strings/types/ReidentifyStringRequestFormat.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/types/ReidentifyStringRequestFormat.ts rename to skyvault/src/ _generated_/rest/api/resources/strings/types/ReidentifyStringRequestFormat.ts diff --git a/src/ _generated_/rest/api/resources/strings/types/index.ts b/skyvault/src/ _generated_/rest/api/resources/strings/types/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/strings/types/index.ts rename to skyvault/src/ _generated_/rest/api/resources/strings/types/index.ts diff --git a/src/ _generated_/rest/api/resources/tokens/client/Client.ts b/skyvault/src/ _generated_/rest/api/resources/tokens/client/Client.ts similarity index 100% rename from src/ _generated_/rest/api/resources/tokens/client/Client.ts rename to skyvault/src/ _generated_/rest/api/resources/tokens/client/Client.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/tokens/client/index.ts b/skyvault/src/ _generated_/rest/api/resources/tokens/client/index.ts new file mode 100644 index 00000000..415726b7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/tokens/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/src/ _generated_/rest/api/resources/tokens/client/requests/V1DetokenizePayload.ts b/skyvault/src/ _generated_/rest/api/resources/tokens/client/requests/V1DetokenizePayload.ts similarity index 100% rename from src/ _generated_/rest/api/resources/tokens/client/requests/V1DetokenizePayload.ts rename to skyvault/src/ _generated_/rest/api/resources/tokens/client/requests/V1DetokenizePayload.ts diff --git a/src/ _generated_/rest/api/resources/tokens/client/requests/V1TokenizePayload.ts b/skyvault/src/ _generated_/rest/api/resources/tokens/client/requests/V1TokenizePayload.ts similarity index 100% rename from src/ _generated_/rest/api/resources/tokens/client/requests/V1TokenizePayload.ts rename to skyvault/src/ _generated_/rest/api/resources/tokens/client/requests/V1TokenizePayload.ts diff --git a/src/ _generated_/rest/api/resources/tokens/client/requests/index.ts b/skyvault/src/ _generated_/rest/api/resources/tokens/client/requests/index.ts similarity index 100% rename from src/ _generated_/rest/api/resources/tokens/client/requests/index.ts rename to skyvault/src/ _generated_/rest/api/resources/tokens/client/requests/index.ts diff --git a/skyvault/src/ _generated_/rest/api/resources/tokens/index.ts b/skyvault/src/ _generated_/rest/api/resources/tokens/index.ts new file mode 100644 index 00000000..5ec76921 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/resources/tokens/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/src/ _generated_/rest/api/types/AllowRegex.ts b/skyvault/src/ _generated_/rest/api/types/AllowRegex.ts similarity index 100% rename from src/ _generated_/rest/api/types/AllowRegex.ts rename to skyvault/src/ _generated_/rest/api/types/AllowRegex.ts diff --git a/src/ _generated_/rest/api/types/AuditEventAuditResourceType.ts b/skyvault/src/ _generated_/rest/api/types/AuditEventAuditResourceType.ts similarity index 100% rename from src/ _generated_/rest/api/types/AuditEventAuditResourceType.ts rename to skyvault/src/ _generated_/rest/api/types/AuditEventAuditResourceType.ts diff --git a/src/ _generated_/rest/api/types/AuditEventContext.ts b/skyvault/src/ _generated_/rest/api/types/AuditEventContext.ts similarity index 100% rename from src/ _generated_/rest/api/types/AuditEventContext.ts rename to skyvault/src/ _generated_/rest/api/types/AuditEventContext.ts diff --git a/src/ _generated_/rest/api/types/AuditEventData.ts b/skyvault/src/ _generated_/rest/api/types/AuditEventData.ts similarity index 100% rename from src/ _generated_/rest/api/types/AuditEventData.ts rename to skyvault/src/ _generated_/rest/api/types/AuditEventData.ts diff --git a/src/ _generated_/rest/api/types/AuditEventHttpInfo.ts b/skyvault/src/ _generated_/rest/api/types/AuditEventHttpInfo.ts similarity index 100% rename from src/ _generated_/rest/api/types/AuditEventHttpInfo.ts rename to skyvault/src/ _generated_/rest/api/types/AuditEventHttpInfo.ts diff --git a/src/ _generated_/rest/api/types/BatchRecordMethod.ts b/skyvault/src/ _generated_/rest/api/types/BatchRecordMethod.ts similarity index 100% rename from src/ _generated_/rest/api/types/BatchRecordMethod.ts rename to skyvault/src/ _generated_/rest/api/types/BatchRecordMethod.ts diff --git a/src/ _generated_/rest/api/types/CheckGuardrailsResponse.ts b/skyvault/src/ _generated_/rest/api/types/CheckGuardrailsResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/CheckGuardrailsResponse.ts rename to skyvault/src/ _generated_/rest/api/types/CheckGuardrailsResponse.ts diff --git a/src/ _generated_/rest/api/types/CheckGuardrailsResponseValidation.ts b/skyvault/src/ _generated_/rest/api/types/CheckGuardrailsResponseValidation.ts similarity index 100% rename from src/ _generated_/rest/api/types/CheckGuardrailsResponseValidation.ts rename to skyvault/src/ _generated_/rest/api/types/CheckGuardrailsResponseValidation.ts diff --git a/src/ _generated_/rest/api/types/ConfigurationId.ts b/skyvault/src/ _generated_/rest/api/types/ConfigurationId.ts similarity index 100% rename from src/ _generated_/rest/api/types/ConfigurationId.ts rename to skyvault/src/ _generated_/rest/api/types/ConfigurationId.ts diff --git a/src/ _generated_/rest/api/types/ContextAccessType.ts b/skyvault/src/ _generated_/rest/api/types/ContextAccessType.ts similarity index 100% rename from src/ _generated_/rest/api/types/ContextAccessType.ts rename to skyvault/src/ _generated_/rest/api/types/ContextAccessType.ts diff --git a/src/ _generated_/rest/api/types/ContextAuthMode.ts b/skyvault/src/ _generated_/rest/api/types/ContextAuthMode.ts similarity index 100% rename from src/ _generated_/rest/api/types/ContextAuthMode.ts rename to skyvault/src/ _generated_/rest/api/types/ContextAuthMode.ts diff --git a/src/ _generated_/rest/api/types/DeidentifyFileOutput.ts b/skyvault/src/ _generated_/rest/api/types/DeidentifyFileOutput.ts similarity index 100% rename from src/ _generated_/rest/api/types/DeidentifyFileOutput.ts rename to skyvault/src/ _generated_/rest/api/types/DeidentifyFileOutput.ts diff --git a/src/ _generated_/rest/api/types/DeidentifyFileOutputProcessedFileType.ts b/skyvault/src/ _generated_/rest/api/types/DeidentifyFileOutputProcessedFileType.ts similarity index 100% rename from src/ _generated_/rest/api/types/DeidentifyFileOutputProcessedFileType.ts rename to skyvault/src/ _generated_/rest/api/types/DeidentifyFileOutputProcessedFileType.ts diff --git a/src/ _generated_/rest/api/types/DeidentifyFileResponse.ts b/skyvault/src/ _generated_/rest/api/types/DeidentifyFileResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/DeidentifyFileResponse.ts rename to skyvault/src/ _generated_/rest/api/types/DeidentifyFileResponse.ts diff --git a/src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts b/skyvault/src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts rename to skyvault/src/ _generated_/rest/api/types/DeidentifyStatusResponse.ts diff --git a/src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts b/skyvault/src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts similarity index 100% rename from src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts rename to skyvault/src/ _generated_/rest/api/types/DeidentifyStatusResponseOutputType.ts diff --git a/src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts b/skyvault/src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts similarity index 100% rename from src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts rename to skyvault/src/ _generated_/rest/api/types/DeidentifyStatusResponseStatus.ts diff --git a/src/ _generated_/rest/api/types/DeidentifyStringResponse.ts b/skyvault/src/ _generated_/rest/api/types/DeidentifyStringResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/DeidentifyStringResponse.ts rename to skyvault/src/ _generated_/rest/api/types/DeidentifyStringResponse.ts diff --git a/src/ _generated_/rest/api/types/DetectedEntity.ts b/skyvault/src/ _generated_/rest/api/types/DetectedEntity.ts similarity index 100% rename from src/ _generated_/rest/api/types/DetectedEntity.ts rename to skyvault/src/ _generated_/rest/api/types/DetectedEntity.ts diff --git a/src/ _generated_/rest/api/types/DetokenizeRecordResponseValueType.ts b/skyvault/src/ _generated_/rest/api/types/DetokenizeRecordResponseValueType.ts similarity index 100% rename from src/ _generated_/rest/api/types/DetokenizeRecordResponseValueType.ts rename to skyvault/src/ _generated_/rest/api/types/DetokenizeRecordResponseValueType.ts diff --git a/src/ _generated_/rest/api/types/EntityLocation.ts b/skyvault/src/ _generated_/rest/api/types/EntityLocation.ts similarity index 100% rename from src/ _generated_/rest/api/types/EntityLocation.ts rename to skyvault/src/ _generated_/rest/api/types/EntityLocation.ts diff --git a/src/ _generated_/rest/api/types/EntityType.ts b/skyvault/src/ _generated_/rest/api/types/EntityType.ts similarity index 100% rename from src/ _generated_/rest/api/types/EntityType.ts rename to skyvault/src/ _generated_/rest/api/types/EntityType.ts diff --git a/src/ _generated_/rest/api/types/EntityTypes.ts b/skyvault/src/ _generated_/rest/api/types/EntityTypes.ts similarity index 100% rename from src/ _generated_/rest/api/types/EntityTypes.ts rename to skyvault/src/ _generated_/rest/api/types/EntityTypes.ts diff --git a/skyvault/src/ _generated_/rest/api/types/ErrorResponse.ts b/skyvault/src/ _generated_/rest/api/types/ErrorResponse.ts new file mode 100644 index 00000000..822dd151 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/types/ErrorResponse.ts @@ -0,0 +1,9 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface ErrorResponse { + error: Skyflow.ErrorResponseError; +} diff --git a/src/ _generated_/rest/api/types/ErrorResponseError.ts b/skyvault/src/ _generated_/rest/api/types/ErrorResponseError.ts similarity index 100% rename from src/ _generated_/rest/api/types/ErrorResponseError.ts rename to skyvault/src/ _generated_/rest/api/types/ErrorResponseError.ts diff --git a/skyvault/src/ _generated_/rest/api/types/GooglerpcStatus.ts b/skyvault/src/ _generated_/rest/api/types/GooglerpcStatus.ts new file mode 100644 index 00000000..70e20691 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/types/GooglerpcStatus.ts @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Skyflow from "../index"; + +export interface GooglerpcStatus { + code?: number; + message?: string; + details?: Skyflow.ProtobufAny[]; +} diff --git a/skyvault/src/ _generated_/rest/api/types/ProtobufAny.ts b/skyvault/src/ _generated_/rest/api/types/ProtobufAny.ts new file mode 100644 index 00000000..9f90a613 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/types/ProtobufAny.ts @@ -0,0 +1,7 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface ProtobufAny { + "@type"?: string; +} diff --git a/src/ _generated_/rest/api/types/RedactionEnumRedaction.ts b/skyvault/src/ _generated_/rest/api/types/RedactionEnumRedaction.ts similarity index 100% rename from src/ _generated_/rest/api/types/RedactionEnumRedaction.ts rename to skyvault/src/ _generated_/rest/api/types/RedactionEnumRedaction.ts diff --git a/src/ _generated_/rest/api/types/ReidentifyFileResponse.ts b/skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/ReidentifyFileResponse.ts rename to skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponse.ts diff --git a/src/ _generated_/rest/api/types/ReidentifyFileResponseOutput.ts b/skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponseOutput.ts similarity index 100% rename from src/ _generated_/rest/api/types/ReidentifyFileResponseOutput.ts rename to skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponseOutput.ts diff --git a/src/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts b/skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts similarity index 100% rename from src/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts rename to skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponseOutputType.ts diff --git a/src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts b/skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts similarity index 100% rename from src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts rename to skyvault/src/ _generated_/rest/api/types/ReidentifyFileResponseStatus.ts diff --git a/src/ _generated_/rest/api/types/ReidentifyStringResponse.ts b/skyvault/src/ _generated_/rest/api/types/ReidentifyStringResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/ReidentifyStringResponse.ts rename to skyvault/src/ _generated_/rest/api/types/ReidentifyStringResponse.ts diff --git a/src/ _generated_/rest/api/types/RequestActionType.ts b/skyvault/src/ _generated_/rest/api/types/RequestActionType.ts similarity index 100% rename from src/ _generated_/rest/api/types/RequestActionType.ts rename to skyvault/src/ _generated_/rest/api/types/RequestActionType.ts diff --git a/src/ _generated_/rest/api/types/ResourceId.ts b/skyvault/src/ _generated_/rest/api/types/ResourceId.ts similarity index 100% rename from src/ _generated_/rest/api/types/ResourceId.ts rename to skyvault/src/ _generated_/rest/api/types/ResourceId.ts diff --git a/src/ _generated_/rest/api/types/RestrictRegex.ts b/skyvault/src/ _generated_/rest/api/types/RestrictRegex.ts similarity index 100% rename from src/ _generated_/rest/api/types/RestrictRegex.ts rename to skyvault/src/ _generated_/rest/api/types/RestrictRegex.ts diff --git a/src/ _generated_/rest/api/types/TokenType.ts b/skyvault/src/ _generated_/rest/api/types/TokenType.ts similarity index 100% rename from src/ _generated_/rest/api/types/TokenType.ts rename to skyvault/src/ _generated_/rest/api/types/TokenType.ts diff --git a/src/ _generated_/rest/api/types/TokenTypeDefault.ts b/skyvault/src/ _generated_/rest/api/types/TokenTypeDefault.ts similarity index 100% rename from src/ _generated_/rest/api/types/TokenTypeDefault.ts rename to skyvault/src/ _generated_/rest/api/types/TokenTypeDefault.ts diff --git a/src/ _generated_/rest/api/types/TokenTypeWithoutVault.ts b/skyvault/src/ _generated_/rest/api/types/TokenTypeWithoutVault.ts similarity index 100% rename from src/ _generated_/rest/api/types/TokenTypeWithoutVault.ts rename to skyvault/src/ _generated_/rest/api/types/TokenTypeWithoutVault.ts diff --git a/src/ _generated_/rest/api/types/TokenTypeWithoutVaultDefault.ts b/skyvault/src/ _generated_/rest/api/types/TokenTypeWithoutVaultDefault.ts similarity index 100% rename from src/ _generated_/rest/api/types/TokenTypeWithoutVaultDefault.ts rename to skyvault/src/ _generated_/rest/api/types/TokenTypeWithoutVaultDefault.ts diff --git a/src/ _generated_/rest/api/types/Transformations.ts b/skyvault/src/ _generated_/rest/api/types/Transformations.ts similarity index 100% rename from src/ _generated_/rest/api/types/Transformations.ts rename to skyvault/src/ _generated_/rest/api/types/Transformations.ts diff --git a/src/ _generated_/rest/api/types/TransformationsShiftDates.ts b/skyvault/src/ _generated_/rest/api/types/TransformationsShiftDates.ts similarity index 100% rename from src/ _generated_/rest/api/types/TransformationsShiftDates.ts rename to skyvault/src/ _generated_/rest/api/types/TransformationsShiftDates.ts diff --git a/src/ _generated_/rest/api/types/TransformationsShiftDatesEntityTypesItem.ts b/skyvault/src/ _generated_/rest/api/types/TransformationsShiftDatesEntityTypesItem.ts similarity index 100% rename from src/ _generated_/rest/api/types/TransformationsShiftDatesEntityTypesItem.ts rename to skyvault/src/ _generated_/rest/api/types/TransformationsShiftDatesEntityTypesItem.ts diff --git a/src/ _generated_/rest/api/types/UploadFileV2Response.ts b/skyvault/src/ _generated_/rest/api/types/UploadFileV2Response.ts similarity index 100% rename from src/ _generated_/rest/api/types/UploadFileV2Response.ts rename to skyvault/src/ _generated_/rest/api/types/UploadFileV2Response.ts diff --git a/src/ _generated_/rest/api/types/Uuid.ts b/skyvault/src/ _generated_/rest/api/types/Uuid.ts similarity index 100% rename from src/ _generated_/rest/api/types/Uuid.ts rename to skyvault/src/ _generated_/rest/api/types/Uuid.ts diff --git a/src/ _generated_/rest/api/types/V1AuditAfterOptions.ts b/skyvault/src/ _generated_/rest/api/types/V1AuditAfterOptions.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1AuditAfterOptions.ts rename to skyvault/src/ _generated_/rest/api/types/V1AuditAfterOptions.ts diff --git a/src/ _generated_/rest/api/types/V1AuditEventResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1AuditEventResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1AuditEventResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1AuditEventResponse.ts diff --git a/src/ _generated_/rest/api/types/V1AuditResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1AuditResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1AuditResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1AuditResponse.ts diff --git a/src/ _generated_/rest/api/types/V1AuditResponseEvent.ts b/skyvault/src/ _generated_/rest/api/types/V1AuditResponseEvent.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1AuditResponseEvent.ts rename to skyvault/src/ _generated_/rest/api/types/V1AuditResponseEvent.ts diff --git a/src/ _generated_/rest/api/types/V1AuditResponseEventRequest.ts b/skyvault/src/ _generated_/rest/api/types/V1AuditResponseEventRequest.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1AuditResponseEventRequest.ts rename to skyvault/src/ _generated_/rest/api/types/V1AuditResponseEventRequest.ts diff --git a/src/ _generated_/rest/api/types/V1BatchOperationResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1BatchOperationResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1BatchOperationResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1BatchOperationResponse.ts diff --git a/src/ _generated_/rest/api/types/V1BatchRecord.ts b/skyvault/src/ _generated_/rest/api/types/V1BatchRecord.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1BatchRecord.ts rename to skyvault/src/ _generated_/rest/api/types/V1BatchRecord.ts diff --git a/src/ _generated_/rest/api/types/V1BinListResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1BinListResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1BinListResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1BinListResponse.ts diff --git a/src/ _generated_/rest/api/types/V1BulkDeleteRecordResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1BulkDeleteRecordResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1BulkDeleteRecordResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1BulkDeleteRecordResponse.ts diff --git a/src/ _generated_/rest/api/types/V1BulkGetRecordResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1BulkGetRecordResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1BulkGetRecordResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1BulkGetRecordResponse.ts diff --git a/src/ _generated_/rest/api/types/V1Byot.ts b/skyvault/src/ _generated_/rest/api/types/V1Byot.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1Byot.ts rename to skyvault/src/ _generated_/rest/api/types/V1Byot.ts diff --git a/src/ _generated_/rest/api/types/V1Card.ts b/skyvault/src/ _generated_/rest/api/types/V1Card.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1Card.ts rename to skyvault/src/ _generated_/rest/api/types/V1Card.ts diff --git a/src/ _generated_/rest/api/types/V1DeleteFileResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1DeleteFileResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1DeleteFileResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1DeleteFileResponse.ts diff --git a/src/ _generated_/rest/api/types/V1DeleteRecordResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1DeleteRecordResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1DeleteRecordResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1DeleteRecordResponse.ts diff --git a/src/ _generated_/rest/api/types/V1DetokenizeRecordRequest.ts b/skyvault/src/ _generated_/rest/api/types/V1DetokenizeRecordRequest.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1DetokenizeRecordRequest.ts rename to skyvault/src/ _generated_/rest/api/types/V1DetokenizeRecordRequest.ts diff --git a/src/ _generated_/rest/api/types/V1DetokenizeRecordResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1DetokenizeRecordResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1DetokenizeRecordResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1DetokenizeRecordResponse.ts diff --git a/src/ _generated_/rest/api/types/V1DetokenizeResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1DetokenizeResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1DetokenizeResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1DetokenizeResponse.ts diff --git a/src/ _generated_/rest/api/types/V1FieldRecords.ts b/skyvault/src/ _generated_/rest/api/types/V1FieldRecords.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1FieldRecords.ts rename to skyvault/src/ _generated_/rest/api/types/V1FieldRecords.ts diff --git a/src/ _generated_/rest/api/types/V1FileAvScanStatus.ts b/skyvault/src/ _generated_/rest/api/types/V1FileAvScanStatus.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1FileAvScanStatus.ts rename to skyvault/src/ _generated_/rest/api/types/V1FileAvScanStatus.ts diff --git a/skyvault/src/ _generated_/rest/api/types/V1GetAuthTokenResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1GetAuthTokenResponse.ts new file mode 100644 index 00000000..860942f1 --- /dev/null +++ b/skyvault/src/ _generated_/rest/api/types/V1GetAuthTokenResponse.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface V1GetAuthTokenResponse { + /** AccessToken. */ + accessToken?: string; + /** TokenType : Bearer. */ + tokenType?: string; +} diff --git a/src/ _generated_/rest/api/types/V1GetFileScanStatusResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1GetFileScanStatusResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1GetFileScanStatusResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1GetFileScanStatusResponse.ts diff --git a/src/ _generated_/rest/api/types/V1GetQueryResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1GetQueryResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1GetQueryResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1GetQueryResponse.ts diff --git a/src/ _generated_/rest/api/types/V1InsertRecordResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1InsertRecordResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1InsertRecordResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1InsertRecordResponse.ts diff --git a/src/ _generated_/rest/api/types/V1MemberType.ts b/skyvault/src/ _generated_/rest/api/types/V1MemberType.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1MemberType.ts rename to skyvault/src/ _generated_/rest/api/types/V1MemberType.ts diff --git a/src/ _generated_/rest/api/types/V1RecordMetaProperties.ts b/skyvault/src/ _generated_/rest/api/types/V1RecordMetaProperties.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1RecordMetaProperties.ts rename to skyvault/src/ _generated_/rest/api/types/V1RecordMetaProperties.ts diff --git a/src/ _generated_/rest/api/types/V1TokenizeRecordRequest.ts b/skyvault/src/ _generated_/rest/api/types/V1TokenizeRecordRequest.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1TokenizeRecordRequest.ts rename to skyvault/src/ _generated_/rest/api/types/V1TokenizeRecordRequest.ts diff --git a/src/ _generated_/rest/api/types/V1TokenizeRecordResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1TokenizeRecordResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1TokenizeRecordResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1TokenizeRecordResponse.ts diff --git a/src/ _generated_/rest/api/types/V1TokenizeResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1TokenizeResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1TokenizeResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1TokenizeResponse.ts diff --git a/src/ _generated_/rest/api/types/V1UpdateRecordResponse.ts b/skyvault/src/ _generated_/rest/api/types/V1UpdateRecordResponse.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1UpdateRecordResponse.ts rename to skyvault/src/ _generated_/rest/api/types/V1UpdateRecordResponse.ts diff --git a/src/ _generated_/rest/api/types/V1VaultFieldMapping.ts b/skyvault/src/ _generated_/rest/api/types/V1VaultFieldMapping.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1VaultFieldMapping.ts rename to skyvault/src/ _generated_/rest/api/types/V1VaultFieldMapping.ts diff --git a/src/ _generated_/rest/api/types/V1VaultSchemaConfig.ts b/skyvault/src/ _generated_/rest/api/types/V1VaultSchemaConfig.ts similarity index 100% rename from src/ _generated_/rest/api/types/V1VaultSchemaConfig.ts rename to skyvault/src/ _generated_/rest/api/types/V1VaultSchemaConfig.ts diff --git a/src/ _generated_/rest/api/types/VaultId.ts b/skyvault/src/ _generated_/rest/api/types/VaultId.ts similarity index 100% rename from src/ _generated_/rest/api/types/VaultId.ts rename to skyvault/src/ _generated_/rest/api/types/VaultId.ts diff --git a/src/ _generated_/rest/api/types/index.ts b/skyvault/src/ _generated_/rest/api/types/index.ts similarity index 100% rename from src/ _generated_/rest/api/types/index.ts rename to skyvault/src/ _generated_/rest/api/types/index.ts diff --git a/skyvault/src/ _generated_/rest/core/auth/BasicAuth.ts b/skyvault/src/ _generated_/rest/core/auth/BasicAuth.ts new file mode 100644 index 00000000..146df215 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/auth/BasicAuth.ts @@ -0,0 +1,31 @@ +import { Base64 } from "js-base64"; + +export interface BasicAuth { + username: string; + password: string; +} + +const BASIC_AUTH_HEADER_PREFIX = /^Basic /i; + +export const BasicAuth = { + toAuthorizationHeader: (basicAuth: BasicAuth | undefined): string | undefined => { + if (basicAuth == null) { + return undefined; + } + const token = Base64.encode(`${basicAuth.username}:${basicAuth.password}`); + return `Basic ${token}`; + }, + fromAuthorizationHeader: (header: string): BasicAuth => { + const credentials = header.replace(BASIC_AUTH_HEADER_PREFIX, ""); + const decoded = Base64.decode(credentials); + const [username, password] = decoded.split(":", 2); + + if (username == null || password == null) { + throw new Error("Invalid basic auth"); + } + return { + username, + password, + }; + }, +}; diff --git a/skyvault/src/ _generated_/rest/core/auth/BearerToken.ts b/skyvault/src/ _generated_/rest/core/auth/BearerToken.ts new file mode 100644 index 00000000..fe987fc9 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/auth/BearerToken.ts @@ -0,0 +1,15 @@ +export type BearerToken = string; + +const BEARER_AUTH_HEADER_PREFIX = /^Bearer /i; + +export const BearerToken = { + toAuthorizationHeader: (token: BearerToken | undefined): string | undefined => { + if (token == null) { + return undefined; + } + return `Bearer ${token}`; + }, + fromAuthorizationHeader: (header: string): BearerToken => { + return header.replace(BEARER_AUTH_HEADER_PREFIX, "").trim() as BearerToken; + }, +}; diff --git a/skyvault/src/ _generated_/rest/core/auth/index.ts b/skyvault/src/ _generated_/rest/core/auth/index.ts new file mode 100644 index 00000000..ee293b34 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/auth/index.ts @@ -0,0 +1,2 @@ +export { BasicAuth } from "./BasicAuth"; +export { BearerToken } from "./BearerToken"; diff --git a/skyvault/src/ _generated_/rest/core/fetcher/APIResponse.ts b/skyvault/src/ _generated_/rest/core/fetcher/APIResponse.ts new file mode 100644 index 00000000..6335291b --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/APIResponse.ts @@ -0,0 +1,23 @@ +import { RawResponse } from "./RawResponse"; + +/** + * The response of an API call. + * It is a successful response or a failed response. + */ +export type APIResponse = SuccessfulResponse | FailedResponse; + +export interface SuccessfulResponse { + ok: true; + body: T; + /** + * @deprecated Use `rawResponse` instead + */ + headers?: Record; + rawResponse: RawResponse; +} + +export interface FailedResponse { + ok: false; + error: T; + rawResponse: RawResponse; +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/Fetcher.ts b/skyvault/src/ _generated_/rest/core/fetcher/Fetcher.ts new file mode 100644 index 00000000..336ee10b --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/Fetcher.ts @@ -0,0 +1,151 @@ +import { toJson } from "../json"; +import { APIResponse } from "./APIResponse"; +import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse"; +import { createRequestUrl } from "./createRequestUrl"; +import { getFetchFn } from "./getFetchFn"; +import { getRequestBody } from "./getRequestBody"; +import { getResponseBody } from "./getResponseBody"; +import { makeRequest } from "./makeRequest"; +import { requestWithRetries } from "./requestWithRetries"; + +export type FetchFunction = (args: Fetcher.Args) => Promise>; + +export declare namespace Fetcher { + export interface Args { + url: string; + method: string; + contentType?: string; + headers?: Record; + queryParameters?: Record; + body?: unknown; + timeoutMs?: number; + maxRetries?: number; + withCredentials?: boolean; + abortSignal?: AbortSignal; + requestType?: "json" | "file" | "bytes"; + responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer"; + duplex?: "half"; + } + + export type Error = FailedStatusCodeError | NonJsonError | TimeoutError | UnknownError; + + export interface FailedStatusCodeError { + reason: "status-code"; + statusCode: number; + body: unknown; + } + + export interface NonJsonError { + reason: "non-json"; + statusCode: number; + rawBody: string; + } + + export interface TimeoutError { + reason: "timeout"; + } + + export interface UnknownError { + reason: "unknown"; + errorMessage: string; + } +} + +export async function fetcherImpl(args: Fetcher.Args): Promise> { + const headers: Record = {}; + if (args.body !== undefined && args.contentType != null) { + headers["Content-Type"] = args.contentType; + } + + if (args.headers != null) { + for (const [key, value] of Object.entries(args.headers)) { + if (value != null) { + headers[key] = value; + } + } + } + + const url = createRequestUrl(args.url, args.queryParameters); + const requestBody: BodyInit | undefined = await getRequestBody({ + body: args.body, + type: args.requestType === "json" ? "json" : "other", + }); + const fetchFn = await getFetchFn(); + + try { + const response = await requestWithRetries( + async () => + makeRequest( + fetchFn, + url, + args.method, + headers, + requestBody, + args.timeoutMs, + args.abortSignal, + args.withCredentials, + args.duplex, + ), + args.maxRetries, + ); + const responseBody = await getResponseBody(response, args.responseType); + + if (response.status >= 200 && response.status < 400) { + return { + ok: true, + body: responseBody as R, + headers: response.headers, + rawResponse: toRawResponse(response), + }; + } else { + return { + ok: false, + error: { + reason: "status-code", + statusCode: response.status, + body: responseBody, + }, + rawResponse: toRawResponse(response), + }; + } + } catch (error) { + if (args.abortSignal != null && args.abortSignal.aborted) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: "The user aborted a request", + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error && error.name === "AbortError") { + return { + ok: false, + error: { + reason: "timeout", + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: error.message, + }, + rawResponse: unknownRawResponse, + }; + } + + return { + ok: false, + error: { + reason: "unknown", + errorMessage: toJson(error), + }, + rawResponse: unknownRawResponse, + }; + } +} + +export const fetcher: FetchFunction = fetcherImpl; diff --git a/skyvault/src/ _generated_/rest/core/fetcher/HttpResponsePromise.ts b/skyvault/src/ _generated_/rest/core/fetcher/HttpResponsePromise.ts new file mode 100644 index 00000000..6b0bc4fc --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/HttpResponsePromise.ts @@ -0,0 +1,116 @@ +import { WithRawResponse } from "./RawResponse"; + +/** + * A promise that returns the parsed response and lets you retrieve the raw response too. + */ +export class HttpResponsePromise extends Promise { + private innerPromise: Promise>; + private unwrappedPromise: Promise | undefined; + + private constructor(promise: Promise>) { + // Initialize with a no-op to avoid premature parsing + super((resolve) => { + resolve(undefined as unknown as T); + }); + this.innerPromise = promise; + } + + /** + * Creates an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @param args - Arguments to pass to the function. + * @returns An `HttpResponsePromise` instance. + */ + public static fromFunction Promise>, T>( + fn: F, + ...args: Parameters + ): HttpResponsePromise { + return new HttpResponsePromise(fn(...args)); + } + + /** + * Creates a function that returns an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @returns A function that returns an `HttpResponsePromise` instance. + */ + public static interceptFunction< + F extends (...args: never[]) => Promise>, + T = Awaited>["data"], + >(fn: F): (...args: Parameters) => HttpResponsePromise { + return (...args: Parameters): HttpResponsePromise => { + return HttpResponsePromise.fromPromise(fn(...args)); + }; + } + + /** + * Creates an `HttpResponsePromise` from an existing promise. + * + * @param promise - A promise resolving to a `WithRawResponse` object. + * @returns An `HttpResponsePromise` instance. + */ + public static fromPromise(promise: Promise>): HttpResponsePromise { + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from an executor function. + * + * @param executor - A function that takes resolve and reject callbacks to create a promise. + * @returns An `HttpResponsePromise` instance. + */ + public static fromExecutor( + executor: (resolve: (value: WithRawResponse) => void, reject: (reason?: unknown) => void) => void, + ): HttpResponsePromise { + const promise = new Promise>(executor); + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from a resolved result. + * + * @param result - A `WithRawResponse` object to resolve immediately. + * @returns An `HttpResponsePromise` instance. + */ + public static fromResult(result: WithRawResponse): HttpResponsePromise { + const promise = Promise.resolve(result); + return new HttpResponsePromise(promise); + } + + private unwrap(): Promise { + if (!this.unwrappedPromise) { + this.unwrappedPromise = this.innerPromise.then(({ data }) => data); + } + return this.unwrappedPromise; + } + + /** @inheritdoc */ + public override then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ): Promise { + return this.unwrap().then(onfulfilled, onrejected); + } + + /** @inheritdoc */ + public override catch( + onrejected?: ((reason: unknown) => TResult | PromiseLike) | null, + ): Promise { + return this.unwrap().catch(onrejected); + } + + /** @inheritdoc */ + public override finally(onfinally?: (() => void) | null): Promise { + return this.unwrap().finally(onfinally); + } + + /** + * Retrieves the data and raw response. + * + * @returns A promise resolving to a `WithRawResponse` object. + */ + public async withRawResponse(): Promise> { + return await this.innerPromise; + } +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/RawResponse.ts b/skyvault/src/ _generated_/rest/core/fetcher/RawResponse.ts new file mode 100644 index 00000000..c975f23c --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/RawResponse.ts @@ -0,0 +1,59 @@ +/** + * The raw response from the fetch call excluding the body. + */ +export type RawResponse = Omit< + { + [K in keyof Response as Response[K] extends Function ? never : K]: Response[K]; // strips out functions + }, + "ok" | "body" | "bodyUsed" +>; // strips out body and bodyUsed + +/** + * A raw response indicating that the request was aborted. + */ +export const abortRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 499, + statusText: "Client Closed Request", + type: "error", + url: "", +} as const; + +/** + * A raw response indicating an unknown error. + */ +export const unknownRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 0, + statusText: "Unknown Error", + type: "error", + url: "", +} as const; + +/** + * Converts a `RawResponse` object into a `RawResponse` by extracting its properties, + * excluding the `body` and `bodyUsed` fields. + * + * @param response - The `RawResponse` object to convert. + * @returns A `RawResponse` object containing the extracted properties of the input response. + */ +export function toRawResponse(response: Response): RawResponse { + return { + headers: response.headers, + redirected: response.redirected, + status: response.status, + statusText: response.statusText, + type: response.type, + url: response.url, + }; +} + +/** + * Creates a `RawResponse` from a standard `Response` object. + */ +export interface WithRawResponse { + readonly data: T; + readonly rawResponse: RawResponse; +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/Supplier.ts b/skyvault/src/ _generated_/rest/core/fetcher/Supplier.ts new file mode 100644 index 00000000..867c931c --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/Supplier.ts @@ -0,0 +1,11 @@ +export type Supplier = T | Promise | (() => T | Promise); + +export const Supplier = { + get: async (supplier: Supplier): Promise => { + if (typeof supplier === "function") { + return (supplier as () => T)(); + } else { + return supplier; + } + }, +}; diff --git a/skyvault/src/ _generated_/rest/core/fetcher/createRequestUrl.ts b/skyvault/src/ _generated_/rest/core/fetcher/createRequestUrl.ts new file mode 100644 index 00000000..f1157ce7 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/createRequestUrl.ts @@ -0,0 +1,10 @@ +import qs from "qs"; + +export function createRequestUrl( + baseUrl: string, + queryParameters?: Record, +): string { + return Object.keys(queryParameters ?? {}).length > 0 + ? `${baseUrl}?${qs.stringify(queryParameters, { arrayFormat: "repeat" })}` + : baseUrl; +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/getFetchFn.ts b/skyvault/src/ _generated_/rest/core/fetcher/getFetchFn.ts new file mode 100644 index 00000000..9fd9bfc4 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/getFetchFn.ts @@ -0,0 +1,25 @@ +import { RUNTIME } from "../runtime"; + +/** + * Returns a fetch function based on the runtime + */ +export async function getFetchFn(): Promise { + // In Node.js 18+ environments, use native fetch + if (RUNTIME.type === "node" && RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + return fetch; + } + + // In Node.js 18 or lower environments, the SDK always uses`node-fetch`. + if (RUNTIME.type === "node") { + return (await import("node-fetch")).default as any; + } + + // Otherwise the SDK uses global fetch if available, + // and falls back to node-fetch. + if (typeof fetch == "function") { + return fetch; + } + + // Defaults to node `node-fetch` if global fetch isn't available + return (await import("node-fetch")).default as any; +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/getHeader.ts b/skyvault/src/ _generated_/rest/core/fetcher/getHeader.ts new file mode 100644 index 00000000..50f922b0 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/getHeader.ts @@ -0,0 +1,8 @@ +export function getHeader(headers: Record, header: string): string | undefined { + for (const [headerKey, headerValue] of Object.entries(headers)) { + if (headerKey.toLowerCase() === header.toLowerCase()) { + return headerValue; + } + } + return undefined; +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/getRequestBody.ts b/skyvault/src/ _generated_/rest/core/fetcher/getRequestBody.ts new file mode 100644 index 00000000..fce5589c --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/getRequestBody.ts @@ -0,0 +1,16 @@ +import { toJson } from "../json"; + +export declare namespace GetRequestBody { + interface Args { + body: unknown; + type: "json" | "file" | "bytes" | "other"; + } +} + +export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { + if (type.includes("json")) { + return toJson(body); + } else { + return body as BodyInit; + } +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/getResponseBody.ts b/skyvault/src/ _generated_/rest/core/fetcher/getResponseBody.ts new file mode 100644 index 00000000..d046e6ea --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/getResponseBody.ts @@ -0,0 +1,34 @@ +import { chooseStreamWrapper } from "./stream-wrappers/chooseStreamWrapper"; + +export async function getResponseBody(response: Response, responseType?: string): Promise { + if (response.body != null && responseType === "blob") { + return await response.blob(); + } else if (response.body != null && responseType === "arrayBuffer") { + return await response.arrayBuffer(); + } else if (response.body != null && responseType === "sse") { + return response.body; + } else if (response.body != null && responseType === "streaming") { + return chooseStreamWrapper(response.body); + } else if (response.body != null && responseType === "text") { + return await response.text(); + } else { + const text = await response.text(); + if (text.length > 0) { + try { + let responseBody = JSON.parse(text); + return responseBody; + } catch (err) { + return { + ok: false, + error: { + reason: "non-json", + statusCode: response.status, + rawBody: text, + }, + }; + } + } else { + return undefined; + } + } +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/index.ts b/skyvault/src/ _generated_/rest/core/fetcher/index.ts new file mode 100644 index 00000000..249f5176 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/index.ts @@ -0,0 +1,8 @@ +export type { APIResponse } from "./APIResponse"; +export { fetcher } from "./Fetcher"; +export type { Fetcher, FetchFunction } from "./Fetcher"; +export { getHeader } from "./getHeader"; +export { Supplier } from "./Supplier"; +export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse"; +export type { RawResponse, WithRawResponse } from "./RawResponse"; +export { HttpResponsePromise } from "./HttpResponsePromise"; diff --git a/skyvault/src/ _generated_/rest/core/fetcher/makeRequest.ts b/skyvault/src/ _generated_/rest/core/fetcher/makeRequest.ts new file mode 100644 index 00000000..1af42bb9 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/makeRequest.ts @@ -0,0 +1,44 @@ +import { anySignal, getTimeoutSignal } from "./signals"; + +export const makeRequest = async ( + fetchFn: (url: string, init: RequestInit) => Promise, + url: string, + method: string, + headers: Record, + requestBody: BodyInit | undefined, + timeoutMs?: number, + abortSignal?: AbortSignal, + withCredentials?: boolean, + duplex?: "half", +): Promise => { + const signals: AbortSignal[] = []; + + // Add timeout signal + let timeoutAbortId: NodeJS.Timeout | undefined = undefined; + if (timeoutMs != null) { + const { signal, abortId } = getTimeoutSignal(timeoutMs); + timeoutAbortId = abortId; + signals.push(signal); + } + + // Add arbitrary signal + if (abortSignal != null) { + signals.push(abortSignal); + } + let newSignals = anySignal(signals); + const response = await fetchFn(url, { + method: method, + headers, + body: requestBody, + signal: newSignals, + credentials: withCredentials ? "include" : undefined, + // @ts-ignore + duplex, + }); + + if (timeoutAbortId != null) { + clearTimeout(timeoutAbortId); + } + + return response; +}; diff --git a/skyvault/src/ _generated_/rest/core/fetcher/requestWithRetries.ts b/skyvault/src/ _generated_/rest/core/fetcher/requestWithRetries.ts new file mode 100644 index 00000000..add3cce0 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/requestWithRetries.ts @@ -0,0 +1,33 @@ +const INITIAL_RETRY_DELAY = 1000; // in milliseconds +const MAX_RETRY_DELAY = 60000; // in milliseconds +const DEFAULT_MAX_RETRIES = 2; +const JITTER_FACTOR = 0.2; // 20% random jitter + +function addJitter(delay: number): number { + // Generate a random value between -JITTER_FACTOR and +JITTER_FACTOR + const jitterMultiplier = 1 + (Math.random() * 2 - 1) * JITTER_FACTOR; + return delay * jitterMultiplier; +} + +export async function requestWithRetries( + requestFn: () => Promise, + maxRetries: number = DEFAULT_MAX_RETRIES, +): Promise { + let response: Response = await requestFn(); + + for (let i = 0; i < maxRetries; ++i) { + if ([408, 429].includes(response.status) || response.status >= 500) { + // Calculate base delay using exponential backoff (in milliseconds) + const baseDelay = Math.min(INITIAL_RETRY_DELAY * Math.pow(2, i), MAX_RETRY_DELAY); + + // Add jitter to the delay + const delayWithJitter = addJitter(baseDelay); + + await new Promise((resolve) => setTimeout(resolve, delayWithJitter)); + response = await requestFn(); + } else { + break; + } + } + return response!; +} diff --git a/skyvault/src/ _generated_/rest/core/fetcher/signals.ts b/skyvault/src/ _generated_/rest/core/fetcher/signals.ts new file mode 100644 index 00000000..a8d32a2e --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/signals.ts @@ -0,0 +1,38 @@ +const TIMEOUT = "timeout"; + +export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: NodeJS.Timeout } { + const controller = new AbortController(); + const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); + return { signal: controller.signal, abortId }; +} + +/** + * Returns an abort signal that is getting aborted when + * at least one of the specified abort signals is aborted. + * + * Requires at least node.js 18. + */ +export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { + // Allowing signals to be passed either as array + // of signals or as multiple arguments. + const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as AbortSignal[]; + + const controller = new AbortController(); + + for (const signal of signals) { + if (signal.aborted) { + // Exiting early if one of the signals + // is already aborted. + controller.abort((signal as any)?.reason); + break; + } + + // Listening for signals and removing the listeners + // when at least one symbol is aborted. + signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { + signal: controller.signal, + }); + } + + return controller.signal; +} diff --git a/src/ _generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts b/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts similarity index 100% rename from src/ _generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts rename to skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts diff --git a/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts b/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts new file mode 100644 index 00000000..23c01a1a --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts @@ -0,0 +1,107 @@ +import type { Readable, Writable } from "readable-stream"; + +import { EventCallback, StreamWrapper } from "./chooseStreamWrapper"; + +export class NodePre18StreamWrapper implements StreamWrapper { + private readableStream: Readable; + private encoding: string | undefined; + + constructor(readableStream: Readable) { + this.readableStream = readableStream; + } + + public on(event: string, callback: EventCallback): void { + this.readableStream.on(event, callback); + } + + public off(event: string, callback: EventCallback): void { + this.readableStream.off(event, callback); + } + + public pipe(dest: Writable): Writable { + this.readableStream.pipe(dest); + return dest; + } + + public pipeTo(dest: Writable): Writable { + return this.pipe(dest); + } + + public unpipe(dest?: Writable): void { + if (dest) { + this.readableStream.unpipe(dest); + } else { + this.readableStream.unpipe(); + } + } + + public destroy(error?: Error): void { + this.readableStream.destroy(error); + } + + public pause(): void { + this.readableStream.pause(); + } + + public resume(): void { + this.readableStream.resume(); + } + + public get isPaused(): boolean { + return this.readableStream.isPaused(); + } + + public async read(): Promise { + return new Promise((resolve, reject) => { + const chunk = this.readableStream.read(); + if (chunk) { + resolve(chunk); + } else { + this.readableStream.once("readable", () => { + const chunk = this.readableStream.read(); + resolve(chunk); + }); + this.readableStream.once("error", reject); + } + }); + } + + public setEncoding(encoding?: string): void { + this.readableStream.setEncoding(encoding as BufferEncoding); + this.encoding = encoding; + } + + public async text(): Promise { + const chunks: Uint8Array[] = []; + const encoder = new TextEncoder(); + this.readableStream.setEncoding((this.encoding || "utf-8") as BufferEncoding); + + for await (const chunk of this.readableStream) { + chunks.push(encoder.encode(chunk)); + } + + const decoder = new TextDecoder(this.encoding || "utf-8"); + return decoder.decode(Buffer.concat(chunks)); + } + + public async json(): Promise { + const text = await this.text(); + return JSON.parse(text); + } + + public [Symbol.asyncIterator](): AsyncIterableIterator { + const readableStream = this.readableStream; + const iterator = readableStream[Symbol.asyncIterator](); + + // Create and return an async iterator that yields buffers + return { + async next(): Promise> { + const { value, done } = await iterator.next(); + return { value: value as Buffer, done }; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; + } +} diff --git a/src/ _generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts b/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts similarity index 100% rename from src/ _generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts rename to skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts diff --git a/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts b/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts new file mode 100644 index 00000000..8c7492fc --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/fetcher/stream-wrappers/chooseStreamWrapper.ts @@ -0,0 +1,34 @@ +import type { Readable } from "readable-stream"; + +import { RUNTIME } from "../../runtime"; + +export type EventCallback = (data?: any) => void; + +export interface StreamWrapper { + setEncoding(encoding?: string): void; + on(event: string, callback: EventCallback): void; + off(event: string, callback: EventCallback): void; + pipe(dest: WritableStream): WritableStream; + pipeTo(dest: WritableStream): WritableStream; + unpipe(dest?: WritableStream): void; + destroy(error?: Error): void; + pause(): void; + resume(): void; + get isPaused(): boolean; + read(): Promise; + text(): Promise; + json(): Promise; + [Symbol.asyncIterator](): AsyncIterableIterator; +} + +export async function chooseStreamWrapper(responseBody: any): Promise>> { + if (RUNTIME.type === "node" && RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + return new (await import("./Node18UniversalStreamWrapper")).Node18UniversalStreamWrapper( + responseBody as ReadableStream, + ); + } else if (RUNTIME.type !== "node" && typeof fetch === "function") { + return new (await import("./UndiciStreamWrapper")).UndiciStreamWrapper(responseBody as ReadableStream); + } else { + return new (await import("./NodePre18StreamWrapper")).NodePre18StreamWrapper(responseBody as Readable); + } +} diff --git a/src/ _generated_/rest/core/form-data-utils/FormDataWrapper.ts b/skyvault/src/ _generated_/rest/core/form-data-utils/FormDataWrapper.ts similarity index 100% rename from src/ _generated_/rest/core/form-data-utils/FormDataWrapper.ts rename to skyvault/src/ _generated_/rest/core/form-data-utils/FormDataWrapper.ts diff --git a/src/ _generated_/rest/core/form-data-utils/encodeAsFormParameter.ts b/skyvault/src/ _generated_/rest/core/form-data-utils/encodeAsFormParameter.ts similarity index 100% rename from src/ _generated_/rest/core/form-data-utils/encodeAsFormParameter.ts rename to skyvault/src/ _generated_/rest/core/form-data-utils/encodeAsFormParameter.ts diff --git a/src/ _generated_/rest/core/form-data-utils/index.ts b/skyvault/src/ _generated_/rest/core/form-data-utils/index.ts similarity index 100% rename from src/ _generated_/rest/core/form-data-utils/index.ts rename to skyvault/src/ _generated_/rest/core/form-data-utils/index.ts diff --git a/src/ _generated_/rest/core/index.ts b/skyvault/src/ _generated_/rest/core/index.ts similarity index 100% rename from src/ _generated_/rest/core/index.ts rename to skyvault/src/ _generated_/rest/core/index.ts diff --git a/skyvault/src/ _generated_/rest/core/json.ts b/skyvault/src/ _generated_/rest/core/json.ts new file mode 100644 index 00000000..c052f324 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/json.ts @@ -0,0 +1,27 @@ +/** + * Serialize a value to JSON + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + * @returns JSON string + */ +export const toJson = ( + value: unknown, + replacer?: (this: unknown, key: string, value: unknown) => unknown, + space?: string | number, +): string => { + return JSON.stringify(value, replacer, space); +}; + +/** + * Parse JSON string to object, array, or other type + * @param text A valid JSON string. + * @param reviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is. + * @returns Parsed object, array, or other type + */ +export function fromJson( + text: string, + reviver?: (this: unknown, key: string, value: unknown) => unknown, +): T { + return JSON.parse(text, reviver); +} diff --git a/skyvault/src/ _generated_/rest/core/runtime/index.ts b/skyvault/src/ _generated_/rest/core/runtime/index.ts new file mode 100644 index 00000000..5c76dbb1 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/runtime/index.ts @@ -0,0 +1 @@ +export { RUNTIME } from "./runtime"; diff --git a/skyvault/src/ _generated_/rest/core/runtime/runtime.ts b/skyvault/src/ _generated_/rest/core/runtime/runtime.ts new file mode 100644 index 00000000..a9750175 --- /dev/null +++ b/skyvault/src/ _generated_/rest/core/runtime/runtime.ts @@ -0,0 +1,131 @@ +interface DenoGlobal { + version: { + deno: string; + }; +} + +interface BunGlobal { + version: string; +} + +declare const Deno: DenoGlobal | undefined; +declare const Bun: BunGlobal | undefined; +declare const EdgeRuntime: string | undefined; + +/** + * A constant that indicates which environment and version the SDK is running in. + */ +export const RUNTIME: Runtime = evaluateRuntime(); + +export interface Runtime { + type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd" | "edge-runtime"; + version?: string; + parsedVersion?: number; +} + +function evaluateRuntime(): Runtime { + /** + * A constant that indicates whether the environment the code is running is a Web Browser. + */ + const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; + if (isBrowser) { + return { + type: "browser", + version: window.navigator.userAgent, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Cloudflare. + * https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent + */ + const isCloudflare = typeof globalThis !== "undefined" && globalThis?.navigator?.userAgent === "Cloudflare-Workers"; + if (isCloudflare) { + return { + type: "workerd", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Edge Runtime. + * https://vercel.com/docs/functions/runtimes/edge-runtime#check-if-you're-running-on-the-edge-runtime + */ + const isEdgeRuntime = typeof EdgeRuntime === "string"; + if (isEdgeRuntime) { + return { + type: "edge-runtime", + }; + } + + /** + * A constant that indicates whether the environment the code is running is a Web Worker. + */ + const isWebWorker = + typeof self === "object" && + // @ts-ignore + typeof self?.importScripts === "function" && + (self.constructor?.name === "DedicatedWorkerGlobalScope" || + self.constructor?.name === "ServiceWorkerGlobalScope" || + self.constructor?.name === "SharedWorkerGlobalScope"); + if (isWebWorker) { + return { + type: "web-worker", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Deno. + * FYI Deno spoofs process.versions.node, see https://deno.land/std@0.177.0/node/process.ts?s=versions + */ + const isDeno = + typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"; + if (isDeno) { + return { + type: "deno", + version: Deno.version.deno, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Bun.sh. + */ + const isBun = typeof Bun !== "undefined" && typeof Bun.version !== "undefined"; + if (isBun) { + return { + type: "bun", + version: Bun.version, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Node.JS. + */ + const isNode = + typeof process !== "undefined" && + "version" in process && + !!process.version && + "versions" in process && + !!process.versions?.node; + if (isNode) { + return { + type: "node", + version: process.versions.node, + parsedVersion: Number(process.versions.node.split(".")[0]), + }; + } + + /** + * A constant that indicates whether the environment the code is running is in React-Native. + * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js + */ + const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; + if (isReactNative) { + return { + type: "react-native", + }; + } + + return { + type: "unknown", + }; +} diff --git a/src/ _generated_/rest/environments.ts b/skyvault/src/ _generated_/rest/environments.ts similarity index 100% rename from src/ _generated_/rest/environments.ts rename to skyvault/src/ _generated_/rest/environments.ts diff --git a/skyvault/src/ _generated_/rest/errors/SkyflowError.ts b/skyvault/src/ _generated_/rest/errors/SkyflowError.ts new file mode 100644 index 00000000..003768c2 --- /dev/null +++ b/skyvault/src/ _generated_/rest/errors/SkyflowError.ts @@ -0,0 +1,55 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../core"; +import { toJson } from "../core/json"; + +export class SkyflowError extends Error { + public readonly statusCode?: number; + public readonly body?: unknown; + public readonly rawResponse?: core.RawResponse; + + constructor({ + message, + statusCode, + body, + rawResponse, + }: { + message?: string; + statusCode?: number; + body?: unknown; + rawResponse?: core.RawResponse; + }) { + super(buildMessage({ message, statusCode, body })); + Object.setPrototypeOf(this, SkyflowError.prototype); + this.statusCode = statusCode; + this.body = body; + this.rawResponse = rawResponse; + } +} + +function buildMessage({ + message, + statusCode, + body, +}: { + message: string | undefined; + statusCode: number | undefined; + body: unknown | undefined; +}): string { + let lines: string[] = []; + if (message != null) { + lines.push(message); + } + + if (statusCode != null) { + lines.push(`Status code: ${statusCode.toString()}`); + } + + if (body != null) { + lines.push(`Body: ${toJson(body, undefined, 2)}`); + } + + return lines.join("\n"); +} diff --git a/skyvault/src/ _generated_/rest/errors/SkyflowTimeoutError.ts b/skyvault/src/ _generated_/rest/errors/SkyflowTimeoutError.ts new file mode 100644 index 00000000..75024f63 --- /dev/null +++ b/skyvault/src/ _generated_/rest/errors/SkyflowTimeoutError.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export class SkyflowTimeoutError extends Error { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, SkyflowTimeoutError.prototype); + } +} diff --git a/skyvault/src/ _generated_/rest/errors/index.ts b/skyvault/src/ _generated_/rest/errors/index.ts new file mode 100644 index 00000000..5567f00d --- /dev/null +++ b/skyvault/src/ _generated_/rest/errors/index.ts @@ -0,0 +1,2 @@ +export { SkyflowError } from "./SkyflowError"; +export { SkyflowTimeoutError } from "./SkyflowTimeoutError"; diff --git a/skyvault/src/ _generated_/rest/index.ts b/skyvault/src/ _generated_/rest/index.ts new file mode 100644 index 00000000..0706e084 --- /dev/null +++ b/skyvault/src/ _generated_/rest/index.ts @@ -0,0 +1,4 @@ +export * as Skyflow from "./api"; +export { SkyflowClient } from "./Client"; +export { SkyflowEnvironment } from "./environments"; +export { SkyflowError, SkyflowTimeoutError } from "./errors"; diff --git a/skyvault/src/ _generated_/rest/version.ts b/skyvault/src/ _generated_/rest/version.ts new file mode 100644 index 00000000..e3655fb3 --- /dev/null +++ b/skyvault/src/ _generated_/rest/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = "1.0.21"; diff --git a/src/index.ts b/skyvault/src/index.ts similarity index 91% rename from src/index.ts rename to skyvault/src/index.ts index a64396c4..a49d2ad2 100644 --- a/src/index.ts +++ b/skyvault/src/index.ts @@ -1,5 +1,5 @@ import Skyflow from './vault/skyflow'; -import { LogLevel, Env, RedactionType, RequestMethod, OrderByEnum, TokenMode, DetectEntities } from './utils'; +import { LogLevel, Env, RedactionType, RequestMethod, OrderByEnum, TokenMode, DetectEntities } from "skyflow-common"; import InsertRequest from './vault/model/request/insert'; import InsertOptions from './vault/model/options/insert'; import GetRequest from './vault/model/request/get'; @@ -11,12 +11,12 @@ import DeleteRequest from './vault/model/request/delete'; import UpdateRequest from './vault/model/request/update'; import FileUploadRequest from './vault/model/request/file-upload'; import QueryRequest from './vault/model/request/query'; -import Credentials from './vault/config/credentials'; -import { TokenCredentials, ApiKeyCredentials, PathCredentials, StringCredentials } from './vault/config/credentials'; +import { Credentials } from "skyflow-common"; +import { TokenCredentials, ApiKeyCredentials, PathCredentials, StringCredentials } from "skyflow-common"; import TokenizeRequest from './vault/model/request/tokenize'; import TokenizeResponse from './vault/model/response/tokenize'; -import { BearerTokenOptions, generateBearerToken, generateBearerTokenFromCreds, generateSignedDataTokens, generateSignedDataTokensFromCreds, GenerateTokenOptions, SignedDataTokensOptions } from './service-account'; -import { isExpired } from './utils/jwt-utils'; +import { BearerTokenOptions, generateBearerToken, generateBearerTokenFromCreds, generateSignedDataTokens, generateSignedDataTokensFromCreds, GenerateTokenOptions, SignedDataTokensOptions } from "skyflow-common"; +import { isExpired } from "skyflow-common"; import UpdateOptions from './vault/model/options/update'; import InvokeConnectionRequest from './vault/model/request/inkove'; import GetColumnRequest from './vault/model/request/get-column'; @@ -29,9 +29,9 @@ import FileUploadResponse from './vault/model/response/file-upload'; import QueryResponse from './vault/model/response/query'; import InvokeConnectionResponse from './vault/model/response/invoke/invoke'; import { SkyflowConfig, TokenizeRequestType, DetokenizeData, InsertResponseType, GetResponseData, QueryResponseType, IndexRange, FileInput, StringKeyValueMapType } from './vault/types'; -import VaultConfig from './vault/config/vault'; -import SkyflowError from './error'; -import ConnectionConfig from './vault/config/connection'; +import { VaultConfig } from "skyflow-common"; +import { SkyflowError } from "skyflow-common"; +import { ConnectionConfig } from "skyflow-common"; import DeidentifyTextRequest from './vault/model/request/deidentify-text'; import DeidentifyTextOptions from './vault/model/options/deidentify-text'; import DeidentifyTextResponse from './vault/model/response/deidentify-text'; @@ -44,9 +44,9 @@ import DeidentifyFileOptions from './vault/model/options/deidentify-file'; import DeidentifyFileRequest from './vault/model/request/deidentify-file'; import DeidentifyFileResponse from './vault/model/response/deidentify-file'; import GetDetectRunRequest from './vault/model/request/get-detect-run'; -import { TokenType, MaskingMethod, DetectOutputTranscription } from './utils'; +import { TokenType, MaskingMethod, DetectOutputTranscription } from "skyflow-common"; import { Bleep } from './vault/model/options/deidentify-file/bleep-audio'; -import { SkyflowRecordError } from './utils/index'; +import { SkyflowRecordError } from "skyflow-common"; export { Env, LogLevel, diff --git a/src/management/config/management/index.ts b/skyvault/src/management/config/management/index.ts similarity index 100% rename from src/management/config/management/index.ts rename to skyvault/src/management/config/management/index.ts diff --git a/src/management/controller/policy/index.ts b/skyvault/src/management/controller/policy/index.ts similarity index 100% rename from src/management/controller/policy/index.ts rename to skyvault/src/management/controller/policy/index.ts diff --git a/src/management/controller/role/index.ts b/skyvault/src/management/controller/role/index.ts similarity index 100% rename from src/management/controller/role/index.ts rename to skyvault/src/management/controller/role/index.ts diff --git a/src/management/controller/vault/index.ts b/skyvault/src/management/controller/vault/index.ts similarity index 100% rename from src/management/controller/vault/index.ts rename to skyvault/src/management/controller/vault/index.ts diff --git a/src/management/index.ts b/skyvault/src/management/index.ts similarity index 100% rename from src/management/index.ts rename to skyvault/src/management/index.ts diff --git a/src/management/model/request/create-vault/index.ts b/skyvault/src/management/model/request/create-vault/index.ts similarity index 100% rename from src/management/model/request/create-vault/index.ts rename to skyvault/src/management/model/request/create-vault/index.ts diff --git a/src/management/model/request/delete-policy/index.ts b/skyvault/src/management/model/request/delete-policy/index.ts similarity index 100% rename from src/management/model/request/delete-policy/index.ts rename to skyvault/src/management/model/request/delete-policy/index.ts diff --git a/src/management/model/response/create-vault/index.ts b/skyvault/src/management/model/response/create-vault/index.ts similarity index 100% rename from src/management/model/response/create-vault/index.ts rename to skyvault/src/management/model/response/create-vault/index.ts diff --git a/src/management/model/response/delete-policy/index.ts b/skyvault/src/management/model/response/delete-policy/index.ts similarity index 100% rename from src/management/model/response/delete-policy/index.ts rename to skyvault/src/management/model/response/delete-policy/index.ts diff --git a/src/management/skyflow/index.ts b/skyvault/src/management/skyflow/index.ts similarity index 100% rename from src/management/skyflow/index.ts rename to skyvault/src/management/skyflow/index.ts diff --git a/src/management/types/index.ts b/skyvault/src/management/types/index.ts similarity index 78% rename from src/management/types/index.ts rename to skyvault/src/management/types/index.ts index 7841e394..2ac72ae7 100644 --- a/src/management/types/index.ts +++ b/skyvault/src/management/types/index.ts @@ -1,4 +1,4 @@ -import { LogLevel } from "../../utils"; +import { LogLevel } from "skyflow-common"; import ManagementConfig from "../config/management"; export interface SkyflowConfig { diff --git a/src/utils/validations/index.ts b/skyvault/src/utils/validations/index.ts similarity index 69% rename from src/utils/validations/index.ts rename to skyvault/src/utils/validations/index.ts index 8123f0ef..f1ae580e 100644 --- a/src/utils/validations/index.ts +++ b/skyvault/src/utils/validations/index.ts @@ -1,10 +1,5 @@ -import { CONFIG, Env, HTTP_HEADER, isValidURL, LogLevel, MessageType, RequestMethod, OrderByEnum, parameterizedString, printLog, RedactionType, SKYFLOW, TokenMode, API_KEY } from ".."; +import { HTTP_HEADER, LogLevel, MessageType, RequestMethod, OrderByEnum, parameterizedString, printLog, RedactionType, SKYFLOW, TokenMode, SkyflowError, SKYFLOW_ERROR_CODE, logs, StringKeyValueMapType } from "skyflow-common"; import { V1Byot } from "../../ _generated_/rest/api"; -import SkyflowError from "../../error"; -import SKYFLOW_ERROR_CODE from "../../error/codes"; -import ConnectionConfig from "../../vault/config/connection"; -import Credentials, { ApiKeyCredentials, PathCredentials, StringCredentials, TokenCredentials } from "../../vault/config/credentials"; -import VaultConfig from "../../vault/config/vault"; import DetokenizeOptions from "../../vault/model/options/detokenize"; import GetOptions from "../../vault/model/options/get"; import InsertOptions from "../../vault/model/options/insert"; @@ -19,11 +14,8 @@ import InsertRequest from "../../vault/model/request/insert"; import QueryRequest from "../../vault/model/request/query"; import TokenizeRequest from "../../vault/model/request/tokenize"; import UpdateRequest from "../../vault/model/request/update"; -import { SkyflowConfig, StringKeyValueMapType } from "../../vault/types"; import * as fs from 'fs'; import * as path from 'path'; -import { isExpired } from "../jwt-utils"; -import logs from "../logs"; import FileUploadOptions from "../../vault/model/options/fileUpload"; import DeidentifyTextRequest from "../../vault/model/request/deidentify-text"; import DeidentifyTextOptions from "../../vault/model/options/deidentify-text"; @@ -36,10 +28,6 @@ import DeidentifyFileRequest from "../../vault/model/request/deidentify-file"; import { Bleep } from "../../vault/model/options/deidentify-file/bleep-audio"; import GetDetectRunRequest from "../../vault/model/request/get-detect-run"; -export function isEnv(value?: string): boolean { - return value !== undefined && Object.values(Env).includes(value as Env); -} - export function isRedactionType(value?: string): boolean { return value !== undefined && Object.values(RedactionType).includes(value as RedactionType); } @@ -56,372 +44,6 @@ export function isMethod(value?: string): boolean { return value !== undefined && Object.values(RequestMethod).includes(value as RequestMethod); } -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) - } -}; - function validateInsertInput(input: unknown, index: number): void { if (typeof input !== 'object' || input === null || Array.isArray(input)) { throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_RECORD_IN_INSERT, [index]); diff --git a/src/vault/client/index.ts b/skyvault/src/vault/client/index.ts similarity index 98% rename from src/vault/client/index.ts rename to skyvault/src/vault/client/index.ts index 29f856fd..7bc5946d 100644 --- a/src/vault/client/index.ts +++ b/skyvault/src/vault/client/index.ts @@ -4,12 +4,12 @@ import { Query } from "../../ _generated_/rest/api/resources/query/client/Client import { Records } from "../../ _generated_/rest/api/resources/records/client/Client"; import { Strings } from "../../ _generated_/rest/api/resources/strings/client/Client"; import { Tokens } from "../../ _generated_/rest/api/resources/tokens/client/Client"; -import SkyflowError from "../../error"; -import errorMessages from "../../error/messages"; -import { AuthInfo, AuthType, LogLevel, MessageType, printLog, TYPES, HTTP_HEADER, CONTENT_TYPE, BOOLEAN_STRING, HTTP_STATUS_CODE } from "../../utils/index"; -import { isExpired } from "../../utils/jwt-utils"; -import logs from "../../utils/logs"; -import Credentials from "../config/credentials"; +import { SkyflowError } from "skyflow-common"; +import { errorMessages } from "skyflow-common"; +import { AuthInfo, AuthType, LogLevel, MessageType, printLog, TYPES, HTTP_HEADER, CONTENT_TYPE, BOOLEAN_STRING, HTTP_STATUS_CODE } from "skyflow-common"; +import { isExpired } from "skyflow-common"; +import { logs } from "skyflow-common"; +import { Credentials } from "skyflow-common"; import { SkyflowApiErrorLegacy, SkyflowApiErrorNewFormat, SkyflowErrorData } from "../types"; class VaultClient { diff --git a/src/vault/controller/audit/index.ts b/skyvault/src/vault/controller/audit/index.ts similarity index 100% rename from src/vault/controller/audit/index.ts rename to skyvault/src/vault/controller/audit/index.ts diff --git a/src/vault/controller/binlookup/index.ts b/skyvault/src/vault/controller/binlookup/index.ts similarity index 100% rename from src/vault/controller/binlookup/index.ts rename to skyvault/src/vault/controller/binlookup/index.ts diff --git a/src/vault/controller/connections/index.ts b/skyvault/src/vault/controller/connections/index.ts similarity index 98% rename from src/vault/controller/connections/index.ts rename to skyvault/src/vault/controller/connections/index.ts index 0c3025fc..8f6b863b 100644 --- a/src/vault/controller/connections/index.ts +++ b/skyvault/src/vault/controller/connections/index.ts @@ -15,14 +15,14 @@ import { HTTP_HEADER, CONTENT_TYPE, objectToXML, -} from "../../../utils"; +} from "skyflow-common"; import InvokeConnectionRequest from "../../model/request/inkove"; -import logs from "../../../utils/logs"; +import { logs } from "skyflow-common"; import { validateInvokeConnectionRequest } from "../../../utils/validations"; import VaultClient from "../../client"; import InvokeConnectionResponse from "../../model/response/invoke/invoke"; -import SkyflowError from "../../../error"; -import SKYFLOW_ERROR_CODE from "../../../error/codes"; +import { SkyflowError } from "skyflow-common"; +import { SKYFLOW_ERROR_CODE } from "skyflow-common"; class ConnectionController { private client: VaultClient; diff --git a/src/vault/controller/detect/index.ts b/skyvault/src/vault/controller/detect/index.ts similarity index 99% rename from src/vault/controller/detect/index.ts rename to skyvault/src/vault/controller/detect/index.ts index 682b83f6..b1425a6f 100644 --- a/src/vault/controller/detect/index.ts +++ b/skyvault/src/vault/controller/detect/index.ts @@ -5,8 +5,8 @@ import { DeidentifyTextRequest as DeidentifyTextRequest2,DeidentifyAudioRequest, import { DeidentifyFileRequest as DeidentifyFileRequest2} from "../../../ _generated_/rest/api"; import { TokenType } from "../../../ _generated_/rest/api"; -import { DeidenitfyFileRequestTypes, generateSDKMetrics, getBearerToken, MessageType, parameterizedString, printLog, removeSDKVersion, SDK, TYPES, HTTP_HEADER, DETECT_STATUS, FILE_EXTENSION, FILE_FORMAT_TYPE, FILE_PROCESSING, ENCODING_TYPE } from "../../../utils"; -import logs from "../../../utils/logs"; +import { DeidenitfyFileRequestTypes, generateSDKMetrics, getBearerToken, MessageType, parameterizedString, printLog, removeSDKVersion, SDK, TYPES, HTTP_HEADER, DETECT_STATUS, FILE_EXTENSION, FILE_FORMAT_TYPE, FILE_PROCESSING, ENCODING_TYPE } from "skyflow-common"; +import { logs } from "skyflow-common"; import { validateDeIdentifyTextRequest, validateReidentifyTextRequest, validateDeidentifyFileRequest, validateGetDetectRunRequest } from "../../../utils/validations"; import VaultClient from "../../client"; import DeidentifyTextOptions from "../../model/options/deidentify-text"; @@ -20,8 +20,8 @@ import DeidentifyFileOptions from "../../model/options/deidentify-file"; import DeidentifyFileRequest from "../../model/request/deidentify-file"; import DeidentifyFileResponse from "../../model/response/deidentify-file"; import * as fs from 'fs'; -import SkyflowError from "../../../error"; -import SKYFLOW_ERROR_CODE from "../../../error/codes"; +import { SkyflowError } from "skyflow-common"; +import { SKYFLOW_ERROR_CODE } from "skyflow-common"; import GetDetectRunRequest from "../../model/request/get-detect-run"; import Transformations from "../../model/options/deidentify-text/transformations"; import { SkyflowAllError } from "../../types"; diff --git a/src/vault/controller/vault/index.ts b/skyvault/src/vault/controller/vault/index.ts similarity index 99% rename from src/vault/controller/vault/index.ts rename to skyvault/src/vault/controller/vault/index.ts index 14adccb4..9df488c5 100644 --- a/src/vault/controller/vault/index.ts +++ b/skyvault/src/vault/controller/vault/index.ts @@ -22,16 +22,16 @@ import FileUploadResponse from '../../model/response/file-upload'; import TokenizeResponse from '../../model/response/tokenize'; import TokenizeRequest from '../../model/request/tokenize'; import { InsertResponseType, ParsedDetokenizeResponse, ParsedInsertBatchResponse, RecordsResponse, StringKeyValueMapType, TokenizeRequestType } from '../../types'; -import { generateSDKMetrics, getBearerToken, MessageType, parameterizedString, printLog, TYPES, SDK, removeSDKVersion, RedactionType, SKYFLOW, SkyflowRecordError, HTTP_STATUS_CODE, HTTP_HEADER, CONTENT_TYPE, ENCODING_TYPE } from '../../../utils'; +import { generateSDKMetrics, getBearerToken, MessageType, parameterizedString, printLog, TYPES, SDK, removeSDKVersion, RedactionType, SKYFLOW, SkyflowRecordError, HTTP_STATUS_CODE, HTTP_HEADER, CONTENT_TYPE, ENCODING_TYPE } from "skyflow-common"; import GetColumnRequest from '../../model/request/get-column'; -import logs from '../../../utils/logs'; +import { logs } from "skyflow-common"; import VaultClient from '../../client'; import { validateDeleteRequest, validateDetokenizeRequest, validateGetColumnRequest, validateGetRequest, validateInsertRequest, validateQueryRequest, validateTokenizeRequest, validateUpdateRequest, validateUploadFileRequest } from '../../../utils/validations'; import path from 'path'; import { Records } from '../../../ _generated_/rest/api/resources/records/client/Client'; import FileUploadOptions from '../../model/options/fileUpload'; -import SkyflowError from '../../../error'; -import SKYFLOW_ERROR_CODE from '../../../error/codes'; +import { SkyflowError } from "skyflow-common"; +import { SKYFLOW_ERROR_CODE } from "skyflow-common"; class VaultController { diff --git a/src/vault/model/options/deidentify-file/bleep-audio/index.ts b/skyvault/src/vault/model/options/deidentify-file/bleep-audio/index.ts similarity index 100% rename from src/vault/model/options/deidentify-file/bleep-audio/index.ts rename to skyvault/src/vault/model/options/deidentify-file/bleep-audio/index.ts diff --git a/src/vault/model/options/deidentify-file/index.ts b/skyvault/src/vault/model/options/deidentify-file/index.ts similarity index 99% rename from src/vault/model/options/deidentify-file/index.ts rename to skyvault/src/vault/model/options/deidentify-file/index.ts index b4cb0e7a..25c0ecc3 100644 --- a/src/vault/model/options/deidentify-file/index.ts +++ b/skyvault/src/vault/model/options/deidentify-file/index.ts @@ -1,4 +1,4 @@ -import { DetectEntities, DetectOutputTranscription, MaskingMethod } from "../../../../utils"; +import { DetectEntities, DetectOutputTranscription, MaskingMethod } from "skyflow-common"; import TokenFormat from "../deidentify-text/token-format"; import Transformations from "../deidentify-text/transformations"; import { Bleep } from './bleep-audio' diff --git a/src/vault/model/options/deidentify-text/index.ts b/skyvault/src/vault/model/options/deidentify-text/index.ts similarity index 96% rename from src/vault/model/options/deidentify-text/index.ts rename to skyvault/src/vault/model/options/deidentify-text/index.ts index 0fcbb078..10e446e0 100644 --- a/src/vault/model/options/deidentify-text/index.ts +++ b/skyvault/src/vault/model/options/deidentify-text/index.ts @@ -1,4 +1,4 @@ -import { DetectEntities } from "../../../../utils"; +import { DetectEntities } from "skyflow-common"; import TokenFormat from "./token-format"; import Transformations from "./transformations"; diff --git a/src/vault/model/options/deidentify-text/token-format/index.ts b/skyvault/src/vault/model/options/deidentify-text/token-format/index.ts similarity index 94% rename from src/vault/model/options/deidentify-text/token-format/index.ts rename to skyvault/src/vault/model/options/deidentify-text/token-format/index.ts index a2c6fb07..8ee2e7e1 100644 --- a/src/vault/model/options/deidentify-text/token-format/index.ts +++ b/skyvault/src/vault/model/options/deidentify-text/token-format/index.ts @@ -1,4 +1,4 @@ -import { DetectEntities, TokenType } from "../../../../../utils"; +import { DetectEntities, TokenType } from "skyflow-common"; class TokenFormat { // Fields diff --git a/src/vault/model/options/deidentify-text/transformations/index.ts b/skyvault/src/vault/model/options/deidentify-text/transformations/index.ts similarity index 89% rename from src/vault/model/options/deidentify-text/transformations/index.ts rename to skyvault/src/vault/model/options/deidentify-text/transformations/index.ts index 85fa2612..d589c074 100644 --- a/src/vault/model/options/deidentify-text/transformations/index.ts +++ b/skyvault/src/vault/model/options/deidentify-text/transformations/index.ts @@ -1,4 +1,4 @@ -import { DetectEntities } from "../../../../../utils"; +import { DetectEntities } from "skyflow-common"; class Transformations { // Fields diff --git a/src/vault/model/options/detokenize/index.ts b/skyvault/src/vault/model/options/detokenize/index.ts similarity index 82% rename from src/vault/model/options/detokenize/index.ts rename to skyvault/src/vault/model/options/detokenize/index.ts index 3fd1b9f6..e9861481 100644 --- a/src/vault/model/options/detokenize/index.ts +++ b/skyvault/src/vault/model/options/detokenize/index.ts @@ -1,13 +1,14 @@ -import { LogLevel, MessageType, printLog } from '../../../../utils'; -import logs from '../../../../utils/logs'; +import { LogLevel, MessageType, printLog } from "skyflow-common"; +import { logs } from "skyflow-common"; +import { BaseOptions } from "skyflow-common"; -class DetokenizeOptions { +class DetokenizeOptions extends BaseOptions { // Fields with default values private continueOnError?: boolean; private downloadUrl?: boolean; // Constructor - constructor() { } + constructor() { super(); } // Setters setContinueOnError(continueOnError: boolean) { diff --git a/src/vault/model/options/fileUpload/index.ts b/skyvault/src/vault/model/options/fileUpload/index.ts similarity index 89% rename from src/vault/model/options/fileUpload/index.ts rename to skyvault/src/vault/model/options/fileUpload/index.ts index bd44772f..8a4dc9b2 100644 --- a/src/vault/model/options/fileUpload/index.ts +++ b/skyvault/src/vault/model/options/fileUpload/index.ts @@ -1,5 +1,6 @@ +import { BaseOptions } from "skyflow-common"; -class FileUploadOptions { +class FileUploadOptions extends BaseOptions { // Fields with default values private filePath?: string; private base64?: string; @@ -8,7 +9,7 @@ class FileUploadOptions { private skyflowId?: string; // Constructor - constructor() { } + constructor() { super(); } // Setters setFilePath(filePath: string): void { diff --git a/src/vault/model/options/get/index.ts b/skyvault/src/vault/model/options/get/index.ts similarity index 93% rename from src/vault/model/options/get/index.ts rename to skyvault/src/vault/model/options/get/index.ts index a59f3464..ee607aa1 100644 --- a/src/vault/model/options/get/index.ts +++ b/skyvault/src/vault/model/options/get/index.ts @@ -1,8 +1,9 @@ // Imports -import { LogLevel, MessageType, OrderByEnum, printLog, RedactionType } from "../../../../utils"; -import logs from '../../../../utils/logs'; +import { LogLevel, MessageType, OrderByEnum, printLog, RedactionType } from "skyflow-common"; +import { logs } from "skyflow-common"; +import { BaseOptions } from "skyflow-common"; -class GetOptions { +class GetOptions extends BaseOptions { // Fields private redactionType?: RedactionType; private returnTokens?: boolean; @@ -15,7 +16,7 @@ class GetOptions { private orderBy?: OrderByEnum; // Constructor - constructor() {} + constructor() { super(); } // Setters setRedactionType(redactionType: RedactionType) { diff --git a/src/vault/model/options/insert/index.ts b/skyvault/src/vault/model/options/insert/index.ts similarity index 89% rename from src/vault/model/options/insert/index.ts rename to skyvault/src/vault/model/options/insert/index.ts index 9d0dfdcd..9a32be2d 100644 --- a/src/vault/model/options/insert/index.ts +++ b/skyvault/src/vault/model/options/insert/index.ts @@ -1,7 +1,8 @@ -import { TokenMode } from "../../../../utils"; +import { TokenMode } from "skyflow-common"; +import { BaseOptions } from "skyflow-common"; //imports -class InsertOptions { +class InsertOptions extends BaseOptions { // Fields private returnTokens?: boolean; private upsert?: string; @@ -11,7 +12,7 @@ class InsertOptions { private continueOnError?: boolean; // Constructor - constructor() { } + constructor() { super(); } // Setters setReturnTokens(returnTokens: boolean) { diff --git a/src/vault/model/options/reidentify-text/index.ts b/skyvault/src/vault/model/options/reidentify-text/index.ts similarity index 94% rename from src/vault/model/options/reidentify-text/index.ts rename to skyvault/src/vault/model/options/reidentify-text/index.ts index 3cbd69e9..78659816 100644 --- a/src/vault/model/options/reidentify-text/index.ts +++ b/skyvault/src/vault/model/options/reidentify-text/index.ts @@ -1,4 +1,4 @@ -import { DetectEntities } from "../../../../utils"; +import { DetectEntities } from "skyflow-common"; class ReidentifyTextOptions { // Fields diff --git a/src/vault/model/options/update/index.ts b/skyvault/src/vault/model/options/update/index.ts similarity index 83% rename from src/vault/model/options/update/index.ts rename to skyvault/src/vault/model/options/update/index.ts index f6dec32a..e86812b9 100644 --- a/src/vault/model/options/update/index.ts +++ b/skyvault/src/vault/model/options/update/index.ts @@ -1,7 +1,8 @@ //imports -import { TokenMode } from "../../../../utils"; +import { TokenMode } from "skyflow-common"; +import { BaseOptions } from "skyflow-common"; -class UpdateOptions { +class UpdateOptions extends BaseOptions { //fields private returnTokens?: boolean; @@ -10,6 +11,7 @@ class UpdateOptions { // Constructor constructor() { + super(); } setReturnTokens(returnTokens: boolean) { diff --git a/src/vault/model/request/deidentify-file/index.ts b/skyvault/src/vault/model/request/deidentify-file/index.ts similarity index 100% rename from src/vault/model/request/deidentify-file/index.ts rename to skyvault/src/vault/model/request/deidentify-file/index.ts diff --git a/src/vault/model/request/deidentify-text/index.ts b/skyvault/src/vault/model/request/deidentify-text/index.ts similarity index 100% rename from src/vault/model/request/deidentify-text/index.ts rename to skyvault/src/vault/model/request/deidentify-text/index.ts diff --git a/src/vault/model/request/delete/index.ts b/skyvault/src/vault/model/request/delete/index.ts similarity index 84% rename from src/vault/model/request/delete/index.ts rename to skyvault/src/vault/model/request/delete/index.ts index f3980922..3bcccee2 100644 --- a/src/vault/model/request/delete/index.ts +++ b/skyvault/src/vault/model/request/delete/index.ts @@ -1,12 +1,14 @@ //imports +import { BaseDeleteRequest } from "skyflow-common"; -class DeleteRequest { +class DeleteRequest extends BaseDeleteRequest { //fields private _table: string; private _ids: Array; // Constructor constructor(table: string, deleteIds: Array) { + super(); this._table = table; this._ids = deleteIds; } diff --git a/src/vault/model/request/detokenize/index.ts b/skyvault/src/vault/model/request/detokenize/index.ts similarity index 77% rename from src/vault/model/request/detokenize/index.ts rename to skyvault/src/vault/model/request/detokenize/index.ts index 01a17623..66f08dca 100644 --- a/src/vault/model/request/detokenize/index.ts +++ b/skyvault/src/vault/model/request/detokenize/index.ts @@ -1,13 +1,15 @@ //imports import { DetokenizeData } from "../../../types"; +import { BaseDetokenizeRequest } from "skyflow-common"; -class DetokenizeRequest { +class DetokenizeRequest extends BaseDetokenizeRequest { //fields private _data: DetokenizeData[]; // Constructor constructor(data: DetokenizeData[]) { + super(); this._data = data; } diff --git a/src/vault/model/request/file-upload/index.ts b/skyvault/src/vault/model/request/file-upload/index.ts similarity index 87% rename from src/vault/model/request/file-upload/index.ts rename to skyvault/src/vault/model/request/file-upload/index.ts index 2ef75143..ef9a94a3 100644 --- a/src/vault/model/request/file-upload/index.ts +++ b/skyvault/src/vault/model/request/file-upload/index.ts @@ -1,14 +1,16 @@ // Imports -import { LogLevel, MessageType, printLog } from '../../../../utils'; -import logs from '../../../../utils/logs'; +import { LogLevel, MessageType, printLog } from "skyflow-common"; +import { logs } from "skyflow-common"; +import { BaseFileUploadRequest } from "skyflow-common"; -class FileUploadRequest { +class FileUploadRequest extends BaseFileUploadRequest { private _table: string; private _columnName: string; private _legacySkyflowId?: string; // Constructor constructor(table: string, columnNameOrSkyflowId: string, columnName?: string) { + super(); this._table = table; if (columnName !== undefined) { diff --git a/src/vault/model/request/get-column/index.ts b/skyvault/src/vault/model/request/get-column/index.ts similarity index 100% rename from src/vault/model/request/get-column/index.ts rename to skyvault/src/vault/model/request/get-column/index.ts diff --git a/src/vault/model/request/get-detect-run/index.ts b/skyvault/src/vault/model/request/get-detect-run/index.ts similarity index 100% rename from src/vault/model/request/get-detect-run/index.ts rename to skyvault/src/vault/model/request/get-detect-run/index.ts diff --git a/src/vault/model/request/get/index.ts b/skyvault/src/vault/model/request/get/index.ts similarity index 80% rename from src/vault/model/request/get/index.ts rename to skyvault/src/vault/model/request/get/index.ts index 575c2009..d6900c64 100644 --- a/src/vault/model/request/get/index.ts +++ b/skyvault/src/vault/model/request/get/index.ts @@ -1,8 +1,9 @@ //imports -import { RedactionType } from "../../../../utils"; +import { RedactionType } from "skyflow-common"; +import { BaseGetRequest } from "skyflow-common"; -class GetRequest { +class GetRequest extends BaseGetRequest { //fields private _table: string; @@ -10,6 +11,7 @@ class GetRequest { // Constructor constructor(table: string, _ids: Array) { + super(); this._table = table; this._ids = _ids; } diff --git a/src/vault/model/request/inkove/index.ts b/skyvault/src/vault/model/request/inkove/index.ts similarity index 77% rename from src/vault/model/request/inkove/index.ts rename to skyvault/src/vault/model/request/inkove/index.ts index d063bb9d..58c6a263 100644 --- a/src/vault/model/request/inkove/index.ts +++ b/skyvault/src/vault/model/request/inkove/index.ts @@ -1,8 +1,9 @@ //imports -import { RequestMethod } from "../../../../utils"; +import { RequestMethod } from "skyflow-common"; import { StringKeyValueMapType } from "../../../types"; +import { BaseInvokeConnectionRequest } from "skyflow-common"; -class InvokeConnectionRequest { +class InvokeConnectionRequest extends BaseInvokeConnectionRequest { //fields method: RequestMethod; queryParams?: StringKeyValueMapType; @@ -11,6 +12,7 @@ class InvokeConnectionRequest { headers?: StringKeyValueMapType; constructor(method: RequestMethod, body?: StringKeyValueMapType, headers?: StringKeyValueMapType, pathParams?: StringKeyValueMapType, queryParams?: StringKeyValueMapType) { + super(); this.method = method; this.pathParams = pathParams; this.queryParams = queryParams; diff --git a/src/vault/model/request/insert/index.ts b/skyvault/src/vault/model/request/insert/index.ts similarity index 85% rename from src/vault/model/request/insert/index.ts rename to skyvault/src/vault/model/request/insert/index.ts index ee251c9f..6ed61df4 100644 --- a/src/vault/model/request/insert/index.ts +++ b/skyvault/src/vault/model/request/insert/index.ts @@ -1,6 +1,7 @@ //imports +import { BaseInsertRequest } from "skyflow-common"; -class InsertRequest { +class InsertRequest extends BaseInsertRequest { //fields private _table: string; @@ -8,6 +9,7 @@ class InsertRequest { // Constructor constructor(table: string, data: Record[]) { + super(); this._table = table; this._data = data; } diff --git a/src/vault/model/request/query/index.ts b/skyvault/src/vault/model/request/query/index.ts similarity index 73% rename from src/vault/model/request/query/index.ts rename to skyvault/src/vault/model/request/query/index.ts index 1fe3da91..e0808671 100644 --- a/src/vault/model/request/query/index.ts +++ b/skyvault/src/vault/model/request/query/index.ts @@ -1,8 +1,10 @@ +import { BaseQueryRequest } from "skyflow-common"; -class QueryRequest { +class QueryRequest extends BaseQueryRequest { private _query: string; constructor(query: string) { + super(); this._query = query; } diff --git a/src/vault/model/request/reidentify-text/index.ts b/skyvault/src/vault/model/request/reidentify-text/index.ts similarity index 100% rename from src/vault/model/request/reidentify-text/index.ts rename to skyvault/src/vault/model/request/reidentify-text/index.ts diff --git a/src/vault/model/request/tokenize/index.ts b/skyvault/src/vault/model/request/tokenize/index.ts similarity index 80% rename from src/vault/model/request/tokenize/index.ts rename to skyvault/src/vault/model/request/tokenize/index.ts index f7ef067d..3fe6bf03 100644 --- a/src/vault/model/request/tokenize/index.ts +++ b/skyvault/src/vault/model/request/tokenize/index.ts @@ -1,14 +1,16 @@ //imports import { TokenizeRequestType } from "../../../types"; +import { BaseTokenizeRequest } from "skyflow-common"; -class TokenizeRequest { +class TokenizeRequest extends BaseTokenizeRequest { //fields private _values: Array; // Constructor constructor(values: Array) { + super(); this._values = values; } diff --git a/src/vault/model/request/update/index.ts b/skyvault/src/vault/model/request/update/index.ts similarity index 85% rename from src/vault/model/request/update/index.ts rename to skyvault/src/vault/model/request/update/index.ts index a749f74c..885e6910 100644 --- a/src/vault/model/request/update/index.ts +++ b/skyvault/src/vault/model/request/update/index.ts @@ -1,6 +1,7 @@ //imports +import { BaseUpdateRequest } from "skyflow-common"; -class UpdateRequest { +class UpdateRequest extends BaseUpdateRequest { //fields private _table: string; @@ -8,6 +9,7 @@ class UpdateRequest { // Constructor constructor(table: string, data: Record) { + super(); this._table = table; this._data = data; } diff --git a/src/vault/model/response/deidentify-file/index.ts b/skyvault/src/vault/model/response/deidentify-file/index.ts similarity index 96% rename from src/vault/model/response/deidentify-file/index.ts rename to skyvault/src/vault/model/response/deidentify-file/index.ts index b39af6d8..9732c40a 100644 --- a/src/vault/model/response/deidentify-file/index.ts +++ b/skyvault/src/vault/model/response/deidentify-file/index.ts @@ -1,4 +1,4 @@ -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; class DeidentifyFileResponse { // fields diff --git a/src/vault/model/response/deidentify-text/index.ts b/skyvault/src/vault/model/response/deidentify-text/index.ts similarity index 95% rename from src/vault/model/response/deidentify-text/index.ts rename to skyvault/src/vault/model/response/deidentify-text/index.ts index 8beca5d9..fb78984f 100644 --- a/src/vault/model/response/deidentify-text/index.ts +++ b/skyvault/src/vault/model/response/deidentify-text/index.ts @@ -1,7 +1,7 @@ //imports import { IndexRange } from "../../../types"; -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; class DeidentifyTextResponse { //fields diff --git a/src/vault/model/response/delete/index.ts b/skyvault/src/vault/model/response/delete/index.ts similarity index 73% rename from src/vault/model/response/delete/index.ts rename to skyvault/src/vault/model/response/delete/index.ts index 737176bb..d9fe8983 100644 --- a/src/vault/model/response/delete/index.ts +++ b/skyvault/src/vault/model/response/delete/index.ts @@ -1,8 +1,9 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; +import { BaseDeleteResponse } from "skyflow-common"; -class DeleteResponse { +class DeleteResponse extends BaseDeleteResponse { //fields @@ -14,6 +15,7 @@ class DeleteResponse { * @deprecated Passing undefined for deletedIds is no longer supported. Pass empty array [] instead. */ constructor({ deletedIds, errors }: { deletedIds: Array, errors: Array | null}) { + super(); this.deletedIds = deletedIds; this.errors = errors; } diff --git a/src/vault/model/response/detokenize/index.ts b/skyvault/src/vault/model/response/detokenize/index.ts similarity index 74% rename from src/vault/model/response/detokenize/index.ts rename to skyvault/src/vault/model/response/detokenize/index.ts index 99b94710..7601b389 100644 --- a/src/vault/model/response/detokenize/index.ts +++ b/skyvault/src/vault/model/response/detokenize/index.ts @@ -1,9 +1,10 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; import { ErrorDetokenizeResponse, SuccessDetokenizeResponse } from "../../../types"; +import { BaseDetokenizeResponse } from "skyflow-common"; -class DetokenizeResponse { +class DetokenizeResponse extends BaseDetokenizeResponse { //fields detokenizedFields: Array | null; @@ -11,6 +12,7 @@ class DetokenizeResponse { errors: Array | null; constructor({ detokenizedFields, errors }: { detokenizedFields: Array | null, errors: Array | null }) { + super(); this.detokenizedFields = detokenizedFields; this.errors = errors; } diff --git a/src/vault/model/response/file-upload/index.ts b/skyvault/src/vault/model/response/file-upload/index.ts similarity index 64% rename from src/vault/model/response/file-upload/index.ts rename to skyvault/src/vault/model/response/file-upload/index.ts index ce0ef2c8..9b778131 100644 --- a/src/vault/model/response/file-upload/index.ts +++ b/skyvault/src/vault/model/response/file-upload/index.ts @@ -1,8 +1,9 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; +import { BaseFileUploadResponse } from "skyflow-common"; -class FileUploadResponse { +class FileUploadResponse extends BaseFileUploadResponse { //fields skyflowId: string; @@ -10,6 +11,7 @@ class FileUploadResponse { errors: Array | null; constructor({ skyflowId, errors }: { skyflowId: string, errors: Array | null }) { + super(); this.skyflowId = skyflowId; this.errors = errors; } diff --git a/src/vault/model/response/get/index.ts b/skyvault/src/vault/model/response/get/index.ts similarity index 70% rename from src/vault/model/response/get/index.ts rename to skyvault/src/vault/model/response/get/index.ts index 3a7c465f..6dcb4256 100644 --- a/src/vault/model/response/get/index.ts +++ b/skyvault/src/vault/model/response/get/index.ts @@ -1,9 +1,10 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; import { GetResponseData } from "../../../types"; +import { BaseGetResponse } from "skyflow-common"; -class GetResponse { +class GetResponse extends BaseGetResponse { //fields data: Array; @@ -11,6 +12,7 @@ class GetResponse { errors: Array | null; constructor({ data, errors }: { data: Array, errors: Array | null}) { + super(); this.data = data; this.errors = errors; } diff --git a/src/vault/model/response/insert/index.ts b/skyvault/src/vault/model/response/insert/index.ts similarity index 70% rename from src/vault/model/response/insert/index.ts rename to skyvault/src/vault/model/response/insert/index.ts index c50dafdf..b55db684 100644 --- a/src/vault/model/response/insert/index.ts +++ b/skyvault/src/vault/model/response/insert/index.ts @@ -1,8 +1,9 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; import { InsertResponseType } from "../../../types"; +import { BaseInsertResponse } from "skyflow-common"; -class InsertResponse { +class InsertResponse extends BaseInsertResponse { //fields insertedFields: Array; @@ -13,7 +14,8 @@ class InsertResponse { * @deprecated Passing null for insertedFields is no longer supported. Pass empty array [] instead. */ constructor({ insertedFields, errors }: { insertedFields: Array, errors: Array | null }) { - this.insertedFields = insertedFields; + super(); + this.insertedFields = insertedFields; this.errors = errors; } diff --git a/src/vault/model/response/invoke/invoke.ts b/skyvault/src/vault/model/response/invoke/invoke.ts similarity index 68% rename from src/vault/model/response/invoke/invoke.ts rename to skyvault/src/vault/model/response/invoke/invoke.ts index 617bb708..85cf9f94 100644 --- a/src/vault/model/response/invoke/invoke.ts +++ b/skyvault/src/vault/model/response/invoke/invoke.ts @@ -1,8 +1,9 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; +import { BaseInvokeConnectionResponse } from "skyflow-common"; -class InvokeConnectionResponse { +class InvokeConnectionResponse extends BaseInvokeConnectionResponse { //fields data?: Object; @@ -11,6 +12,7 @@ class InvokeConnectionResponse { errors: Array | null; constructor({ data, metadata, errors }: { data?: object, metadata?: Record, errors: Array | null }) { + super(); this.data = data; this.metadata = metadata; this.errors = errors; diff --git a/src/vault/model/response/query/index.ts b/skyvault/src/vault/model/response/query/index.ts similarity index 70% rename from src/vault/model/response/query/index.ts rename to skyvault/src/vault/model/response/query/index.ts index a67321ec..b007f629 100644 --- a/src/vault/model/response/query/index.ts +++ b/skyvault/src/vault/model/response/query/index.ts @@ -1,9 +1,10 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; import { QueryResponseType } from "../../../types"; +import { BaseQueryResponse } from "skyflow-common"; -class QueryResponse { +class QueryResponse extends BaseQueryResponse { //fields fields: Array; @@ -11,6 +12,7 @@ class QueryResponse { errors: Array | null; constructor( { fields, errors }: { fields: Array, errors: Array | null }) { + super(); this.fields = fields; this.errors = errors; } diff --git a/src/vault/model/response/reidentify-text/index.ts b/skyvault/src/vault/model/response/reidentify-text/index.ts similarity index 100% rename from src/vault/model/response/reidentify-text/index.ts rename to skyvault/src/vault/model/response/reidentify-text/index.ts diff --git a/src/vault/model/response/tokenize/index.ts b/skyvault/src/vault/model/response/tokenize/index.ts similarity index 65% rename from src/vault/model/response/tokenize/index.ts rename to skyvault/src/vault/model/response/tokenize/index.ts index ea279e47..56b6ea2d 100644 --- a/src/vault/model/response/tokenize/index.ts +++ b/skyvault/src/vault/model/response/tokenize/index.ts @@ -1,8 +1,9 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; +import { BaseTokenizeResponse } from "skyflow-common"; -class TokenizeResponse { +class TokenizeResponse extends BaseTokenizeResponse { //fields tokens: Array; @@ -10,6 +11,7 @@ class TokenizeResponse { errors: Array | null; constructor({ tokens, errors }: { tokens: Array, errors: Array | null }) { + super(); this.tokens = tokens; this.errors = errors; } diff --git a/src/vault/model/response/update/index.ts b/skyvault/src/vault/model/response/update/index.ts similarity index 64% rename from src/vault/model/response/update/index.ts rename to skyvault/src/vault/model/response/update/index.ts index 6696dffe..9f215ff8 100644 --- a/src/vault/model/response/update/index.ts +++ b/skyvault/src/vault/model/response/update/index.ts @@ -1,9 +1,10 @@ //imports -import { SkyflowRecordError } from "../../../../utils"; +import { SkyflowRecordError } from "skyflow-common"; import { InsertResponseType } from "../../../types"; +import { BaseUpdateResponse } from "skyflow-common"; -class UpdateResponse { +class UpdateResponse extends BaseUpdateResponse { //fields updatedField: InsertResponseType; @@ -11,7 +12,8 @@ class UpdateResponse { errors: Array | null; constructor({ updatedField, errors }: { updatedField: InsertResponseType, errors: Array | null }) { - this.updatedField = updatedField; + super(); + this.updatedField = updatedField; this.errors = errors; } diff --git a/skyvault/src/vault/skyflow/index.ts b/skyvault/src/vault/skyflow/index.ts new file mode 100644 index 00000000..aaa0f94f --- /dev/null +++ b/skyvault/src/vault/skyflow/index.ts @@ -0,0 +1,201 @@ +import { + BaseSkyflow, + BaseVaultEntry, + CONFIG, + Env, + getVaultURL, + LogLevel, + MessageType, + parameterizedString, + printLog, + logs, + SkyflowError, + SKYFLOW_ERROR_CODE, + Credentials, + VaultConfig, + ConnectionConfig, + validateConnectionConfig, + validateUpdateConnectionConfig, + setSdkIdentity, +} from "skyflow-common"; +import { SkyflowConfig } from "../types"; +import VaultController from "../controller/vault"; +import ConnectionController from "../controller/connections"; +import VaultClient from "../client"; +import DetectController from "../controller/detect"; +import { SDK_NAME, SDK_VERSION } from "../../version"; + +// Stamp the SDK identity so error text + telemetry self-report this product. +setSdkIdentity({ name: SDK_NAME, version: SDK_VERSION }); + +/** A configured vault: the client plus the controllers skyvault exposes. */ +interface VaultEntry extends BaseVaultEntry { + vaultController: VaultController; + detectController: DetectController; +} + +/** A configured connection: the client plus its connection controller. */ +interface ConnectionEntry { + config: ConnectionConfig; + client: VaultClient; + connectionController: ConnectionController; +} + +/** + * Skyflow is the SkyVault (`skyflow-node`) SDK facade. The vault-config / + * credential / log-level management is inherited from the shared `BaseSkyflow` + * in `common`; this layer adds the SkyVault-only surfaces: the detect + * controller on each vault, and the connection registry + `connection()`. + */ +class Skyflow extends BaseSkyflow { + private connectionClients: { [connectionId: string]: ConnectionEntry } = {}; + + constructor(config: SkyflowConfig) { + super(); + // Common vault + credential + log-level setup (validates config, logs, + // registers vaultConfigs via buildVaultEntry). + this.initializeSkyflow(config); + + printLog(logs.infoLogs.VALIDATING_CONNECTION_CONFIG, MessageType.LOG, this.logLevel); + config.connectionConfigs?.map((connectionConfig) => { + this.addConnectionConfig(connectionConfig); + }); + printLog(logs.infoLogs.CLIENT_INITIALIZED, MessageType.LOG, this.logLevel); + } + + // --- layer-specific vault entry (vault + detect controllers) --------------- + protected buildVaultEntry(config: VaultConfig): VaultEntry { + const env = config.env || Env.PROD; + const vaultUrl = getVaultURL(config.clusterId, env); + const client = new VaultClient( + vaultUrl, + config.vaultId, + config?.credentials, + this.commonCredentials, + this.logLevel, + ); + const vaultController = new VaultController(client); + const detectController = new DetectController(client); + return { config, client, vaultController, detectController }; + } + + // --- log level / credentials also propagate to connection clients ---------- + protected applyLogLevel(): void { + super.applyLogLevel(); + Object.values(this.connectionClients).forEach((entry) => entry.client.setLogLevel(this.logLevel)); + } + + protected applyCredentials(): void { + super.applyCredentials(); + Object.values(this.connectionClients).forEach((entry) => + entry.client.updateSkyflowCredentials(this.commonCredentials), + ); + } + + // --- connections (SkyVault only) ------------------------------------------- + private addConnectionClient(config: ConnectionConfig): void { + const client = new VaultClient( + config.connectionUrl, + "", + config?.credentials, + this.commonCredentials, + this.logLevel, + ); + const connectionController = new ConnectionController(client); + printLog( + parameterizedString(logs.infoLogs.CONNECTION_CONTROLLER_INITIALIZED, [config.connectionId]), + MessageType.LOG, + this.logLevel, + ); + this.connectionClients[config.connectionId] = { config, client, connectionController }; + } + + addConnectionConfig(config: ConnectionConfig): void { + validateConnectionConfig(config, this.logLevel); + this.throwErrorIfConnectionIdExists(config?.connectionId); + this.addConnectionClient(config); + } + + updateConnectionConfig(config: ConnectionConfig): void { + validateUpdateConnectionConfig(config, this.logLevel); + const existing = this.connectionClients[config.connectionId]; + if (!existing) { + this.throwErrorForUnknownConnectionId(config.connectionId); + return; + } + const updatedConfig = { ...existing.config, ...config }; + existing.config = updatedConfig; + existing.client.updateClientConfig( + updatedConfig.connectionUrl, + "", + updatedConfig.credentials, + this.commonCredentials, + this.logLevel, + ); + } + + getConnectionConfig(connectionId: string): ConnectionConfig { + if (!connectionId) { + throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_ID_VALIDATION); + } + const existing = this.connectionClients[connectionId]; + if (!existing) { + this.throwErrorForUnknownConnectionId(connectionId); + } + return existing.config; + } + + removeConnectionConfig(connectionId: string): void { + if (!this.connectionClients[connectionId]) { + this.throwErrorForUnknownConnectionId(connectionId); + return; + } + delete this.connectionClients[connectionId]; + } + + // --- controller accessors -------------------------------------------------- + vault(vaultId?: string): VaultController { + return this.resolveVaultEntry(vaultId).vaultController; + } + + detect(vaultId?: string): DetectController { + return this.resolveVaultEntry(vaultId).detectController; + } + + connection(connectionId?: string): ConnectionController { + if (Object.keys(this.connectionClients).length === 0) { + throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CLIENTS); + } + const clientId = connectionId || Object.keys(this.connectionClients)[0]; + const entry = this.connectionClients[clientId]; + if (entry?.connectionController) { + return entry.connectionController; + } + this.throwErrorForUnknownConnectionId(clientId); + // Unreachable: throwErrorForUnknownConnectionId always throws. + throw new SkyflowError(SKYFLOW_ERROR_CODE.INTERNAL_SERVER_ERROR); + } + + // --- connection error helpers ---------------------------------------------- + private throwErrorIfConnectionIdExists(id: string): void { + if (Object.keys(this.connectionClients).includes(id)) { + printLog( + parameterizedString(logs.infoLogs[`${CONFIG.CONNECTION_ID}_CONFIG_EXISTS`], [id]), + MessageType.ERROR, + this.logLevel, + ); + throw new SkyflowError(SKYFLOW_ERROR_CODE.CONNECTION_ID_EXITS_IN_CONFIG_LIST, [id]); + } + } + + private throwErrorForUnknownConnectionId(id: string): void { + printLog( + parameterizedString(logs.infoLogs[`${CONFIG.CONNECTION_ID}_CONFIG_DOES_NOT_EXIST`], [id]), + MessageType.ERROR, + this.logLevel, + ); + throw new SkyflowError(SKYFLOW_ERROR_CODE.CONNECTION_ID_NOT_IN_CONFIG_LIST, [id]); + } +} + +export default Skyflow; diff --git a/src/vault/types/index.ts b/skyvault/src/vault/types/index.ts similarity index 72% rename from src/vault/types/index.ts rename to skyvault/src/vault/types/index.ts index 7821f1cd..5e78002e 100644 --- a/src/vault/types/index.ts +++ b/skyvault/src/vault/types/index.ts @@ -1,31 +1,28 @@ -import { LogLevel, RedactionType, SkyflowRecordError } from "../../utils"; -import ConnectionConfig from "../config/connection"; -import VaultConfig from "../config/vault" -import Credentials from "../config/credentials"; +import { RedactionType, SkyflowRecordError } from "skyflow-common"; +import { ConnectionConfig } from "skyflow-common"; +import { VaultConfig } from "skyflow-common"; import VaultController from "../controller/vault"; import ConnectionController from "../controller/connections"; import VaultClient from "../client"; import DetectController from "../controller/detect"; -import SkyflowError from "../../error"; - -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; -} +import { SkyflowError } from "skyflow-common"; +import { InsertResponseType } from "skyflow-common"; + +// Re-export the contract-agnostic types that now live in skyflow-common, so the +// public API surface of skyflow-node continues to expose them from here. +export type { + SkyflowConfig, + FileInput, + Filepath, + FileObject, + InsertResponseType, + GetResponseData, + QueryResponseType, + StringKeyValueMapType, + TokenizeRequestType, + IndexRange, + ServiceAccountResponseError, +} from "skyflow-common"; export interface ClientConfig { config: VaultConfig | ConnectionConfig; @@ -39,29 +36,6 @@ export interface ClientObj { [vaultId: string]: ClientConfig; } -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 SuccessDetokenizeResponse { token: string; value: string; @@ -142,20 +116,6 @@ export type SkyflowErrorData = | SkyflowApiErrorLegacyBody['error'] | undefined; -export type ServiceAccountResponseError = { - rawResponse?: { - headers?: { - get: (header: string) => string | undefined; - }; - }; - body: any; - response?: { - status?: number | string; - }; - message: string; - [key: string]: any; -}; - export interface RecordsResponse> { records: T[]; requestId: string; @@ -175,14 +135,10 @@ export interface SkyflowIdResponse { /** @deprecated Renamed to skyflowId. Will be removed in v3. */ skyflow_id?: string; } - + export interface TokensResponse extends SkyflowIdResponse { tokens?: Record; } -export interface IndexRange { - start?: number; - end?: number; -} export type DeidentifyFileOutputProcessedFileType = | "entities" diff --git a/skyvault/src/version.ts b/skyvault/src/version.ts new file mode 100644 index 00000000..d07c66bc --- /dev/null +++ b/skyvault/src/version.ts @@ -0,0 +1,4 @@ +// SDK version constant for skyflow-node (skyvault). +// Kept in sync with skyvault/package.json "version" by scripts/bump_version.sh. +export const SDK_NAME = "skyflow-node"; +export const SDK_VERSION = "2.1.2"; diff --git a/skyvault/test/demo-credentials/empty.json b/skyvault/test/demo-credentials/empty.json new file mode 100644 index 00000000..e69de29b diff --git a/skyvault/test/demo-credentials/invalidJson.json b/skyvault/test/demo-credentials/invalidJson.json new file mode 100644 index 00000000..c11a9d0a --- /dev/null +++ b/skyvault/test/demo-credentials/invalidJson.json @@ -0,0 +1 @@ +"some test creds" diff --git a/skyvault/test/demo-credentials/noClientId.json b/skyvault/test/demo-credentials/noClientId.json new file mode 100644 index 00000000..10699c21 --- /dev/null +++ b/skyvault/test/demo-credentials/noClientId.json @@ -0,0 +1,3 @@ +{ + "privateKey": "Some private key" +} \ No newline at end of file diff --git a/skyvault/test/demo-credentials/noKeyId.json b/skyvault/test/demo-credentials/noKeyId.json new file mode 100644 index 00000000..9b93da90 --- /dev/null +++ b/skyvault/test/demo-credentials/noKeyId.json @@ -0,0 +1,4 @@ +{ + "privateKey": "Some private key", + "clientID": "Some client ID" +} \ No newline at end of file diff --git a/skyvault/test/demo-credentials/noPrivateKey.json b/skyvault/test/demo-credentials/noPrivateKey.json new file mode 100644 index 00000000..94efc8d8 --- /dev/null +++ b/skyvault/test/demo-credentials/noPrivateKey.json @@ -0,0 +1 @@ +{"data": "this has no data","clientID": "Some client ID", "keyID": "Some key ID","tokenURI":"uri"} \ No newline at end of file diff --git a/skyvault/test/demo-credentials/noTokenURI.json b/skyvault/test/demo-credentials/noTokenURI.json new file mode 100644 index 00000000..f7d86552 --- /dev/null +++ b/skyvault/test/demo-credentials/noTokenURI.json @@ -0,0 +1,5 @@ +{ + "privateKey": "Some private key", + "clientID": "Some client ID", + "keyID": "Some key ID" +} \ No newline at end of file diff --git a/skyvault/test/demo-credentials/valid.json b/skyvault/test/demo-credentials/valid.json new file mode 100644 index 00000000..ac7f5c00 --- /dev/null +++ b/skyvault/test/demo-credentials/valid.json @@ -0,0 +1,7 @@ +{ + "clientID": "test-client-id", + "keyID": "test-key-id", + "tokenURI": "https://manage.skyflow.dev", + "privateKey": "KEY", + "data": "DATA" +} \ No newline at end of file diff --git a/test/mockData/pii.pdf b/skyvault/test/mockData/pii.pdf similarity index 100% rename from test/mockData/pii.pdf rename to skyvault/test/mockData/pii.pdf diff --git a/test/utils/validations.test.js b/skyvault/test/utils/validations.test.js similarity index 98% rename from test/utils/validations.test.js rename to skyvault/test/utils/validations.test.js index 04e108ef..de25251d 100644 --- a/test/utils/validations.test.js +++ b/skyvault/test/utils/validations.test.js @@ -1,13 +1,19 @@ const fs = require('fs'); const path = require('path'); -const { validateDeidentifyFileRequest, validateDeidentifyFileOptions, validateSkyflowConfig, validateVaultConfig, validateUpdateVaultConfig, validateSkyflowCredentials, validateConnectionConfig, validateUpdateConnectionConfig, validateInsertRequest, validateUpdateRequest, validateGetRequest, validateDetokenizeRequest, validateTokenizeRequest, validateDeleteRequest, validateUploadFileRequest, validateQueryRequest, validateDeIdentifyTextRequest, validateReidentifyTextRequest, validateGetDetectRunRequest, validateInvokeConnectionRequest, validateUpdateOptions, validateGetColumnRequest, validateCredentialsWithId, isEnv, isRedactionType, isByot, isOrderBy, isMethod, isLogLevel, isValidAPIKey, validateInsertOptions, validateGetOptions, validateDetokenizeOptions } = require('../../src/utils/validations'); +const { validateDeidentifyFileRequest, validateDeidentifyFileOptions, validateInsertRequest, validateUpdateRequest, validateGetRequest, validateDetokenizeRequest, validateTokenizeRequest, validateDeleteRequest, validateUploadFileRequest, validateQueryRequest, validateDeIdentifyTextRequest, validateReidentifyTextRequest, validateGetDetectRunRequest, validateInvokeConnectionRequest, validateUpdateOptions, validateGetColumnRequest, isRedactionType, isByot, isOrderBy, isMethod, validateInsertOptions, validateGetOptions, validateDetokenizeOptions } = require('../../src/utils/validations'); +// Generic (contract-agnostic) validators now live in skyflow-common. +const { validateSkyflowConfig, validateVaultConfig, validateUpdateVaultConfig, validateSkyflowCredentials, validateConnectionConfig, validateUpdateConnectionConfig, validateCredentialsWithId, isEnv, isLogLevel, isValidAPIKey } = require('skyflow-common'); const DeidentifyFileRequest = require('../../src/vault/model/request/deidentify-file').default; const DeidentifyFileOptions = require('../../src/vault/model/options/deidentify-file').default; -const SKYFLOW_ERROR_CODE = require('../../src/error/codes'); +// NOTE: On main, `require('../../src/error/codes')` returned the ES module +// wrapper (the codes live on `.default`), so `SKYFLOW_ERROR_CODE.` was +// `undefined` and `toThrow(undefined)` only asserted "throws something". We +// require the whole skyflow-common module here to preserve that exact behavior. +const SKYFLOW_ERROR_CODE = require('skyflow-common'); const { default: TokenFormat } = require('../../src/vault/model/options/deidentify-text/token-format'); -const { DetectEntities, Env, TokenMode, OrderByEnum, MaskingMethod } = require('../../src/utils'); +const { DetectEntities, Env, TokenMode, OrderByEnum, MaskingMethod } = require('skyflow-common'); const { Bleep } = require('../../src/vault/model/options/deidentify-file/bleep-audio'); -const { LogLevel } = require('../../src/utils'); +const { LogLevel } = require('skyflow-common'); const { default: Transformations } = require('../../src/vault/model/options/deidentify-text/transformations'); describe('validateDeidentifyFileRequest', () => { @@ -703,7 +709,7 @@ describe('validateSkyflowCredentials', () => { const credentials = { token: 'valid-non-expired-token' }; - jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(false); + jest.spyOn(require('skyflow-common/lib/utils/jwt-utils'), 'isExpired').mockReturnValue(false); expect(() => validateSkyflowCredentials(credentials)).not.toThrow(); }); @@ -711,7 +717,7 @@ describe('validateSkyflowCredentials', () => { const credentials = { token: 'expired-token' }; - jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(true); + jest.spyOn(require('skyflow-common/lib/utils/jwt-utils'), 'isExpired').mockReturnValue(true); expect(() => validateSkyflowCredentials(credentials)).toThrow(SKYFLOW_ERROR_CODE.INVALID_BEARER_TOKEN); }); }); @@ -4138,7 +4144,7 @@ describe('validateCredentialsWithId', () => { test('should throw error when token is expired', () => { // Mock isExpired to return true - jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(true); + jest.spyOn(require('skyflow-common/lib/utils/jwt-utils'), 'isExpired').mockReturnValue(true); const credentials = { token: 'expired-token' @@ -4149,7 +4155,7 @@ describe('validateCredentialsWithId', () => { test('should accept valid token credentials', () => { // Mock isExpired to return false - jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(false); + jest.spyOn(require('skyflow-common/lib/utils/jwt-utils'), 'isExpired').mockReturnValue(false); const credentials = { token: 'valid-token' @@ -4295,7 +4301,7 @@ describe('validateCredentialsWithId and validateSkyflowCredentials - tokenUri va }); test('validateCredentialsWithId: should accept valid tokenUri (TokenCredentials)', () => { - jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(false); + jest.spyOn(require('skyflow-common/lib/utils/jwt-utils'), 'isExpired').mockReturnValue(false); const credentials = { token: 'valid-token', tokenUri: validUrl @@ -4359,7 +4365,7 @@ describe('validateCredentialsWithId and validateSkyflowCredentials - tokenUri va }); test('validateSkyflowCredentials: should accept valid tokenUri (TokenCredentials)', () => { - jest.spyOn(require('../../src/utils/jwt-utils'), 'isExpired').mockReturnValue(false); + jest.spyOn(require('skyflow-common/lib/utils/jwt-utils'), 'isExpired').mockReturnValue(false); const credentials = { token: 'valid-token', tokenUri: validUrl diff --git a/test/vault/client/client.test.js b/skyvault/test/vault/client/client.test.js similarity index 99% rename from test/vault/client/client.test.js rename to skyvault/test/vault/client/client.test.js index bfb02efd..24511701 100644 --- a/test/vault/client/client.test.js +++ b/skyvault/test/vault/client/client.test.js @@ -2,9 +2,9 @@ import VaultClient from '../../../src/vault/client'; // Adjust the import path import { Query } from '../../../src/ _generated_/rest/api/resources/query/client/Client'; import { Records } from '../../../src/ _generated_/rest/api/resources/records/client/Client'; import { Tokens } from '../../../src/ _generated_/rest/api/resources/tokens/client/Client'; -import { AuthType, LogLevel, TYPES } from '../../../src/utils'; -import { isExpired } from '../../../src/utils/jwt-utils'; -import SkyflowError from '../../../src/error'; +import { AuthType, LogLevel, TYPES } from 'skyflow-common'; +import { isExpired } from 'skyflow-common'; +import { SkyflowError } from 'skyflow-common'; import { Strings } from '../../../src/ _generated_/rest/api/resources/strings/client/Client'; import { Files } from '../../../src/ _generated_/rest/api/resources/files/client/Client'; @@ -14,8 +14,13 @@ jest.mock('../../../src/ _generated_/rest/api/resources/query/client/Client'); jest.mock('../../../src/ _generated_/rest/api/resources/tokens/client/Client'); jest.mock('../../../src/ _generated_/rest/api/resources/strings/client/Client'); jest.mock('../../../src/ _generated_/rest/api/resources/files/client/Client'); -jest.mock('../../../src/utils/jwt-utils'); -jest.mock('../../../src/error'); +jest.mock('skyflow-common', () => { + const actual = jest.requireActual('skyflow-common'); + return { + ...actual, + isExpired: jest.fn(), + }; +}); describe('VaultClient', () => { const url = 'https://vault.skyflow.com'; diff --git a/test/vault/controller/connection.test.js b/skyvault/test/vault/controller/connection.test.js similarity index 96% rename from test/vault/controller/connection.test.js rename to skyvault/test/vault/controller/connection.test.js index af1441d6..0e545bc9 100644 --- a/test/vault/controller/connection.test.js +++ b/skyvault/test/vault/controller/connection.test.js @@ -10,60 +10,39 @@ import { HTTP_HEADER, CONTENT_TYPE, objectToXML, -} from "../../../src/utils"; +} from "skyflow-common"; import { validateInvokeConnectionRequest } from "../../../src/utils/validations"; import VaultClient from "../../../src/vault/client"; import ConnectionController from "../../../src/vault/controller/connections"; -import SkyflowError from "../../../src/error"; -import SKYFLOW_ERROR_CODE from "../../../src/error/codes"; - -jest.mock("../../../src/utils", () => ({ - fillUrlWithPathAndQueryParams: jest.fn(), - generateSDKMetrics: jest.fn(), - getBearerToken: jest.fn(), - LogLevel: { WARN: 'WARN', INFO: 'INFO', DEBUG: 'DEBUG', ERROR: 'ERROR', OFF: 'OFF' }, - MessageType: { LOG: 'LOG', WARN: 'WARN', ERROR: 'ERROR' }, - RequestMethod: { POST: 'POST', GET: 'GET', PUT: 'PUT', PATCH: 'PATCH' }, - parameterizedString: jest.fn((...args) => args.join(' ')), - printLog: jest.fn(), - SDK: { METRICS_HEADER_KEY: 'sky-metadata' }, - SKYFLOW: { ID: 'skyflowId', LEGACY_ID: 'skyflow_id', AUTH_HEADER_KEY: 'x-skyflow-authorization' }, - REQUEST: { ID_KEY: 'x-request-id' }, - TYPES: { - INSERT: 'INSERT', INSERT_BATCH: 'INSERT_BATCH', DETOKENIZE: 'DETOKENIZE', TOKENIZE: 'TOKENIZE', - DELETE: 'DELETE', UPDATE: 'UPDATE', GET: 'GET', FILE_UPLOAD: 'FILE_UPLOAD', QUERY: 'QUERY', - DETECT: 'DETECT', INVOKE_CONNECTION: 'INVOKE_CONNECTION', DEIDENTIFY_TEXT: 'DEIDENTIFY_TEXT', - REIDENTIFY_TEXT: 'REIDENTIFY_TEXT', DEIDENTIFY_FILE: 'DEIDENTIFY_FILE', DETECT_RUN: 'DETECT_RUN', - }, - HTTP_HEADER: { CONTENT_TYPE: 'Content-Type', CONTENT_TYPE_LOWER: 'content-type', X_REQUEST_ID: 'x-request-id', ERROR_FROM_CLIENT: 'error-from-client' }, - CONTENT_TYPE: { - APPLICATION_JSON: 'application/json', - APPLICATION_X_WWW_FORM_URLENCODED: 'application/x-www-form-urlencoded', - TEXT_PLAIN: 'text/plain', - MULTIPART_FORM_DATA: 'multipart/form-data', - TEXT_XML: 'text/xml', - APPLICATION_XML: 'application/xml', - TEXT_HTML: 'text/html', - }, - objectToXML: jest.fn((obj, root) => `<${root}>${JSON.stringify(obj)}`), -})); -jest.mock("../../../src/utils/logs", () => ({ - __esModule: true, - default: { - infoLogs: { - INVOKE_CONNECTION_TRIGGERED: 'invoke connection triggered', - VALIDATE_CONNECTION_CONFIG: 'validate connection config', - EMIT_REQUEST: 'emit request %s', - INVOKE_CONNECTION_REQUEST_RESOLVED: 'invoke connection request resolved', - }, - errorLogs: { - INVOKE_CONNECTION_REQUEST_REJECTED: 'invoke connection request rejected', - }, - warnLogs: { - DEPRECATED_REQUEST_ID_PROPERTY: 'deprecated request_ID property', +import { SkyflowError } from "skyflow-common"; +import { SKYFLOW_ERROR_CODE } from "skyflow-common"; + +jest.mock("skyflow-common", () => { + const actual = jest.requireActual("skyflow-common"); + return { + ...actual, + fillUrlWithPathAndQueryParams: jest.fn(), + generateSDKMetrics: jest.fn(), + getBearerToken: jest.fn(), + parameterizedString: jest.fn((...args) => args.join(' ')), + printLog: jest.fn(), + objectToXML: jest.fn((obj, root) => `<${root}>${JSON.stringify(obj)}`), + logs: { + infoLogs: { + INVOKE_CONNECTION_TRIGGERED: 'invoke connection triggered', + VALIDATE_CONNECTION_CONFIG: 'validate connection config', + EMIT_REQUEST: 'emit request %s', + INVOKE_CONNECTION_REQUEST_RESOLVED: 'invoke connection request resolved', + }, + errorLogs: { + INVOKE_CONNECTION_REQUEST_REJECTED: 'invoke connection request rejected', + }, + warnLogs: { + DEPRECATED_REQUEST_ID_PROPERTY: 'deprecated request_ID property', + }, }, - }, -})); + }; +}); jest.mock("../../../src/utils/validations"); jest.mock("../../../src/vault/client", () => { return jest.fn().mockImplementation(() => ({})); diff --git a/test/vault/controller/deprecated-detokenize.test.js b/skyvault/test/vault/controller/deprecated-detokenize.test.js similarity index 81% rename from test/vault/controller/deprecated-detokenize.test.js rename to skyvault/test/vault/controller/deprecated-detokenize.test.js index a89ad7fe..1df961d4 100644 --- a/test/vault/controller/deprecated-detokenize.test.js +++ b/skyvault/test/vault/controller/deprecated-detokenize.test.js @@ -1,38 +1,32 @@ import VaultController from '../../../src/vault/controller/vault'; -import { printLog } from '../../../src/utils'; +import { printLog } from 'skyflow-common'; import DetokenizeResponse from '../../../src/vault/model/response/detokenize'; import { validateDetokenizeRequest } from '../../../src/utils/validations'; -jest.mock('../../../src/utils', () => ({ - printLog: jest.fn(), - parameterizedString: jest.fn(), - removeSDKVersion: jest.fn(), - generateSDKMetrics: jest.fn(), - getBearerToken: jest.fn().mockResolvedValue({ key: 'bearer-token' }), - MessageType: { LOG: 'LOG', ERROR: 'ERROR', WARN: 'WARN' }, - LogLevel: { DEBUG: 'DEBUG', INFO: 'INFO', WARN: 'WARN', ERROR: 'ERROR', OFF: 'OFF' }, - TYPES: { DETOKENIZE: 'DETOKENIZE' }, - HTTP_STATUS_CODE: { OK: 200 }, - SDK: { METRICS_HEADER_KEY: 'sky-metadata' }, - SKYFLOW: { ID: 'skyflowId' }, - CONTENT_TYPE: { APPLICATION_JSON: 'application/json' }, - ENCODING_TYPE: { UTF8: 'utf8' }, - RedactionType: { DEFAULT: 'DEFAULT', PLAIN_TEXT: 'PLAIN_TEXT' }, -})); +jest.mock('skyflow-common', () => { + const actual = jest.requireActual('skyflow-common'); + return { + ...actual, + printLog: jest.fn(), + parameterizedString: jest.fn(), + removeSDKVersion: jest.fn(), + generateSDKMetrics: jest.fn(), + getBearerToken: jest.fn().mockResolvedValue({ key: 'bearer-token' }), + logs: { + infoLogs: { CONTROLLER_INITIALIZED: 'init', EMIT_REQUEST: 'emit' }, + errorLogs: { DETOKENIZE_REQUEST_REJECTED: 'rejected' }, + warnLogs: { + DEPRECATED_REQUEST_ID_PROPERTY: "[DEPRECATED] Property 'request_ID' is deprecated and will be removed in an upcoming release. Use 'requestId' instead.", + DEPRECATED_SKYFLOW_ID_PROPERTY: "[DEPRECATED] Property 'skyflow_id' is deprecated.", + }, + }, + }; +}); jest.mock('../../../src/utils/validations', () => ({ validateDetokenizeRequest: jest.fn(), })); -jest.mock('../../../src/utils/logs', () => ({ - infoLogs: { CONTROLLER_INITIALIZED: 'init', EMIT_REQUEST: 'emit' }, - errorLogs: { DETOKENIZE_REQUEST_REJECTED: 'rejected' }, - warnLogs: { - DEPRECATED_REQUEST_ID_PROPERTY: "[DEPRECATED] Property 'request_ID' is deprecated and will be removed in an upcoming release. Use 'requestId' instead.", - DEPRECATED_SKYFLOW_ID_PROPERTY: "[DEPRECATED] Property 'skyflow_id' is deprecated.", - }, -})); - // ─── SHARED SETUP ───────────────────────────────────────────────────────────── const REQ_ID = 'req-detok-001'; diff --git a/test/vault/controller/deprecated-insert.test.js b/skyvault/test/vault/controller/deprecated-insert.test.js similarity index 88% rename from test/vault/controller/deprecated-insert.test.js rename to skyvault/test/vault/controller/deprecated-insert.test.js index 8b256239..0d48dd93 100644 --- a/test/vault/controller/deprecated-insert.test.js +++ b/skyvault/test/vault/controller/deprecated-insert.test.js @@ -1,37 +1,32 @@ import VaultController from '../../../src/vault/controller/vault'; -import { printLog } from '../../../src/utils'; +import { printLog } from 'skyflow-common'; import InsertResponse from '../../../src/vault/model/response/insert'; import { validateInsertRequest } from '../../../src/utils/validations'; -jest.mock('../../../src/utils', () => ({ - printLog: jest.fn(), - parameterizedString: jest.fn(), - removeSDKVersion: jest.fn(), - generateSDKMetrics: jest.fn(), - getBearerToken: jest.fn().mockResolvedValue({ key: 'bearer-token' }), - MessageType: { LOG: 'LOG', ERROR: 'ERROR', WARN: 'WARN' }, - LogLevel: { DEBUG: 'DEBUG', INFO: 'INFO', WARN: 'WARN', ERROR: 'ERROR', OFF: 'OFF' }, - TYPES: { INSERT: 'INSERT', INSERT_BATCH: 'INSERT_BATCH' }, - HTTP_STATUS_CODE: { OK: 200, BAD_REQUEST: 400 }, - SDK: { METRICS_HEADER_KEY: 'sky-metadata' }, - SKYFLOW: { ID: 'skyflowId' }, - CONTENT_TYPE: { APPLICATION_JSON: 'application/json' }, - ENCODING_TYPE: { UTF8: 'utf8' }, -})); +jest.mock('skyflow-common', () => { + const actual = jest.requireActual('skyflow-common'); + return { + ...actual, + printLog: jest.fn(), + parameterizedString: jest.fn(), + removeSDKVersion: jest.fn(), + generateSDKMetrics: jest.fn(), + getBearerToken: jest.fn().mockResolvedValue({ key: 'bearer-token' }), + logs: { + infoLogs: { CONTROLLER_INITIALIZED: 'init', EMIT_REQUEST: 'emit' }, + errorLogs: { INSERT_REQUEST_REJECTED: 'rejected' }, + warnLogs: { + DEPRECATED_SKYFLOW_ID_PROPERTY: "[DEPRECATED] Property 'skyflow_id' is deprecated and will be removed in an upcoming release. Use 'skyflowId' instead.", + DEPRECATED_REQUEST_ID_PROPERTY: "[DEPRECATED] Property 'request_ID' is deprecated and will be removed in an upcoming release. Use 'requestId' instead.", + }, + }, + }; +}); jest.mock('../../../src/utils/validations', () => ({ validateInsertRequest: jest.fn(), })); -jest.mock('../../../src/utils/logs', () => ({ - infoLogs: { CONTROLLER_INITIALIZED: 'init', EMIT_REQUEST: 'emit' }, - errorLogs: { INSERT_REQUEST_REJECTED: 'rejected' }, - warnLogs: { - DEPRECATED_SKYFLOW_ID_PROPERTY: "[DEPRECATED] Property 'skyflow_id' is deprecated and will be removed in an upcoming release. Use 'skyflowId' instead.", - DEPRECATED_REQUEST_ID_PROPERTY: "[DEPRECATED] Property 'request_ID' is deprecated and will be removed in an upcoming release. Use 'requestId' instead.", - }, -})); - // ─── SHARED SETUP ───────────────────────────────────────────────────────────── function makeClient(overrides = {}) { diff --git a/test/vault/controller/detect.test.js b/skyvault/test/vault/controller/detect.test.js similarity index 97% rename from test/vault/controller/detect.test.js rename to skyvault/test/vault/controller/detect.test.js index c01dccb1..5d55678d 100644 --- a/test/vault/controller/detect.test.js +++ b/skyvault/test/vault/controller/detect.test.js @@ -7,7 +7,7 @@ import DeidentifyFileOptions from '../../../src/vault/model/options/deidentify-f import TokenFormat from '../../../src/vault/model/options/deidentify-text/token-format'; import Transformations from '../../../src/vault/model/options/deidentify-text/transformations'; import { Bleep } from '../../../src/vault/model/options/deidentify-file/bleep-audio'; -import { DETECT_STATUS, ENCODING_TYPE, TYPES } from '../../../src/utils'; +import { DETECT_STATUS, ENCODING_TYPE, TYPES } from 'skyflow-common'; import fs from 'fs'; function createFullDeidentifyFileOptions() { @@ -42,76 +42,17 @@ function createFullDeidentifyFileOptions() { return options; } -jest.mock('../../../src/utils', () => ({ - printLog: jest.fn(), - parameterizedString: jest.fn(), - removeSDKVersion: jest.fn(), - MessageType: { - LOG: 'LOG', - ERROR: 'ERROR', - WARN: 'WARN', - }, - LogLevel: { - DEBUG: 'DEBUG', - INFO: 'INFO', - WARN: 'WARN', - ERROR: 'ERROR', - OFF: 'OFF', - }, - TYPES: { - DETECT: 'DETECT', - DEIDENTIFY_FILE: 'DEIDENTIFY_FILE', - DETECT_RUN: 'DETECT_RUN', - DEIDENTIFY_TEXT: 'DEIDENTIFY_TEXT', - REIDENTIFY_TEXT: 'REIDENTIFY_TEXT', - }, - DeidenitfyFileRequestTypes: { - AUDIO: 'AUDIO', - TEXT: 'TEXT', - PDF: 'PDF', - IMAGE: 'IMAGE', - PPT: 'PPT', - SPREADSHEET: 'SPREADSHEET', - STRUCTURED_TEXT: 'STRUCTURED_TEXT', - DOCUMENT: 'DOCUMENT', - FILE: 'FILE', - }, - DETECT_STATUS: { - IN_PROGRESS: 'IN_PROGRESS', - SUCCESS: 'SUCCESS', - FAILED: 'FAILED', - }, - SDK: { - METRICS_HEADER_KEY: 'sky-metadata', - }, - FILE_EXTENSION: { - JSON: 'json', - MP3: 'mp3', - WAV: 'wav', - }, - FILE_FORMAT_TYPE: { - TXT: 'txt', - PDF: 'pdf', - }, - FILE_PROCESSING: { - PROCESSED_PREFIX: 'processed-', - DEIDENTIFIED_PREFIX: 'deidentified.', - ENTITIES: 'entities', - }, - ENCODING_TYPE: { - UTF8: 'utf8', - BASE64: 'base64', - BINARY: 'binary', - UTF_8: 'utf-8', - }, - TokenType: { - ENTITY_UNIQUE_COUNTER: 'entity_unq_counter', - ENTITY_ONLY: 'entity_only', - VAULT_TOKEN: 'vault_token', - }, - generateSDKMetrics: jest.fn().mockReturnValue({ sdk: 'metrics' }), - getBearerToken: jest.fn().mockResolvedValue(Promise.resolve('your-bearer-token')), -})); +jest.mock('skyflow-common', () => { + const actual = jest.requireActual('skyflow-common'); + return { + ...actual, + printLog: jest.fn(), + parameterizedString: jest.fn(), + removeSDKVersion: jest.fn(), + generateSDKMetrics: jest.fn().mockReturnValue({ sdk: 'metrics' }), + getBearerToken: jest.fn().mockResolvedValue(Promise.resolve('your-bearer-token')), + }; +}); jest.mock('../../../src/utils/validations', () => ({ validateDeIdentifyTextRequest: jest.fn(), diff --git a/test/vault/controller/vault.test.js b/skyvault/test/vault/controller/vault.test.js similarity index 98% rename from test/vault/controller/vault.test.js rename to skyvault/test/vault/controller/vault.test.js index 3d2cc56e..68565fa5 100644 --- a/test/vault/controller/vault.test.js +++ b/skyvault/test/vault/controller/vault.test.js @@ -1,6 +1,6 @@ import VaultController from '../../../src/vault/controller/vault'; -import { printLog, MessageType, removeSDKVersion, LogLevel } from '../../../src/utils'; -import logs from '../../../src/utils/logs'; +import { printLog, MessageType, removeSDKVersion, LogLevel } from 'skyflow-common'; +import { logs } from 'skyflow-common'; import { validateInsertRequest, validateDetokenizeRequest, validateDeleteRequest, validateTokenizeRequest, validateQueryRequest, validateUpdateRequest, validateUploadFileRequest, validateGetRequest, validateGetColumnRequest } from '../../../src/utils/validations'; import InsertResponse from '../../../src/vault/model/response/insert'; import DeleteResponse from '../../../src/vault/model/response/delete'; @@ -11,7 +11,7 @@ import FileUploadResponse from '../../../src/vault/model/response/file-upload'; import GetResponse from '../../../src/vault/model/response/get'; import GetRequest from '../../../src/vault/model/request/get'; import GetColumnRequest from '../../../src/vault/model/request/get-column'; -import SkyflowError from '../../../src/error'; +import { SkyflowError } from 'skyflow-common'; import * as fs from 'fs'; jest.mock('fs', () => ({ @@ -41,64 +41,29 @@ if (typeof File === 'undefined') { }; } -jest.mock('../../../src/utils', () => ({ - printLog: jest.fn(), - parameterizedString: jest.fn(), - removeSDKVersion: jest.fn(), - MessageType: { - LOG: 'LOG', - ERROR: 'ERROR', - WARN: 'WARN', - }, - RedactionType: { - DEFAULT: 'DEFAULT', - PLAIN_TEXT: 'PLAIN_TEXT', - MASKED: 'MASKED', - REDACTED: 'REDACTED', - }, - SDK: { - METRICS_HEADER_KEY: 'sky-metadata', - }, - SKYFLOW: { - ID: 'skyflowId', - LEGACY_ID: 'skyflow_id', - }, - CONTENT_TYPE: { - APPLICATION_JSON: 'application/json', - APPLICATION_X_WWW_FORM_URLENCODED: 'application/x-www-form-urlencoded', - TEXT_PLAIN: 'text/plain', - }, - ENCODING_TYPE: { - UTF8: 'utf8', - BASE64: 'base64', - BINARY: 'binary', - UTF_8: 'utf-8', - }, - TYPES: { - INSERT: 'INSERT', - INSERT_BATCH: 'INSERT_BATCH', - DETOKENIZE: 'DETOKENIZE', - TOKENIZE: 'TOKENIZE', - DELETE: 'DELETE', - UPDATE: 'UPDATE', - GET: 'GET', - FILE_UPLOAD: 'FILE_UPLOAD', - QUERY: 'QUERY', - INVOKE_CONNECTION: 'INVOKE_CONNECTION', - }, - generateSDKMetrics: jest.fn(), - getBearerToken: jest.fn().mockResolvedValue(Promise.resolve('your-bearer-token')), - HTTP_STATUS_CODE: { - OK: 200, - BAD_REQUEST: 400, - INTERNAL_SERVER_ERROR: 500, - }, - HTTP_HEADER: { - CONTENT_TYPE: 'Content-Type', - X_REQUEST_ID: 'x-request-id', - }, - SkyflowRecordError: {}, -})); +jest.mock('skyflow-common', () => { + const actual = jest.requireActual('skyflow-common'); + return { + ...actual, + printLog: jest.fn(), + parameterizedString: jest.fn(), + removeSDKVersion: jest.fn(), + generateSDKMetrics: jest.fn(), + getBearerToken: jest.fn().mockResolvedValue(Promise.resolve('your-bearer-token')), + logs: { + infoLogs: { + CONTROLLER_INITIALIZED: 'VaultController initialized successfully.', + }, + errorLogs: { + INSERT_REQUEST_REJECTED: 'INSERT_REJECTED', + }, + warnLogs: { + DEPRECATED_SKYFLOW_ID_PROPERTY: "[DEPRECATED] Property 'skyflow_id' is deprecated and will be removed in an upcoming release. Use 'skyflowId' instead.", + DEPRECATED_REQUEST_ID_PROPERTY: "[DEPRECATED] Property 'request_ID' is deprecated and will be removed in an upcoming release. Use 'requestId' instead.", + }, + }, + }; +}); jest.mock('../../../src/utils/validations', () => ({ validateInsertRequest: jest.fn(), @@ -112,19 +77,6 @@ jest.mock('../../../src/utils/validations', () => ({ validateGetColumnRequest: jest.fn(), })); -jest.mock('../../../src/utils/logs', () => ({ - infoLogs: { - CONTROLLER_INITIALIZED: 'VaultController initialized successfully.', - }, - errorLogs: { - INSERT_REQUEST_REJECTED: 'INSERT_REJECTED', - }, - warnLogs: { - DEPRECATED_SKYFLOW_ID_PROPERTY: "[DEPRECATED] Property 'skyflow_id' is deprecated and will be removed in an upcoming release. Use 'skyflowId' instead.", - DEPRECATED_REQUEST_ID_PROPERTY: "[DEPRECATED] Property 'request_ID' is deprecated and will be removed in an upcoming release. Use 'requestId' instead.", - }, -})); - describe('VaultController', () => { let mockVaultClient; diff --git a/test/vault/model/deprecated.test.js b/skyvault/test/vault/model/deprecated.test.js similarity index 96% rename from test/vault/model/deprecated.test.js rename to skyvault/test/vault/model/deprecated.test.js index 8fbf655d..1169c62b 100644 --- a/test/vault/model/deprecated.test.js +++ b/skyvault/test/vault/model/deprecated.test.js @@ -1,23 +1,12 @@ -jest.mock("../../../src/utils", () => ({ - printLog: jest.fn(), - MessageType: { LOG: "LOG", ERROR: "ERROR", WARN: "WARN" }, - LogLevel: { - DEBUG: "DEBUG", - INFO: "INFO", - WARN: "WARN", - ERROR: "ERROR", - OFF: "OFF", - }, - OrderByEnum: { ASC: "ASC", DESC: "DESC" }, - RedactionType: { - DEFAULT: "DEFAULT", - PLAIN_TEXT: "PLAIN_TEXT", - MASKED: "MASKED", - REDACTED: "REDACTED", - }, -})); - -import { printLog, OrderByEnum, RedactionType } from "../../../src/utils"; +jest.mock("skyflow-common", () => { + const actual = jest.requireActual("skyflow-common"); + return { + ...actual, + printLog: jest.fn(), + }; +}); + +import { printLog, OrderByEnum, RedactionType } from "skyflow-common"; import DetokenizeOptions from "../../../src/vault/model/options/detokenize"; import GetOptions from "../../../src/vault/model/options/get"; import FileUploadRequest from "../../../src/vault/model/request/file-upload"; diff --git a/test/vault/skyflow/skyflow.test.js b/skyvault/test/vault/skyflow/skyflow.test.js similarity index 100% rename from test/vault/skyflow/skyflow.test.js rename to skyvault/test/vault/skyflow/skyflow.test.js diff --git a/skyvault/tsconfig.dts.json b/skyvault/tsconfig.dts.json new file mode 100644 index 00000000..8d46eaf5 --- /dev/null +++ b/skyvault/tsconfig.dts.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2019", + "module": "commonjs", + "moduleResolution": "node", + "lib": ["es2015", "es2017", "es2019", "dom"], + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "allowJs": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": false, + "noImplicitAny": false, + "declaration": true, + "emitDeclarationOnly": true, + "composite": false, + "incremental": false, + "stripInternal": true, + "types": ["node"], + "rootDir": "src" + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "lib", "dist", "test"] +} diff --git a/skyvault/tsconfig.json b/skyvault/tsconfig.json new file mode 100644 index 00000000..c228d95f --- /dev/null +++ b/skyvault/tsconfig.json @@ -0,0 +1,30 @@ +{ + "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"] + }, + "references": [{ "path": "../common" }], + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "lib", "test"] +} diff --git a/skyvault/tsup.config.ts b/skyvault/tsup.config.ts new file mode 100644 index 00000000..067ef014 --- /dev/null +++ b/skyvault/tsup.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'tsup'; + +// Bundles the published entry, inlining the private `skyflow-common` workspace +// package so the tarball is self-contained (no skyflow-common dependency ships). +// Real npm dependencies stay external (declared in package.json "dependencies"). +export default defineConfig({ + entry: { index: 'src/index.ts' }, + format: ['cjs'], + outDir: 'dist', + target: 'es2019', + platform: 'node', + noExternal: ['skyflow-common'], + tsconfig: 'tsconfig.dts.json', + dts: true, + sourcemap: true, + clean: true, + splitting: false, + shims: false, +}); diff --git a/src/error/messages/index.ts b/src/error/messages/index.ts deleted file mode 100644 index 2b634c84..00000000 --- a/src/error/messages/index.ts +++ /dev/null @@ -1,252 +0,0 @@ -import sdkDetails from "../../../package.json"; - -const errorPrefix = `Skyflow Node SDK v${sdkDetails.version}`; - -const errorMessages = { - CONFIG_MISSING: `${errorPrefix} Initialization failed. Skyflow config cannot be empty. Specify a valid skyflow config.`, - INVALID_SKYFLOW_CONFIG: `${errorPrefix} Initialization failed. Invalid skyflow config. Vault/Connection config key missing in skyflow config.`, - INVALID_TYPE_FOR_CONFIG: `${errorPrefix} Initialization failed. Invalid %s1 config. Specify a valid %s1 config.`, - EMPTY_VAULT_CONFIG: `${errorPrefix} Initialization failed. Vault config cannot be empty. Specify a valid vault config.`, - EMPTY_CONNECTION_CONFIG: `${errorPrefix} Initialization failed. Connection config cannot be empty. Specify a valid connection config.`, - - EMPTY_VAULT_ID: `${errorPrefix} Initialization failed. Invalid vault ID. Specify a valid vault Id.`, - EMPTY_VAULT_ID_VALIDATION: `${errorPrefix} Validation error. Invalid vault ID. Specify a valid vault Id.`, - INVALID_VAULT_ID: `${errorPrefix} Initialization failed. Invalid vault ID. Specify a valid vault Id as a string.`, - EMPTY_CLUSTER_ID: `${errorPrefix} Initialization failed. Invalid cluster ID. Specify a valid cluster Id for vault with vaultId %s1 .`, - INVALID_CLUSTER_ID: `${errorPrefix} Initialization failed. Invalid cluster ID. Specify cluster Id as a string for vault with vaultId %s1.`, - INVALID_TOKEN: `${errorPrefix} Validation error. Invalid token. Specify a valid token.`, - TOKEN_EXPIRED: `${errorPrefix} Validation error. Token provided is either invalid or has expired. Specify a valid token.`, - INVALID_ENV: `${errorPrefix} Initialization failed. Invalid env. Specify a valid env for vault with vaultId %s1.`, - INVALID_LOG_LEVEL: `${errorPrefix} Initialization failed. Invalid log level. Specify a valid log level.`, - EMPTY_CREDENTIAL_FILE_PATH: `${errorPrefix} Initialization failed. Invalid credentials. Specify a valid file path.`, - INVALID_CREDENTIAL_FILE_PATH: `${errorPrefix} Initialization failed. Invalid credentials. Expected file path to be a string.`, - - INVALID_CREDENTIALS_FILE_PATH: `${errorPrefix} Initialization failed. Invalid skyflow credentials. Expected file path to exists.`, - INVALID_TOKEN_URI: `${errorPrefix} Initialization failed. Invalid Skyflow credentials. The token URI must be a string and a valid URL.`, - INVALID_TOKEN_URI_WITH_ID: `${errorPrefix} Initialization failed. Invalid Skyflow credentials. The token URI must be a string and a valid URL for %s1 with %s2 %s3.`, - INVALID_KEY: `${errorPrefix} Initialization failed. Invalid skyflow credentials. Specify a valid api key.`, - INVALID_PARSED_CREDENTIALS_STRING: `${errorPrefix} Initialization failed. Invalid skyflow credentials. Specify a valid credentials string.`, - INVALID_BEARER_TOKEN: `${errorPrefix} Initialization failed. Invalid skyflow credentials. Bearer token is invalid or expired. Specify a valid token.`, - - INVALID_FILE_PATH_WITH_ID: `${errorPrefix} Initialization failed. Invalid credentials. Expected file path to exists for %s1 with %s2 %s3.`, - INVALID_KEY_WITH_ID: `${errorPrefix} Initialization failed. Invalid credentials. Specify a valid api key for %s1 with %s2 %s3.`, - INVALID_PARSED_CREDENTIALS_STRING_WITH_ID: `${errorPrefix} Initialization failed. Invalid credentials. Specify a valid credentials string for %s1 with %s2 %s3.`, - INVALID_BEARER_TOKEN_WITH_ID: `${errorPrefix} Initialization failed. Invalid credentials. Bearer token is invalid or expired. Specify a valid token for %s1 with %s2 %s3.`, - - EMPTY_CONNECTION_ID_VALIDATION: `${errorPrefix} Validation error. Invalid connection ID. Specify a valid connection Id.`, - EMPTY_CONNECTION_ID: `${errorPrefix} Initialization failed. Invalid connection ID. Specify a valid connection Id.`, - INVALID_CONNECTION_ID: `${errorPrefix} Initialization failed. Invalid connection ID. Specify connection Id as a string.`, - EMPTY_CONNECTION_URL: `${errorPrefix} Initialization failed. Invalid connection URL. Specify a valid connection Url.`, - INVALID_CONNECTION_URL: `${errorPrefix} Initialization failed. Invalid connection URL. Specify connection Url as a valid url.`, - - VAULT_ID_EXITS_IN_CONFIG_LIST: `${errorPrefix} Validation error. %s1 already exists in the config list. Specify a new vaultId.`, - CONNECTION_ID_EXITS_IN_CONFIG_LIST: `${errorPrefix} Validation error. %s1 already exists in the config list. Specify a new vaultId.`, - VAULT_ID_NOT_IN_CONFIG_LIST: `${errorPrefix} Validation error. %s1 is missing from the config. Specify the vaultId's from config.`, - CONNECTION_ID_NOT_IN_CONFIG_LIST: `${errorPrefix} Validation error. %s1 is missing from the config. Specify the connectionIds from config.`, - - EMPTY_CREDENTIALS: `${errorPrefix} Validation error. Invalid credentials. Credentials must not be empty.`, - INVALID_CREDENTIALS: `${errorPrefix} Validation error. Invalid credentials. Specify a valid credentials.`, - CREDENTIALS_WITH_NO_VALID_KEY: `${errorPrefix} Validation error. Invalid credentials. Credentials must include one of the following: { apiKey, token, credentials, path }.`, - MULTIPLE_CREDENTIALS_PASSED: `${errorPrefix} Validation error. Multiple credentials provided. Specify only one of the following: { apiKey, token, credentials, path }.`, - INVALID_CREDENTIALS_WITH_ID: `${errorPrefix} Validation error. Invalid credentials. Credentials must include one of the following: { apiKey, token, credentials, path } for %s1 with %s2 %s3.`, - MULTIPLE_CREDENTIALS_PASSED_WITH_ID: `${errorPrefix} Validation error. Invalid credentials.Specify only one of the following: { apiKey, token, credentials, path } for %s1 with %s2 %s3.`, - - FILE_NOT_FOUND: `${errorPrefix} Validation error. Credential file not found at %s1. Verify the file path.`, - INVALID_JSON_FILE: `${errorPrefix} Validation error. File at %s1 is not in valid JSON format. Verify the file contents.`, - - EMPTY_CREDENTIALS_STRING: `${errorPrefix} Validation error. Invalid credentials. Specify a valid credentials.`, - INVALID_CREDENTIALS_STRING: `${errorPrefix} Validation error. Invalid credentials. Specify credentials as a string.`, - - MISSING_PRIVATE_KEY: `${errorPrefix} Validation error. Unable to read private key in credentials. Verify your private key.`, - MISSING_CLIENT_ID: `${errorPrefix} Validation error. Unable to read client ID in credentials. Verify your client ID.`, - MISSING_KEY_ID: `${errorPrefix} Validation error. Unable to read key ID in credentials. Verify your key ID.`, - MISSING_TOKEN_URI: `${errorPrefix} Validation error. Unable to read token URI in credentials. Verify your token URI.`, - - INVALID_ROLES_KEY_TYPE: `${errorPrefix} Validation error. Invalid roles. Specify roles as an array.`, - INVALID_CONTEXT: `${errorPrefix} Validation error. Invalid context. Specify context as a string.`, - EMPTY_ROLES: `${errorPrefix} Validation error. Invalid roles. Specify at least one role.`, - - INVALID_JSON_FORMAT: `${errorPrefix} Validation error. Credentials is not in valid JSON format. Verify the credentials.`, - - EMPTY_DATA_TOKENS: `${errorPrefix} Validation error. Invalid data tokens. Specify valid data tokens.`, - DATA_TOKEN_KEY_TYPE: `${errorPrefix} Validation error. Invalid data tokens. Specify data token as an string array.`, - TIME_TO_LIVE_KET_TYPE: `${errorPrefix} Validation error. Invalid time to live. Specify time to live parameter as an string.`, - - INVALID_DELETE_IDS_INPUT: `${errorPrefix} Validation error. Invalid delete ids type in delete request. Specify delete ids as a string array.`, - EMPTY_DELETE_IDS: `${errorPrefix} Validation error. Delete ids array cannot be empty. Specify id's in delete request.`, - INVALID_ID_IN_DELETE: `${errorPrefix} Validation error. Invalid type of id passed in delete request. Id must be of type string at index %s1.`, - INVALID_DELETE_REQUEST: `${errorPrefix} Validation error. Invalid delete request. Specify a valid delete request.`, - EMPTY_ID_IN_DELETE: `${errorPrefix} Validation error. Id cannot be empty in delete request. Specify a valid id.`, - - MISSING_REDACTION_TYPE_IN_DETOKENIZE: `${errorPrefix} Validation error. Redaction type cannot be empty in detokenize request. Specify the redaction type.`, - INVALID_REDACTION_TYPE_IN_DETOKENIZE: `${errorPrefix} Validation error. Invalid redaction type in detokenize request. Specify a redaction type.`, - INVALID_TOKENS_TYPE_IN_DETOKENIZE: `${errorPrefix} Validation error. Invalid tokens type in detokenize request. Specify tokens as a string array.`, - EMPTY_TOKENS_IN_DETOKENIZE: `${errorPrefix} Validation error. Tokens array cannot be empty. Specify token's in detokenize request.`, - EMPTY_TOKEN_IN_DETOKENIZE: `${errorPrefix} Validation error. Token cannot be empty in detokenize request. Specify a valid token at index %s1.`, - INVALID_TOKEN_IN_DETOKENIZE: `${errorPrefix} Validation error. Invalid type of token passed in detokenize request. token must be of type string at index %s1.`, - INVALID_DETOKENIZE_REQUEST: `${errorPrefix} Validation error. Invalid detokenize request. Specify a valid detokenize request.`, - - MISSING_VALUES_IN_TOKENIZE: `${errorPrefix} Validation error. Values cannot be empty in tokenize request. Specify valid values.`, - INVALID_VALUES_TYPE_IN_TOKENIZE: `${errorPrefix} Validation error. Invalid values type in tokenize request. Specify valid values of type array.`, - EMPTY_VALUES_IN_TOKENIZE: `${errorPrefix} Validation error. Values array cannot be empty. Specify value's in tokenize request.`, - EMPTY_DATA_IN_TOKENIZE: `${errorPrefix} Validation error. Data cannot be empty in tokenize request. Specify a valid data at index %s1.`, - INVALID_DATA_IN_TOKENIZE: `${errorPrefix} Validation error. Invalid Data. Specify a valid data at index %s1.`, - INVALID_COLUMN_GROUP_IN_TOKENIZE: `${errorPrefix} Validation error. Invalid type of column group passed in tokenize request. Column group must be of type string at index %s1.`, - INVALID_VALUE_IN_TOKENIZE: `${errorPrefix} Validation error. Invalid type of value passed in tokenize request. Value must be of type string at index %s1.`, - INVALID_TOKENIZE_REQUEST: `${errorPrefix} Validation error. Invalid tokenize request. Specify a valid tokenize request.`, - EMPTY_COLUMN_GROUP_IN_TOKENIZE: `${errorPrefix} Validation error. Column group cannot be empty in tokenize request. Specify a valid column group at index %s1.`, - EMPTY_VALUE_IN_TOKENIZE: `${errorPrefix} Validation error. Value cannot be empty in tokenize request. Specify a valid value at index %s1.`, - - INVALID_RECORD_IN_INSERT: `${errorPrefix} Validation error. Invalid data in insert request. data must be of type object at index %s1.`, - INVALID_RECORD_IN_UPDATE: `${errorPrefix} Validation error. Invalid data in update request. data must be of type object.`, - EMPTY_RECORD_IN_INSERT: `${errorPrefix} Validation error. Data cannot be empty in insert request. Specify valid data at index %s1.`, - INVALID_INSERT_REQUEST: `${errorPrefix} Validation error. Invalid insert request. Specify a valid insert request.`, - INVALID_TYPE_OF_DATA_IN_INSERT: `${errorPrefix} Validation error. Invalid type of data in insert request. Specify data as a object array.`, - EMPTY_DATA_IN_INSERT: `${errorPrefix} Validation error. Data array cannot be empty. Specify data in insert request.`, - - INVALID_QUERY_REQUEST: `${errorPrefix} Validation error. Invalid query request. Specify a valid query request.`, - EMPTY_QUERY: `${errorPrefix} Validation error. Query cannot be empty in query request. Specify a valid query.`, - INVALID_QUERY: `${errorPrefix} Validation error. Invalid query in query request. Specify a valid query.`, - - INVALID_FILE_UPLOAD_REQUEST: `${errorPrefix} Validation error. Invalid file upload request. Specify a valid file upload request.`, - MISSING_TABLE_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Table name cannot be empty in file upload request. Specify table name as a string.`, - INVALID_TABLE_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Invalid table name in file upload request. Specify a valid table name.`, - MISSING_SKYFLOW_ID_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Skyflow id cannot be empty in file upload request. Specify a valid skyflow Id as string.`, - INVALID_SKYFLOW_ID_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Invalid skyflow Id in file upload request. Specify a valid skyflow Id.`, - MISSING_COLUMN_NAME_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Column name cannot be empty in file upload request. Specify a valid column name as string.`, - INVALID_COLUMN_NAME_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Invalid column name in file upload request. Specify a valid column name.`, - MISSING_FILE_PATH_IN_UPLOAD_FILE: `${errorPrefix} Validation error. File path cannot be empty in file upload request. Specify a valid file path as string.`, - INVALID_FILE_PATH_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Invalid file path in file upload request. Specify a valid file path.`, - MISSING_FILE_SOURCE_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Provide exactly one of filePath, base64, or fileObject.`, - MISSING_FILE_NAME_FOR_BASE64: `${errorPrefix} Validation error. File name is required when providing a base64 string`, - INVALID_FILE_OBJECT_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Invalid file object in file upload request. Specify a valid file object.`, - MISSING_FILE_NAME_IN_FILE_OBJECT: `${errorPrefix} Validation error. File name is required when providing a file object. Specify a valid file name.`, - INVALID_BASE64_IN_UPLOAD_FILE: `${errorPrefix} Validation error. Invalid base64 string in file upload request. Specify a valid base64 string.`, - - MISSING_SKYFLOW_ID_IN_UPDATE: `${errorPrefix} Validation error. Skyflow id name cannot be empty in update request. Specify a skyflow Id name as string.`, - INVALID_SKYFLOW_ID_IN_UPDATE: `${errorPrefix} Validation error. Invalid skyflow Id in update request. Specify a valid skyflow Id.`, - INVALID_TYPE_OF_UPDATE_DATA: `${errorPrefix} Validation error. Invalid update data in update request. Specify a valid update data as array of object.`, - EMPTY_UPDATE_DATA: `${errorPrefix} Validation error. Update data cannot be empty in update request. Specify a valid update data.`, - INVALID_UPDATE_REQUEST: `${errorPrefix} Validation error. Invalid update request. Specify a valid update request.`, - INVALID_DATA_IN_UPDATE: `${errorPrefix} Validation error. Invalid data in update request. data must be of type object at index %s1.`, - EMPTY_DATA_IN_UPDATE: `${errorPrefix} Validation error. Data cannot be empty in update request. Specify a valid data at index %s1.`, - INVALID_UPDATE_TOKENS: `${errorPrefix} Validation error. Invalid tokens. Specify valid tokens as object.`, - INVALID_TOKEN_IN_UPDATE: `${errorPrefix} Validation error. Invalid tokens. Specify valid tokens as key value pairs.`, - - EMPTY_TABLE_NAME: `${errorPrefix} Validation error. Table name cannot be empty. Specify a valid table name.`, - INVALID_TABLE_NAME: `${errorPrefix} Validation error. Invalid table name. Specify a valid table name as string.`, - EMPTY_REDACTION_TYPE: `${errorPrefix} Validation error. Redaction type cannot be empty. Specify a valid redaction type.`, - INVALID_REDACTION_TYPE: `${errorPrefix} Validation error. Invalid redaction type. Specify a valid redaction type.`, - - INVALID_TYPE_OF_IDS: `${errorPrefix} Validation error. Invalid ids passed in get request. Specify valid ids as array of string.`, - EMPTY_IDS_IN_GET: `${errorPrefix} Validation error. Ids cannot be empty in get request. Specify valid ids.`, - EMPTY_ID_IN_GET: `${errorPrefix} Validation error. Id cannot be empty. Specify a valid Id at index %s1.`, - INVALID_ID_IN_GET: `${errorPrefix} Validation error. Invalid Id. Specify a valid Id at index %s1 as string.`, - INVALID_GET_REQUEST: `${errorPrefix} Validation error. Invalid get request. Specify a valid get request.`, - - EMPTY_COLUMN_NAME: `${errorPrefix} Validation error. Column name cannot be empty. Specify a valid column name.`, - INVALID_COLUMN_NAME: `${errorPrefix} Validation error. Invalid column name. Specify a valid column name as string.`, - INVALID_COLUMN_VALUES: `${errorPrefix} Validation error. Invalid column values. Specify valid column values as string array.`, - EMPTY_COLUMN_VALUES: `${errorPrefix} Validation error. Column values cannot be empty. Specify valid column values.`, - EMPTY_COLUMN_VALUE: `${errorPrefix} Validation error. Column value cannot be empty. Specify a valid column value at index %s1.`, - INVALID_COLUMN_VALUE: `${errorPrefix} Validation error. Invalid column value. Specify a valid column value at index %s1 as string.`, - INVALID_GET_COLUMN_REQUEST: `${errorPrefix} Validation error. Invalid get column request. Specify a valid get column request.`, - - EMPTY_URL: `${errorPrefix} Validation error. Url cannot be empty. Specify a valid url.`, - INVALID_URL: `${errorPrefix} Validation error. Invalid url. Specify a valid url as a string.`, - EMPTY_METHOD_NAME: `${errorPrefix} Validation error. Method name cannot be empty. Specify a valid method name.`, - INVALID_METHOD_NAME: `${errorPrefix} Validation error. Invalid method name. Specify a valid method name as a string.`, - - EMPTY_PATH_PARAMS: `${errorPrefix} Validation error. Path params cannot be empty. Specify valid path params.`, - INVALID_PATH_PARAMS: `${errorPrefix} Validation error. Invalid path params. Specify valid path params.`, - EMPTY_QUERY_PARAMS: `${errorPrefix} Validation error. Query params cannot be empty. Specify valid query params.`, - INVALID_QUERY_PARAMS: `${errorPrefix} Validation error. Invalid query params. Specify valid query params.`, - EMPTY_BODY: `${errorPrefix} Validation error. Body cannot be empty. Specify a valid body.`, - INVALID_BODY: `${errorPrefix} Validation error. Invalid body. Specify a valid body.`, - EMPTY_HEADERS: `${errorPrefix} Validation error. Headers cannot be empty. Specify valid headers.`, - INVALID_HEADERS: `${errorPrefix} Validation error. Invalid headers. Specify valid headers.`, - - INVALID_INVOKE_CONNECTION_REQUEST: `${errorPrefix} Validation error. Invalid invoke connection request. Specify a valid get invoke connection request.`, - ERROR_OCCURRED: `${errorPrefix} API error. Error occurred.`, - - EMPTY_INSERT_TOKEN: `${errorPrefix} Validation error. Tokens object cannot be empty. Specify a valid tokens object at index %s1.`, - INVALID_INSERT_TOKEN: `${errorPrefix} Validation error. Invalid tokens object. Specify a valid tokens object at index %s1.`, - INVALID_INSERT_TOKENS: `${errorPrefix} Validation error. Invalid tokens. Specify valid tokens as object array.`, - INVALID_TOKEN_MODE: `${errorPrefix} Validation error. The token mode key has a value of type %s1. Specify token as type of TokenMode.`, - INVALID_HOMOGENEOUS: `${errorPrefix} Validation error. The homogeneous key has a value of type %s1. Specify homogeneous as boolean.`, - INVALID_TOKEN_STRICT: `${errorPrefix} Validation error. The tokenStrict key has a value of type %s1. Specify tokenStrict as boolean.`, - INVALID_CONTINUE_ON_ERROR: `${errorPrefix} Validation error. The continueOnError key has a value of type %s1. Specify continueOnError as boolean.`, - INVALID_UPSERT: `${errorPrefix} Validation error. The upsert key has a value of type %s1. Specify upsert as string.`, - INVALID_RETURN_TOKEN: `${errorPrefix} Validation error. The returnToken key has a value of type %s1. Specify returnToken as boolean.`, - - NO_TOKENS_WITH_TOKEN_MODE: `${errorPrefix} Validation error. Tokens weren't specified for records while 'tokenMode' was ENABLE or ENABLE_STRICT` , - INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT: `${errorPrefix} Validation error. 'tokenMode' is set to 'ENABLE_STRICT', but some fields are missing tokens. Specify tokens for all fields.`, - - INVALID_DOWNLOAD_URL: `${errorPrefix} Validation error. The downloadURL key has a value of type %s1. Specify downloadURL as string.`, - - EMPTY_FIELD: `${errorPrefix} Validation error. Filed value cannot be empty. Specify a valid filed value at index %s1.`, - INVALID_FIELD: `${errorPrefix} Validation error. Invalid filed value. Specify a valid filed value at index %s1 as string.`, - - INVALID_OFFSET: `${errorPrefix} Validation error. The offset key has a value of type %s1. Specify offset as string.`, - INVALID_LIMIT: `${errorPrefix} Validation error. The limit key has a value of type %s1. Specify limit as string.`, - - INVALID_ORDER_BY: `${errorPrefix} Validation error. The orderBy key has a value of type %s1. Specify orderBy as string.`, - INVALID_FIELDS: `${errorPrefix} Validation error. The fields key has a value of type %s1. Specify fields as array of strings.`, - - INVAILD_JSON_RESPONSE: `${errorPrefix} Validation error. The invalid json response. Please reach out to skyflow using requestId - %s1.`, - - EMPTY_VAULT_CLIENTS: `${errorPrefix} Validation error. No vault config found. Please add a vault config`, - EMPTY_CONNECTION_CLIENTS: `${errorPrefix} Validation error. No connection config found. Please add a connection config`, - - GENERIC_API_ERROR: `${errorPrefix} Validation error. Invalid configuration. Please add a valid vault configuration`, - INVALID_TEXT_IN_DEIDENTIFY: `${errorPrefix} Validation error. The text field is required and must be a non-empty string. Specify a valid text.`, - INVALID_ENTITIES_IN_DEIDENTIFY: `${errorPrefix} Validation error. The entities field must be an array of DetectEntities enums. Specify a valid entities.`, - INVALID_ALLOW_REGEX_LIST: `${errorPrefix} Validation error. The allowRegexList field must be an array of strings. Specify a valid allowRegexList.`, - INVALID_RESTRICT_REGEX_LIST: `${errorPrefix} Validation error. The restrictRegexList field must be an array of strings. Specify a valid restrictRegexList.`, - INVALID_TOKEN_FORMAT: `${errorPrefix} Validation error. The tokenFormat key must be an instance of TokenFormat. Specify a valid token format.`, - INVALID_TRANSFORMATIONS: `${errorPrefix} Validation error. The transformations key must be an instance of Transformations. Specify a valid transformations.`, - VAULT_TOKEN_FORMAT_NOT_ALLOWED_FOR_DEIDENTIFY_FILES: `${errorPrefix} Validation error. Vault token format is not allowed for deidentify file request.`, - - INVALID_TEXT_IN_REIDENTIFY: `${errorPrefix} Validation error. The text field is required and must be a non-empty string. Specify a valid text.`, - INVALID_REDACTED_ENTITIES_IN_REIDENTIFY: `${errorPrefix} Validation error. The redactedEntities field must be an array of DetectEntities enums. Specify a valid redactedEntities.`, - INVALID_MASKED_ENTITIES_IN_REIDENTIFY: `${errorPrefix} Validation error. The maskedEntities field must be an array of DetectEntities enums. Specify a valid maskedEntities.`, - INVALID_PLAIN_TEXT_ENTITIES_IN_REIDENTIFY: `${errorPrefix} Validation error. The plainTextEntities field must be an array of DetectEntities enums. Specify a valid plainTextEntities.`, - - INVALID_DEIDENTIFY_FILE_REQUEST: `${errorPrefix} Validation error. Invalid deidentify file request. Specify a valid deidentify file request.`, - INVALID_DEIDENTIFY_FILE_INPUT: `${errorPrefix} Validation error. Invalid deidentify file input. Please provide either a file or a file path.`, - EMPTY_FILE_OBJECT: `${errorPrefix} Validation error. File object cannot be empty. Specify a valid file object.`, - INVALID_FILE_FORMAT: `${errorPrefix} Validation error. Invalid file format. Specify a valid file format.`, - MISSING_FILE_SOURCE: `${errorPrefix} Validation error. Provide exactly one of filePath, base64, or fileObject.`, - INVALID_FILE_OBJECT: `${errorPrefix} Validation error. Invalid file object. Specify a valid file object.`, - INVALID_BASE64_STRING: `${errorPrefix} Validation error. Invalid base64 string. Specify a valid base64 string.`, - INVALID_DEIDENTIFY_FILE_OPTIONS: `${errorPrefix} Validation error. Invalid deidentify file options. Specify a valid deidentify file options.`, - INVALID_ENTITIES: `${errorPrefix} Validation error. Invalid entities. Specify valid entities as string array.`, - EMPTY_ENTITIES: `${errorPrefix} Validation error. Entities cannot be empty. Specify valid entities.`, - EMPTY_ALLOW_REGEX_LIST: `${errorPrefix} Validation error. Allow regex list cannot be empty. Specify valid allow regex list.`, - INVALID_ALLOW_REGEX: `${errorPrefix} Validation error. Invalid allow regex. Specify valid allow regex at index %s1.`, - EMPTY_RESTRICT_REGEX_LIST: `${errorPrefix} Validation error. Restrict regex list cannot be empty. Specify valid restrict regex list.`, - INVALID_RESTRICT_REGEX: `${errorPrefix} Validation error. Invalid restrict regex. Specify valid restrict regex at index %s1.`, - INVALID_OUTPUT_PROCESSED_IMAGE: `${errorPrefix} Validation error. Invalid output processed image. Specify valid output processed image as string.`, - INVALID_OUTPUT_OCR_TEXT: `${errorPrefix} Validation error. Invalid output ocr text. Specify valid output ocr text as string.`, - INVALID_MASKING_METHOD: `${errorPrefix} Validation error. Invalid masking method. Specify valid masking method as string.`, - INVALID_PIXEL_DENSITY: `${errorPrefix} Validation error. Invalid pixel density. Specify valid pixel density as string.`, - INVALID_OUTPUT_TRANSCRIPTION: `${errorPrefix} Validation error. Invalid output transcription. Specify valid output transcription as string.`, - INVALID_OUTPUT_PROCESSED_AUDIO: `${errorPrefix} Validation error. Invalid output processed audio. Specify valid output processed audio as string.`, - INVALID_MAX_RESOLUTION: `${errorPrefix} Validation error. Invalid max resolution. Specify valid max resolution as string.`, - INVALID_BLEEP: `${errorPrefix} Validation error. Invalid bleep. Specify valid bleep as object.`, - INVALID_FILE_OR_ENCODED_FILE: `${errorPrefix} . Error while decoding base64 and saving file`, - INVALID_FILE_TYPE : `${errorPrefix} Validation error. Invalid file type. Specify a valid file type.`, - INVALID_DEIDENTIFY_FILE_PATH: `${errorPrefix} Validation error. Invalid file path. Specify a valid file path.`, - INVALID_FILE_NAME: `${errorPrefix} Validation error. Invalid file name. Specify a valid file name.`, - FILE_READ_ERROR: `${errorPrefix} Validation error. Unable to read file. Verify the file path.`, - INVALID_BASE64_HEADER: `${errorPrefix} Validation error. Invalid base64 header. Specify a valid base64 header.`, - INVALID_WAIT_TIME: `${errorPrefix} Validation error. Invalid wait time. Specify a valid wait time as number and should not be greater than 20 secs.`, - INVALID_OUTPUT_DIRECTORY: `${errorPrefix} Validation error. Invalid output directory. Specify a valid output directory as string.`, - INVALID_OUTPUT_DIRECTORY_PATH: `${errorPrefix} Validation error. Invalid output directory path. Specify a valid output directory path as string.`, - EMPTY_RUN_ID: `${errorPrefix} Validation error. Run id cannot be empty. Specify a valid run id.`, - INVALID_RUN_ID: `${errorPrefix} Validation error. Invalid run id. Specify a valid run id as string.`, - INTERNAL_SERVER_ERROR: `${errorPrefix}. Internal server error. %s1.`, - INVALID_XML_FORMAT: `${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/src/vault/skyflow/index.ts b/src/vault/skyflow/index.ts deleted file mode 100644 index 4968a372..00000000 --- a/src/vault/skyflow/index.ts +++ /dev/null @@ -1,274 +0,0 @@ -import { CONFIG, CONTROLLER_TYPES, Env, getVaultURL, ISkyflowError, LogLevel, MessageType, parameterizedString, printLog } from "../../utils"; -import ConnectionConfig from "../config/connection"; -import VaultConfig from "../config/vault"; -import { SkyflowConfig, ClientObj } from "../types"; -import VaultController from "../controller/vault"; -import ConnectionController from "../controller/connections"; -import VaultClient from "../client"; -import Credentials from "../config/credentials"; -import SkyflowError from "../../error"; -import logs from "../../utils/logs"; -import { isLogLevel, validateConnectionConfig, validateSkyflowConfig, validateSkyflowCredentials, validateUpdateConnectionConfig, validateUpdateVaultConfig, validateVaultConfig } from "../../utils/validations"; -import SKYFLOW_ERROR_CODE from "../../error/codes"; -import DetectController from "../controller/detect"; - -class Skyflow { - - private vaultClients: ClientObj = {}; - - private connectionClients: ClientObj = {}; - - private commonCredentials?: Credentials; - - private logLevel: LogLevel = LogLevel.ERROR; - - constructor(config: SkyflowConfig) { - 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?.map(vaultConfig => { - this.addVaultConfig(vaultConfig); - }); - printLog(logs.infoLogs.VALIDATING_CONNECTION_CONFIG, MessageType.LOG, this.logLevel); - config.connectionConfigs?.map(connectionConfig => { - this.addConnectionConfig(connectionConfig); - }); - printLog(logs.infoLogs.CLIENT_INITIALIZED, MessageType.LOG, this.logLevel); - } - - private addVaultClient(config: VaultConfig, clients: ClientObj) { - const env = config.env || Env.PROD; - const vaultUrl = getVaultURL(config.clusterId, env); - const client = new VaultClient(vaultUrl, config.vaultId, config?.credentials, this.commonCredentials, this.logLevel); - const vaultController = new VaultController(client); - const detectController = new DetectController(client); - printLog(logs.infoLogs.CONTROLLER_INITIALIZED, MessageType.LOG, this.logLevel); - clients[config.vaultId] = { config, client, vaultController, detectController }; - } - - private addConnectionClient(config: ConnectionConfig, clients: ClientObj) { - const client = new VaultClient(config.connectionUrl, '', config?.credentials, this.commonCredentials, this.logLevel); - const connectionController = new ConnectionController(client); - printLog(parameterizedString(logs.infoLogs.CONNECTION_CONTROLLER_INITIALIZED, [config.connectionId]), MessageType.LOG, this.logLevel); - clients[config.connectionId] = { config, client, connectionController }; - } - - addVaultConfig(config: VaultConfig) { - validateVaultConfig(config, this.logLevel); - this.throwErrorIfIdExits(config?.vaultId, this.vaultClients, CONFIG.VAULT_ID); - this.addVaultClient(config, this.vaultClients); - } - - addConnectionConfig(config: ConnectionConfig) { - validateConnectionConfig(config, this.logLevel); - this.throwErrorIfIdExits(config?.connectionId, this.connectionClients, CONFIG.CONNECTION_ID); - this.addConnectionClient(config, this.connectionClients); - } - - private updateVaultClient(config: VaultConfig, clients: ClientObj, idKey: string) { - const existingClient = clients[config[idKey]]; - if (existingClient) { - const updatedConfig = { ...existingClient.config, ...config }; - const vaultUrl = getVaultURL(updatedConfig.clusterId, updatedConfig.env || Env.PROD); - existingClient.config = updatedConfig; - existingClient.client.updateClientConfig(vaultUrl, updatedConfig.vaultId, updatedConfig.credentials, this.commonCredentials, this.logLevel); - } else { - this.throwErrorForUnknownId(config[idKey], idKey) - } - } - - private updateConnectionClient(config: ConnectionConfig, clients: ClientObj, idKey: string) { - const existingClient = clients[config[idKey]]; - if (existingClient) { - const updatedConfig = { ...existingClient.config, ...config }; - existingClient.config = updatedConfig; - existingClient.client.updateClientConfig(updatedConfig.connectionUrl, '', updatedConfig.credentials, this.commonCredentials, this.logLevel); - } else { - this.throwErrorForUnknownId(config[idKey], idKey) - } - } - - updateVaultConfig(config: VaultConfig) { - validateUpdateVaultConfig(config, this.logLevel); - this.updateVaultClient(config, this.vaultClients, CONFIG.VAULT_ID); - } - - updateConnectionConfig(config: ConnectionConfig) { - validateUpdateConnectionConfig(config, this.logLevel); - this.updateConnectionClient(config, this.connectionClients, CONFIG.CONNECTION_ID); - } - - getVaultConfig(vaultId: string) { - return this.getConfig(vaultId, this.vaultClients, CONFIG.VAULT_ID); - } - - removeVaultConfig(vaultId: string) { - this.removeConfig(vaultId, this.vaultClients, CONFIG.VAULT_ID); - } - - getConnectionConfig(connectionId: string) { - return this.getConfig(connectionId, this.connectionClients, CONFIG.CONNECTION_ID); - } - - removeConnectionConfig(connectionId: string) { - this.removeConfig(connectionId, this.connectionClients, CONFIG.CONNECTION_ID); - } - - private throwSkyflowError(idKey: string, errorMapping: { [key: string]: ISkyflowError }, params?: string[]) { - const errorCode = errorMapping[idKey]; - if (errorCode) { - throw new SkyflowError(errorCode, params); - } - } - - private throwErrorIfIdExits(id: string, clients: ClientObj, idKey: string) { - const errorMapping = { - [CONFIG.VAULT_ID]: SKYFLOW_ERROR_CODE.VAULT_ID_EXITS_IN_CONFIG_LIST, - [CONFIG.CONNECTION_ID]: SKYFLOW_ERROR_CODE.CONNECTION_ID_EXITS_IN_CONFIG_LIST, - }; - if(Object.keys(clients).includes(id)){ - printLog(parameterizedString(logs.infoLogs[`${idKey}_CONFIG_EXISTS`], [id]), MessageType.ERROR, this.logLevel); - this.throwSkyflowError(idKey, errorMapping, [id]); - } - } - - private throwErrorForUnknownId(id: string, idKey: string) { - const errorMapping = { - [CONFIG.VAULT_ID]: SKYFLOW_ERROR_CODE.VAULT_ID_NOT_IN_CONFIG_LIST, - [CONFIG.CONNECTION_ID]: SKYFLOW_ERROR_CODE.CONNECTION_ID_NOT_IN_CONFIG_LIST, - }; - printLog(parameterizedString(logs.infoLogs[`${idKey}_CONFIG_DOES_NOT_EXIST`], [id]), MessageType.ERROR, this.logLevel); - this.throwSkyflowError(idKey, errorMapping, [id]); - } - - private throwErrorForEmptyClients(idKey: string) { - const errorMapping = { - [CONFIG.VAULT_ID]: SKYFLOW_ERROR_CODE.EMPTY_VAULT_CLIENTS, - [CONFIG.CONNECTION_ID]: SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_CLIENTS, - }; - this.throwSkyflowError(idKey, errorMapping); - } - - private throwErrorForEmptyId(idKey: string) { - const errorMapping = { - [CONFIG.VAULT_ID]: SKYFLOW_ERROR_CODE.EMPTY_VAULT_ID_VALIDATION, - [CONFIG.CONNECTION_ID]: SKYFLOW_ERROR_CODE.EMPTY_CONNECTION_ID_VALIDATION, - }; - this.throwSkyflowError(idKey, errorMapping); - } - - private removeConfig(id: string, clients: ClientObj, idKey: string) { - if (!clients[id]) this.throwErrorForUnknownId(id, idKey); - delete clients[id]; - } - - private getConfig(id: string, clients: ClientObj, idKey: string) { - if(!id) this.throwErrorForEmptyId(idKey); - if (!clients[id]) this.throwErrorForUnknownId(id, idKey); - return clients[id].config; - } - - setLogLevel(logLevel: LogLevel) { - if (logLevel && !isLogLevel(logLevel)) { - throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_LOG_LEVEL); - } - this.logLevel = logLevel; - this.updateClients(CONFIG.LOGLEVEL); - } - - updateLogLevel(logLevel: LogLevel): Skyflow { - if (logLevel && !isLogLevel(logLevel)) { - throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_LOG_LEVEL); - } - this.logLevel = logLevel; - this.updateClients(CONFIG.LOGLEVEL); - return this; - } - - getLogLevel() { - return this.logLevel; - } - - updateSkyflowCredentials(credentials: Credentials) { - if (!credentials) - throw new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_CREDENTIALS); - validateSkyflowCredentials(credentials); - this.commonCredentials = credentials; - this.updateClients(CONFIG.CREDENTIALS); - } - - getSkyflowCredentials() { - return this.commonCredentials; - } - - vault(vaultId?: string) { - return this.getClient(vaultId, this.vaultClients, CONFIG.VAULT_ID, CONTROLLER_TYPES.VAULT) as VaultController; - } - - detect(vaultId?: string) { - return this.getClient(vaultId, this.vaultClients, CONFIG.VAULT_ID, CONTROLLER_TYPES.DETECT) as DetectController; - } - - connection(connectionId?: string) { - return this.getClient(connectionId, this.connectionClients, CONFIG.CONNECTION_ID, CONTROLLER_TYPES.CONNECTION) as ConnectionController; - } - - private getClient( - id: string | undefined, - clients: ClientObj, - idKey: string, - controllerType: string - ) { - if (Object.keys(clients).length === 0) { - this.throwErrorForEmptyClients(idKey); - } - - const clientId = id || Object.keys(clients)[0]; - if (clientId) { - const clientData = clients[clientId]; - - if (controllerType === CONTROLLER_TYPES.VAULT && clientData?.vaultController) { - return clientData.vaultController; - } - - if (controllerType === CONTROLLER_TYPES.DETECT && clientData?.detectController) { - return clientData.detectController; - } - - if (controllerType === CONTROLLER_TYPES.CONNECTION && clientData?.connectionController) { - return clientData.connectionController; - } - - this.throwErrorForUnknownId(clientId, idKey); - } - throw new SkyflowError(SKYFLOW_ERROR_CODE.INTERNAL_SERVER_ERROR); - } - - private updateClients(updateType: string) { - this.updateClient(updateType, this.vaultClients); - this.updateClient(updateType, this.connectionClients); - } - - private updateClient(updateType: string, list: ClientObj) { - Object.values(list).forEach(clientConfig => { - if (updateType === CONFIG.LOGLEVEL) { - clientConfig.client.setLogLevel(this.logLevel); - } else if (updateType === CONFIG.CREDENTIALS) { - clientConfig.client.updateSkyflowCredentials(this.commonCredentials); - } - }); - } - -} - -export default Skyflow; diff --git a/tsconfig.json b/tsconfig.json index dbd34688..5cfce407 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,8 @@ { - "compilerOptions": { - "sourceMap": 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, - "declaration": true, - "rootDir": "src", - "outDir": "lib", - "types": ["node"] - }, - "include": ["src/**/*.ts","typings.d.ts"], - "exclude": ["node_modules", "dist", "tests/**/*.test.*"], + "files": [], + "references": [ + { "path": "./common" }, + { "path": "./skyvault" }, + { "path": "./flowvault" } + ] } From 1fd6971e3ba576f59068464fc2ec97a9999afef4 Mon Sep 17 00:00:00 2001 From: saileshwar-skyflow Date: Wed, 14 Oct 2026 21:21:12 +0530 Subject: [PATCH 2/4] SK-3225: fix PR CI (eslint ignores + gitleaks allowlist for generated/dist) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - eslint.config.mjs: ignore all generated dirs (**/_generated_/**, legacy leading-space ** / _generated_/**), all workspace test dirs (**/test/**), and build output across workspaces — the old ignores only matched the single-package paths. - Replace three banned magic-string comparisons in hand-written code (typeof === "symbol"/"function") with equivalent non-literal checks. - Rule/gitleaks.toml: allowlist the maintainer-owned generated dirs (example JWTs) and the bundled dist/ output. Co-Authored-By: Claude Opus 4.8 --- Rule/gitleaks.toml | 7 +++++++ common/src/error/codes/index.ts | 2 +- eslint.config.mjs | 18 +++++++++++------- flowvault/src/client/index.ts | 2 +- flowvault/src/controller/vault/index.ts | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) 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/common/src/error/codes/index.ts b/common/src/error/codes/index.ts index 214c8111..141bafd9 100644 --- a/common/src/error/codes/index.ts +++ b/common/src/error/codes/index.ts @@ -242,7 +242,7 @@ const _SKYFLOW_ERROR_CODE = { // 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 === "symbol" || !(prop in target)) { + if (typeof prop !== "string" || !(prop in target)) { return (target as Record)[prop as string]; } const key = prop as keyof typeof _SKYFLOW_ERROR_CODE; diff --git a/eslint.config.mjs b/eslint.config.mjs index 7d4fde46..b91f395e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -41,13 +41,17 @@ export default tseslint.config( , { ignores: [ - "node_modules/", - "dist/", - "coverage/", - "lib/", - "src/ _generated_/", - "test/**", - "samples/**", + "**/node_modules/**", + "**/dist/**", + "**/coverage/**", + "**/lib/**", + // Generated (maintainer-owned) clients in every workspace — both the + // plain and the legacy leading-space directory names. + "**/_generated_/**", + "**/ _generated_/**", + // Tests live under each workspace (common/test, skyvault/test, flowvault/test). + "**/test/**", + "**/samples/**", "scripts/**", ] } diff --git a/flowvault/src/client/index.ts b/flowvault/src/client/index.ts index da4581f8..77fe36b5 100644 --- a/flowvault/src/client/index.ts +++ b/flowvault/src/client/index.ts @@ -177,7 +177,7 @@ class VaultClient { const rawResponse = err?.rawResponse; const headers = rawResponse?.headers; const headerRequestId = - headers && typeof headers.get === 'function' + headers && headers.get ? headers.get(HTTP_HEADER.X_REQUEST_ID) || '' : ''; diff --git a/flowvault/src/controller/vault/index.ts b/flowvault/src/controller/vault/index.ts index 9b2cbbbc..b80c7d88 100644 --- a/flowvault/src/controller/vault/index.ts +++ b/flowvault/src/controller/vault/index.ts @@ -89,7 +89,7 @@ class VaultController { // eslint-disable-next-line class-methods-use-this private getRequestId(rawResponse: any): string | undefined { const headers = rawResponse?.headers; - if (headers && typeof headers.get === 'function') { + if (headers && headers.get) { return headers.get('x-request-id') || undefined; } return undefined; From 73d319aef2335369bb79afccbf1fbed1e8a0454a Mon Sep 17 00:00:00 2001 From: saileshwar-skyflow Date: Wed, 14 Oct 2026 21:26:30 +0530 Subject: [PATCH 3/4] =?UTF-8?q?SK-3225:=20fix=20skyvault=20jest=20?= =?UTF-8?q?=E2=80=94=20don't=20babel-transform=20compiled=20common/lib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI resolves the skyflow-common symlink to its realpath (outside node_modules), so babel-jest was transforming the compiled common/lib and injecting core-js (an undeclared dep) -> suites failed to run. Add `common/lib` to transformIgnorePatterns so the already-CJS common package is loaded as-is. Co-Authored-By: Claude Opus 4.8 --- skyvault/jest.config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/skyvault/jest.config.js b/skyvault/jest.config.js index 5f07cf94..4ed9a522 100644 --- a/skyvault/jest.config.js +++ b/skyvault/jest.config.js @@ -3,6 +3,12 @@ module.exports = { '^.+\\.(ts|tsx)?$': 'ts-jest', '^.+\\.(js|jsx)$': 'babel-jest', }, + // Do NOT babel-transform the already-compiled `common` package (it is plain + // CJS from tsc). Jest may resolve the skyflow-common symlink to its realpath + // (outside node_modules), which would otherwise trigger babel's core-js + // injection against a dep that isn't installed. Matches both the realpath + // (".../common/lib/...") and the symlink path (".../skyflow-common/lib/..."). + transformIgnorePatterns: ['/node_modules/', 'common/lib'], verbose: true, collectCoverage: true, coveragePathIgnorePatterns: [ From 874487cb391e722716691ef8f80d242f7a38b2b1 Mon Sep 17 00:00:00 2001 From: saileshwar-skyflow Date: Wed, 14 Oct 2026 21:43:32 +0530 Subject: [PATCH 4/4] SK-3225: add flowvault branch-coverage tests to clear codecov/project Covers the defensive edge branches the codecov/project gate flagged (net-new flowvault code dipped overall coverage 0.44% vs base): - empty/absent `records`/`response` bodies map to [] across all ops - per-record errors with no x-request-id header -> requestId null - getRequestId with an absent raw response - uploadFiles: non-Error PUT rejection, no-name/trailing-dot content-type, response record with no matching prepared columns - VaultClient: default log level, unrecognized cached auth type flowvault branches 92.51% -> 96.89% (statements/lines 100%); 132 -> 153 tests. No source/generated/config changes. Co-Authored-By: Claude Opus 4.8 --- flowvault/test/client/client.test.ts | 19 ++++ .../test/controller/delete-files.test.ts | 31 ++++++- flowvault/test/controller/delete.test.ts | 24 ++++- flowvault/test/controller/detokenize.test.ts | 27 +++++- flowvault/test/controller/get-tokens.test.ts | 27 +++++- flowvault/test/controller/get.test.ts | 16 ++++ flowvault/test/controller/query.test.ts | 7 ++ flowvault/test/controller/update.test.ts | 24 ++++- .../test/controller/upload-files.test.ts | 89 ++++++++++++++++++- 9 files changed, 258 insertions(+), 6 deletions(-) diff --git a/flowvault/test/client/client.test.ts b/flowvault/test/client/client.test.ts index fe174bc7..0cd45011 100644 --- a/flowvault/test/client/client.test.ts +++ b/flowvault/test/client/client.test.ts @@ -137,6 +137,14 @@ describe('VaultClient.getCredentials', () => { expect(client.getCredentials()).toEqual({ apiKey: 'sky-common' }); }); + it('falls through to stored credentials for an unrecognized cached auth type', () => { + // Defensive: an authInfo.type the switch does not handle hits the `default` + // arm and falls back to the configured credentials. + const client = newClient({ apiKey: 'sky-individual' }); + (client as unknown as { authInfo: unknown }).authInfo = { key: 'sky-cached', type: 'MYSTERY' as never }; + expect(client.getCredentials()).toEqual({ apiKey: 'sky-individual' }); + }); + it('skips reuse right after updateSkyflowCredentials (updateTriggered), then resumes', () => { const client = newClient({ apiKey: 'sky-individual' }); (client as unknown as { authInfo: unknown }).authInfo = { key: 'sky-cached', type: AuthType.API_KEY }; @@ -170,4 +178,15 @@ describe('VaultClient config updates', () => { client.setLogLevel(LogLevel.WARN); expect(client.getLogLevel()).toBe(LogLevel.WARN); }); + + it('defaults the log level to ERROR when the constructor gets none', () => { + const client = new VaultClient(URL, 'vault123', { apiKey: 'sky-x' } as never); + expect(client.getLogLevel()).toBe(LogLevel.ERROR); + }); + + it('updateClientConfig defaults the log level to ERROR when none is provided', () => { + const client = newClient({ apiKey: 'sky-x' }); + client.updateClientConfig(URL, 'vault123', { apiKey: 'sky-y' } as never, undefined, undefined); + expect(client.getLogLevel()).toBe(LogLevel.ERROR); + }); }); diff --git a/flowvault/test/controller/delete-files.test.ts b/flowvault/test/controller/delete-files.test.ts index d8f7d4ec..13811d35 100644 --- a/flowvault/test/controller/delete-files.test.ts +++ b/flowvault/test/controller/delete-files.test.ts @@ -1,6 +1,13 @@ import { DeleteFilesRequest, DeleteFilesResponse } from '../../src'; import VaultController from '../../src/controller/vault'; -import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoHeaders, + REQUEST_ID, + silenceConsole, +} from '../helpers'; silenceConsole(); @@ -83,6 +90,28 @@ describe('VaultController.deleteFiles', () => { expect(resp.records[0].columns).toEqual([{ column: 'resume', status: null }]); }); + it('returns an empty records array when the response has none', async () => { + const deleteFiles = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); + const resp = await controller.deleteFiles( + new DeleteFilesRequest([{ tableName: 't', columns: ['c'], skyflowId: 'id1' }]), + ); + expect(resp.records).toEqual([]); + }); + + it('sets requestId to null on an errored record when the raw response has no headers', async () => { + const deleteFiles = resourceMethod( + { records: [{ skyflowID: 'id1', tableName: 't', httpCode: 404, error: 'not found' }] }, + rawResponseNoHeaders, + ); + const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); + const resp = await controller.deleteFiles( + new DeleteFilesRequest([{ tableName: 't', columns: ['c'], skyflowId: 'id1' }]), + ); + expect(resp.records[0].error).toBe('not found'); + expect(resp.records[0].requestId).toBeNull(); + }); + it('rejects when the generated method throws', async () => { const deleteFiles = rejectingMethod(new Error('boom-delete-files')); const controller = new VaultController(makeFakeClient({ files: { deleteFiles } })); diff --git a/flowvault/test/controller/delete.test.ts b/flowvault/test/controller/delete.test.ts index ada7308a..613c7ac9 100644 --- a/flowvault/test/controller/delete.test.ts +++ b/flowvault/test/controller/delete.test.ts @@ -1,6 +1,13 @@ import { DeleteRequest, DeleteResponse } from '../../src'; import VaultController from '../../src/controller/vault'; -import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoHeaders, + REQUEST_ID, + silenceConsole, +} from '../helpers'; silenceConsole(); @@ -48,6 +55,21 @@ describe('VaultController.delete', () => { expect(resp.records[0].requestId).toBe(REQUEST_ID); }); + it('returns an empty records array when the response has none', async () => { + const deleteRecords = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ records: { deleteRecords } })); + const resp = await controller.delete(new DeleteRequest('cards', ['id1'])); + expect(resp.records).toEqual([]); + }); + + it('sets requestId to null on an errored record when the raw response has no headers', async () => { + const deleteRecords = resourceMethod({ records: [{ skyflowID: 'id1', httpCode: 404, error: 'missing' }] }, rawResponseNoHeaders); + const controller = new VaultController(makeFakeClient({ records: { deleteRecords } })); + const resp = await controller.delete(new DeleteRequest('cards', ['id1'])); + expect(resp.records[0].error).toBe('missing'); + expect(resp.records[0].requestId).toBeNull(); + }); + it('rejects when the generated method throws', async () => { const deleteRecords = rejectingMethod(new Error('boom-delete')); const controller = new VaultController(makeFakeClient({ records: { deleteRecords } })); diff --git a/flowvault/test/controller/detokenize.test.ts b/flowvault/test/controller/detokenize.test.ts index 00644de3..e083a12f 100644 --- a/flowvault/test/controller/detokenize.test.ts +++ b/flowvault/test/controller/detokenize.test.ts @@ -1,6 +1,13 @@ import { DetokenizeRequest, DetokenizeResponse } from '../../src'; import VaultController from '../../src/controller/vault'; -import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoHeaders, + REQUEST_ID, + silenceConsole, +} from '../helpers'; silenceConsole(); @@ -75,6 +82,24 @@ describe('VaultController.detokenize', () => { expect(resp.records[0].requestId).toBe(REQUEST_ID); }); + it('returns an empty records array when the generated `response` is absent', async () => { + const detokenize = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); + const resp = await controller.detokenize(new DetokenizeRequest(['tok1'])); + expect(resp.records).toEqual([]); + }); + + it('sets requestId to null on an errored record when the raw response has no headers', async () => { + const detokenize = resourceMethod( + { response: [{ token: 'tok1', httpCode: 404, error: 'token not found' }] }, + rawResponseNoHeaders, + ); + const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); + const resp = await controller.detokenize(new DetokenizeRequest(['tok1'])); + expect(resp.records[0].error).toBe('token not found'); + expect(resp.records[0].requestId).toBeNull(); + }); + it('rejects when the generated method throws', async () => { const detokenize = rejectingMethod(new Error('boom-detok')); const controller = new VaultController(makeFakeClient({ tokens: { detokenize } })); diff --git a/flowvault/test/controller/get-tokens.test.ts b/flowvault/test/controller/get-tokens.test.ts index 4ac9874c..f6b1b5be 100644 --- a/flowvault/test/controller/get-tokens.test.ts +++ b/flowvault/test/controller/get-tokens.test.ts @@ -1,6 +1,13 @@ import { GetTokensRequest, GetTokensResponse } from '../../src'; import VaultController from '../../src/controller/vault'; -import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoHeaders, + REQUEST_ID, + silenceConsole, +} from '../helpers'; silenceConsole(); @@ -48,6 +55,24 @@ describe('VaultController.getTokens', () => { expect(resp.records[0].requestId).toBe(REQUEST_ID); }); + it('returns an empty records array when the response has none', async () => { + const getTokens = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ tokens: { getTokens } })); + const resp = await controller.getTokens(new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }])); + expect(resp.records).toEqual([]); + }); + + it('sets requestId to null on an errored record when the raw response has no headers', async () => { + const getTokens = resourceMethod( + { records: [{ value: '4111', tokenGroupName: 'cards', httpCode: 400, error: 'tokenize failed' }] }, + rawResponseNoHeaders, + ); + const controller = new VaultController(makeFakeClient({ tokens: { getTokens } })); + const resp = await controller.getTokens(new GetTokensRequest([{ value: '4111', tokenGroupName: 'cards' }])); + expect(resp.records[0].error).toBe('tokenize failed'); + expect(resp.records[0].requestId).toBeNull(); + }); + it('rejects when the generated method throws', async () => { const getTokens = rejectingMethod(new Error('boom-tokens')); const controller = new VaultController(makeFakeClient({ tokens: { getTokens } })); diff --git a/flowvault/test/controller/get.test.ts b/flowvault/test/controller/get.test.ts index be564598..cf3c0a91 100644 --- a/flowvault/test/controller/get.test.ts +++ b/flowvault/test/controller/get.test.ts @@ -102,6 +102,22 @@ describe('VaultController.get', () => { expect(resp.records[0].requestId).toBe(REQUEST_ID); }); + it('returns an empty records array when the response has none', async () => { + const getRecords = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + const resp = await controller.get(new GetRequest('cards', ['id1'])); + expect(resp.records).toEqual([]); + }); + + it('sets requestId to null on an errored record when the raw response is absent', async () => { + // rawResponse null -> getRequestId short-circuits -> requestId falls back to null. + const getRecords = resourceMethod({ records: [{ httpCode: 404, error: 'not found' }] }, null); + const controller = new VaultController(makeFakeClient({ records: { getRecords } })); + const resp = await controller.get(new GetRequest('cards', ['id1'])); + expect(resp.records[0].error).toBe('not found'); + expect(resp.records[0].requestId).toBeNull(); + }); + it('rejects when the generated method throws', async () => { const getRecords = rejectingMethod(new Error('boom-get')); const controller = new VaultController(makeFakeClient({ records: { getRecords } })); diff --git a/flowvault/test/controller/query.test.ts b/flowvault/test/controller/query.test.ts index d5858ea6..e9ce3e95 100644 --- a/flowvault/test/controller/query.test.ts +++ b/flowvault/test/controller/query.test.ts @@ -44,6 +44,13 @@ describe('VaultController.query', () => { expect(resp.requestId).toBe(''); }); + it('returns empty fields when the response has no records', async () => { + const executeQuery = resourceMethod({ metadata: { columns: [] } }); + const controller = new VaultController(makeFakeClient({ query: { executeQuery } })); + const resp = await controller.query(new QueryRequest('SELECT 1')); + expect(resp.fields).toEqual([]); + }); + it('rejects when the generated method throws', async () => { const executeQuery = rejectingMethod(new Error('boom-query')); const controller = new VaultController(makeFakeClient({ query: { executeQuery } })); diff --git a/flowvault/test/controller/update.test.ts b/flowvault/test/controller/update.test.ts index cc3a345e..220cf9f3 100644 --- a/flowvault/test/controller/update.test.ts +++ b/flowvault/test/controller/update.test.ts @@ -1,6 +1,13 @@ import { UpdateRequest, UpdateResponse } from '../../src'; import VaultController from '../../src/controller/vault'; -import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoHeaders, + REQUEST_ID, + silenceConsole, +} from '../helpers'; silenceConsole(); @@ -50,6 +57,21 @@ describe('VaultController.update', () => { expect(resp.records[0].requestId).toBe(REQUEST_ID); }); + it('returns an empty records array when the response has none', async () => { + const updateRecords = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ records: { updateRecords } })); + const resp = await controller.update(new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 1 } }])); + expect(resp.records).toEqual([]); + }); + + it('sets requestId to null on an errored record when the raw response has no headers', async () => { + const updateRecords = resourceMethod({ records: [{ httpCode: 400, error: 'update failed' }] }, rawResponseNoHeaders); + const controller = new VaultController(makeFakeClient({ records: { updateRecords } })); + const resp = await controller.update(new UpdateRequest('cards', [{ skyflowId: 'id1', data: { x: 1 } }])); + expect(resp.records[0].error).toBe('update failed'); + expect(resp.records[0].requestId).toBeNull(); + }); + it('rejects when the generated method throws', async () => { const updateRecords = rejectingMethod(new Error('boom-update')); const controller = new VaultController(makeFakeClient({ records: { updateRecords } })); diff --git a/flowvault/test/controller/upload-files.test.ts b/flowvault/test/controller/upload-files.test.ts index 70da624e..514a3199 100644 --- a/flowvault/test/controller/upload-files.test.ts +++ b/flowvault/test/controller/upload-files.test.ts @@ -3,7 +3,14 @@ jest.mock('fs/promises', () => ({ readFile: jest.fn() })); import * as fsPromises from 'fs/promises'; import { UploadFilesRequest, UploadFilesResponse } from '../../src'; import VaultController from '../../src/controller/vault'; -import { makeFakeClient, resourceMethod, rejectingMethod, REQUEST_ID, silenceConsole } from '../helpers'; +import { + makeFakeClient, + resourceMethod, + rejectingMethod, + rawResponseNoHeaders, + REQUEST_ID, + silenceConsole, +} from '../helpers'; silenceConsole(); @@ -217,6 +224,86 @@ describe('VaultController.uploadFiles', () => { expect(cols.find((c) => c.column === 'b')?.uploadStatus).toBe('SKIPPED'); }); + it('FAILED (stringified) when the signed-URL PUT rejects with a non-Error', async () => { + fetchMock.mockRejectedValue('socket hang up'); // a thrown string, not an Error + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { resume: 'https://signed/resume' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + + const col = resp.records[0].columns[0]; + expect(col.uploadStatus).toBe('FAILED'); + expect(col.error).toBe('socket hang up'); + }); + + it('infers octet-stream for a file with no name and for a trailing-dot name', async () => { + const uploadFiles = resourceMethod({ + records: [{ skyflowID: 'id1', tableName: 't', data: { a: 'https://signed/a', b: 'https://signed/b' }, httpCode: 200 }], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + await controller.uploadFiles( + new UploadFilesRequest([ + { + tableName: 't', + columns: [ + // File-like with an undefined name and no explicit fileName -> fileName stays undefined. + { column: 'a', file: fakeFile([1], undefined as unknown as string) }, + // A trailing-dot name -> extension resolves to '' -> octet-stream. + { column: 'b', base64: 'aGk=', fileName: 'weird.' }, + ], + }, + ]), + ); + + expect(fetchMock).toHaveBeenCalledTimes(2); + for (const [, init] of fetchMock.mock.calls) { + expect(init.headers['Content-Type']).toBe('application/octet-stream'); + } + }); + + it('tolerates a response record with no matching prepared columns (prepared index absent)', async () => { + // Server returns MORE records than were sent -> prepared[1] is undefined -> []. + const uploadFiles = resourceMethod({ + records: [ + { skyflowID: 'id1', tableName: 't', data: {}, httpCode: 200 }, + { skyflowID: 'id2', tableName: 't', data: {}, httpCode: 200 }, + ], + }); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + + expect(resp.records).toHaveLength(2); + expect(resp.records[1].columns).toEqual([]); // no prepared columns for the extra record + }); + + it('returns an empty records array when the response has none', async () => { + const uploadFiles = resourceMethod({}); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + expect(resp.records).toEqual([]); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it('sets requestId to null on an errored record when the raw response has no headers', async () => { + const uploadFiles = resourceMethod({ records: [{ tableName: 't', httpCode: 400, error: 'row rejected' }] }, rawResponseNoHeaders); + const controller = new VaultController(makeFakeClient({ files: { uploadFiles } })); + const resp = await controller.uploadFiles( + new UploadFilesRequest([{ tableName: 't', columns: [{ column: 'resume', base64: 'aGk=', fileName: 'r.pdf' }] }]), + ); + expect(resp.records[0].error).toBe('row rejected'); + expect(resp.records[0].requestId).toBeNull(); + }); + it('rejects when the generated uploadFiles method throws', async () => { const uploadFiles = rejectingMethod(new Error('boom-upload')); const controller = new VaultController(makeFakeClient({ files: { uploadFiles } }));