mirror of
https://github.com/bitwarden/browser
synced 2026-02-07 04:03:29 +00:00
refactor: introduce @bitwarden/api and @bitwarden/legacy-api
- Created libs/legacy-api with description and platform team ownership - Updated tsconfig.base path mapping and CODEOWNERS - npm install changes in package-lock from generator feat(api): add minimal ApiClient and HttpOperations type with lifted core methods (send, fetch/nativeFetch, token helpers, error handling) feat(api): include prerelease flag header in ApiClient headers to match ApiService behavior refactor(api): type logoutCallback param as LogoutReason to mirror ApiService chore(api): remove unused HttpOperations import from abstraction refactor(api): rename api-client.ts to api.service.ts and update barrel/spec imports chore(api): restore comments and constructor parity with common ApiService (device field, directory traversal note, unauthenticated header rationale) angular: provide TokenProvider -> TokenProviderService; convert TokenProvider to abstract class for DI token; no cycles with @bitwarden/api
This commit is contained in:
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -106,6 +106,8 @@ libs/state-test-utils @bitwarden/team-platform-dev
|
||||
libs/device-type @bitwarden/team-platform-dev
|
||||
libs/encoding @bitwarden/team-platform-dev
|
||||
libs/platform-utils @bitwarden/team-platform-dev
|
||||
libs/api @bitwarden/team-platform-dev
|
||||
libs/legacy-api @bitwarden/team-platform-dev
|
||||
# Web utils used across app and connectors
|
||||
apps/web/src/utils/ @bitwarden/team-platform-dev
|
||||
# Web core and shared files
|
||||
|
||||
5
libs/api/README.md
Normal file
5
libs/api/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# api
|
||||
|
||||
Owned by: platform
|
||||
|
||||
Library for Bitwarden API access
|
||||
3
libs/api/eslint.config.mjs
Normal file
3
libs/api/eslint.config.mjs
Normal file
@@ -0,0 +1,3 @@
|
||||
import baseConfig from "../../eslint.config.mjs";
|
||||
|
||||
export default [...baseConfig];
|
||||
10
libs/api/jest.config.js
Normal file
10
libs/api/jest.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
displayName: "api",
|
||||
preset: "../../jest.preset.js",
|
||||
testEnvironment: "node",
|
||||
transform: {
|
||||
"^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }],
|
||||
},
|
||||
moduleFileExtensions: ["ts", "js", "html"],
|
||||
coverageDirectory: "../../coverage/libs/api",
|
||||
};
|
||||
11
libs/api/package.json
Normal file
11
libs/api/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@bitwarden/api",
|
||||
"version": "0.0.1",
|
||||
"description": "Library for Bitwarden API access",
|
||||
"private": true,
|
||||
"type": "commonjs",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "GPL-3.0",
|
||||
"author": "platform"
|
||||
}
|
||||
33
libs/api/project.json
Normal file
33
libs/api/project.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "api",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/api/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/api",
|
||||
"main": "libs/api/src/index.ts",
|
||||
"tsConfig": "libs/api/tsconfig.lib.json",
|
||||
"assets": ["libs/api/*.md"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/api/**/*.ts"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/api/jest.config.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
libs/api/src/api.service.abstraction.ts
Normal file
33
libs/api/src/api.service.abstraction.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request";
|
||||
import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/sso-token.request";
|
||||
import { UserApiTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/user-api-token.request";
|
||||
import { WebAuthnLoginTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/webauthn-login-token.request";
|
||||
import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response";
|
||||
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response";
|
||||
|
||||
export interface ApiServiceAbstraction {
|
||||
fetch(request: Request): Promise<Response>;
|
||||
nativeFetch(request: Request): Promise<Response>;
|
||||
send(
|
||||
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||
path: string,
|
||||
body: any,
|
||||
authed: boolean,
|
||||
hasResponse: boolean,
|
||||
apiUrl?: string | null,
|
||||
alterHeaders?: (headers: Headers) => void,
|
||||
): Promise<any>;
|
||||
|
||||
postIdentityToken(
|
||||
request:
|
||||
| UserApiTokenRequest
|
||||
| PasswordTokenRequest
|
||||
| SsoTokenRequest
|
||||
| WebAuthnLoginTokenRequest,
|
||||
): Promise<
|
||||
IdentityTokenResponse | IdentityTwoFactorResponse | IdentityDeviceVerificationResponse
|
||||
>;
|
||||
refreshIdentityToken(): Promise<any>;
|
||||
getActiveBearerToken(): Promise<string>;
|
||||
}
|
||||
369
libs/api/src/api.service.ts
Normal file
369
libs/api/src/api.service.ts
Normal file
@@ -0,0 +1,369 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { DeviceRequest } from "@bitwarden/common/auth/models/request/identity-token/device.request";
|
||||
import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request";
|
||||
import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/sso-token.request";
|
||||
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
|
||||
import { UserApiTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/user-api-token.request";
|
||||
import { WebAuthnLoginTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/webauthn-login-token.request";
|
||||
import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response";
|
||||
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response";
|
||||
import { ClientType, DeviceType } from "@bitwarden/common/enums";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
|
||||
import { VaultTimeoutAction } from "@bitwarden/common/key-management/vault-timeout/enums/vault-timeout-action.enum";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { flagEnabled } from "@bitwarden/common/platform/misc/flags";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { TokenProvider } from "@bitwarden/token-provider";
|
||||
|
||||
import { ApiServiceAbstraction } from "./abstractions/api.service";
|
||||
import { HttpOperations } from "./http-operations";
|
||||
|
||||
export class ApiService implements ApiServiceAbstraction {
|
||||
private device: DeviceType;
|
||||
private deviceType: string;
|
||||
private refreshTokenPromise: Promise<string> | undefined;
|
||||
|
||||
/**
|
||||
* The message (responseJson.ErrorModel.Message) that comes back from the server when a new device verification is required.
|
||||
*/
|
||||
private static readonly NEW_DEVICE_VERIFICATION_REQUIRED_MESSAGE =
|
||||
"new device verification required";
|
||||
|
||||
constructor(
|
||||
private tokenService: TokenService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private environmentService: EnvironmentService,
|
||||
private appIdService: AppIdService,
|
||||
private refreshAccessTokenErrorCallback: () => void,
|
||||
private logService: LogService,
|
||||
private logoutCallback: (logoutReason: LogoutReason) => Promise<void>,
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private readonly httpOperations: HttpOperations,
|
||||
private tokenProvider: TokenProvider,
|
||||
private customUserAgent: string = null,
|
||||
) {
|
||||
this.device = platformUtilsService.getDevice();
|
||||
this.deviceType = this.device.toString();
|
||||
}
|
||||
|
||||
async postIdentityToken(
|
||||
request:
|
||||
| UserApiTokenRequest
|
||||
| PasswordTokenRequest
|
||||
| SsoTokenRequest
|
||||
| WebAuthnLoginTokenRequest,
|
||||
): Promise<
|
||||
IdentityTokenResponse | IdentityTwoFactorResponse | IdentityDeviceVerificationResponse
|
||||
> {
|
||||
return this.tokenProvider.postIdentityToken(request);
|
||||
}
|
||||
|
||||
async refreshIdentityToken(): Promise<any> {
|
||||
return this.tokenProvider.refreshIdentityToken();
|
||||
}
|
||||
|
||||
async getActiveBearerToken(): Promise<string> {
|
||||
return this.tokenProvider.getActiveBearerToken();
|
||||
}
|
||||
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
if (request.method === "GET") {
|
||||
request.headers.set("Cache-Control", "no-store");
|
||||
request.headers.set("Pragma", "no-cache");
|
||||
}
|
||||
request.headers.set("Bitwarden-Client-Name", this.platformUtilsService.getClientType());
|
||||
request.headers.set(
|
||||
"Bitwarden-Client-Version",
|
||||
await this.platformUtilsService.getApplicationVersionNumber(),
|
||||
);
|
||||
return this.nativeFetch(request);
|
||||
}
|
||||
|
||||
nativeFetch(request: Request): Promise<Response> {
|
||||
return fetch(request);
|
||||
}
|
||||
|
||||
async send(
|
||||
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||
path: string,
|
||||
body: any,
|
||||
authed: boolean,
|
||||
hasResponse: boolean,
|
||||
apiUrl?: string | null,
|
||||
alterHeaders?: (headers: Headers) => void,
|
||||
): Promise<any> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
apiUrl = Utils.isNullOrWhitespace(apiUrl) ? env.getApiUrl() : apiUrl;
|
||||
|
||||
// Prevent directory traversal from malicious paths
|
||||
const pathParts = path.split("?");
|
||||
const requestUrl =
|
||||
apiUrl + Utils.normalizePath(pathParts[0]) + (pathParts.length > 1 ? `?${pathParts[1]}` : "");
|
||||
|
||||
const [requestHeaders, requestBody] = await this.buildHeadersAndBody(
|
||||
authed,
|
||||
hasResponse,
|
||||
body,
|
||||
alterHeaders,
|
||||
);
|
||||
|
||||
const requestInit: RequestInit = {
|
||||
cache: "no-store",
|
||||
credentials: await this.getCredentials(),
|
||||
method: method,
|
||||
};
|
||||
requestInit.headers = requestHeaders;
|
||||
requestInit.body = requestBody;
|
||||
const response = await this.fetch(this.httpOperations.createRequest(requestUrl, requestInit));
|
||||
|
||||
const responseType = response.headers.get("content-type");
|
||||
const responseIsJson = responseType != null && responseType.indexOf("application/json") !== -1;
|
||||
const responseIsCsv = responseType != null && responseType.indexOf("text/csv") !== -1;
|
||||
if (hasResponse && response.status === 200 && responseIsJson) {
|
||||
const responseJson = await response.json();
|
||||
return responseJson;
|
||||
} else if (hasResponse && response.status === 200 && responseIsCsv) {
|
||||
return await response.text();
|
||||
} else if (response.status !== 200 && response.status !== 204) {
|
||||
const error = await this.handleError(response, false, authed);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async buildHeadersAndBody(
|
||||
authed: boolean,
|
||||
hasResponse: boolean,
|
||||
body: any,
|
||||
alterHeaders: (headers: Headers) => void,
|
||||
): Promise<[Headers, any]> {
|
||||
let requestBody: any = null;
|
||||
const headers = new Headers({
|
||||
"Device-Type": this.deviceType,
|
||||
});
|
||||
|
||||
if (flagEnabled("prereleaseBuild")) {
|
||||
headers.set("Is-Prerelease", "1");
|
||||
}
|
||||
if (this.customUserAgent != null) {
|
||||
headers.set("User-Agent", this.customUserAgent);
|
||||
}
|
||||
if (hasResponse) {
|
||||
headers.set("Accept", "application/json");
|
||||
}
|
||||
if (alterHeaders != null) {
|
||||
alterHeaders(headers);
|
||||
}
|
||||
if (authed) {
|
||||
const authHeader = await this.getActiveBearerToken();
|
||||
headers.set("Authorization", "Bearer " + authHeader);
|
||||
} else {
|
||||
// For unauthenticated requests, we need to tell the server what the device is for flag targeting,
|
||||
// since it won't be able to get it from the access token.
|
||||
const appId = await this.appIdService.getAppId();
|
||||
headers.set("Device-Identifier", appId);
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
if (typeof body === "string") {
|
||||
requestBody = body;
|
||||
headers.set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
|
||||
} else if (typeof body === "object") {
|
||||
if (body instanceof FormData) {
|
||||
requestBody = body;
|
||||
} else {
|
||||
headers.set("Content-Type", "application/json; charset=utf-8");
|
||||
requestBody = JSON.stringify(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [headers, requestBody];
|
||||
}
|
||||
|
||||
private async handleError(
|
||||
response: Response,
|
||||
tokenError: boolean,
|
||||
authed: boolean,
|
||||
): Promise<ErrorResponse> {
|
||||
let responseJson: any = null;
|
||||
if (this.isJsonResponse(response)) {
|
||||
responseJson = await response.json();
|
||||
} else if (this.isTextPlainResponse(response)) {
|
||||
responseJson = { Message: await response.text() };
|
||||
}
|
||||
|
||||
if (authed) {
|
||||
if (
|
||||
response.status === 401 ||
|
||||
response.status === 403 ||
|
||||
(tokenError &&
|
||||
response.status === 400 &&
|
||||
responseJson != null &&
|
||||
responseJson.error === "invalid_grant")
|
||||
) {
|
||||
await this.logoutCallback("invalidGrantError");
|
||||
}
|
||||
}
|
||||
|
||||
return new ErrorResponse(responseJson, response.status, tokenError);
|
||||
}
|
||||
|
||||
private qsStringify(params: any): string {
|
||||
return Object.keys(params)
|
||||
.map((key) => {
|
||||
return encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
|
||||
})
|
||||
.join("&");
|
||||
}
|
||||
|
||||
private async getCredentials(): Promise<RequestCredentials> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
if (this.platformUtilsService.getClientType() !== ClientType.Web || env.hasBaseUrl()) {
|
||||
return "include";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private isJsonResponse(response: Response): boolean {
|
||||
const typeHeader = response.headers.get("content-type");
|
||||
return typeHeader != null && typeHeader.indexOf("application/json") > -1;
|
||||
}
|
||||
|
||||
private isTextPlainResponse(response: Response): boolean {
|
||||
const typeHeader = response.headers.get("content-type");
|
||||
return typeHeader != null && typeHeader.indexOf("text/plain") > -1;
|
||||
}
|
||||
|
||||
// Token refresh helpers (same behavior as ApiService)
|
||||
private async internalRefreshToken(): Promise<string> {
|
||||
const refreshToken = await this.tokenService.getRefreshToken();
|
||||
if (refreshToken != null && refreshToken !== "") {
|
||||
return this.refreshAccessToken();
|
||||
}
|
||||
|
||||
const clientId = await this.tokenService.getClientId();
|
||||
const clientSecret = await this.tokenService.getClientSecret();
|
||||
if (!Utils.isNullOrWhitespace(clientId) && !Utils.isNullOrWhitespace(clientSecret)) {
|
||||
return this.refreshApiToken();
|
||||
}
|
||||
|
||||
this.refreshAccessTokenErrorCallback();
|
||||
|
||||
throw new Error("Cannot refresh access token, no refresh token or api keys are stored.");
|
||||
}
|
||||
|
||||
protected refreshToken(): Promise<string> {
|
||||
if (this.refreshTokenPromise === undefined) {
|
||||
this.refreshTokenPromise = this.internalRefreshToken();
|
||||
void this.refreshTokenPromise.finally(() => {
|
||||
this.refreshTokenPromise = undefined;
|
||||
});
|
||||
}
|
||||
return this.refreshTokenPromise;
|
||||
}
|
||||
|
||||
protected async refreshAccessToken(): Promise<string> {
|
||||
const refreshToken = await this.tokenService.getRefreshToken();
|
||||
if (refreshToken == null || refreshToken === "") {
|
||||
throw new Error();
|
||||
}
|
||||
const headers = new Headers({
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
|
||||
Accept: "application/json",
|
||||
"Device-Type": this.deviceType,
|
||||
});
|
||||
if (this.customUserAgent != null) {
|
||||
headers.set("User-Agent", this.customUserAgent);
|
||||
}
|
||||
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
const decodedToken = await this.tokenService.decodeAccessToken();
|
||||
const response = await this.fetch(
|
||||
this.httpOperations.createRequest(env.getIdentityUrl() + "/connect/token", {
|
||||
body: this.qsStringify({
|
||||
grant_type: "refresh_token",
|
||||
client_id: decodedToken.client_id,
|
||||
refresh_token: refreshToken,
|
||||
}),
|
||||
cache: "no-store",
|
||||
credentials: await this.getCredentials(),
|
||||
headers: headers,
|
||||
method: "POST",
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.status === 200) {
|
||||
const responseJson = await response.json();
|
||||
const tokenResponse = new IdentityTokenResponse(responseJson);
|
||||
|
||||
const newDecodedAccessToken = await this.tokenService.decodeAccessToken(
|
||||
tokenResponse.accessToken,
|
||||
);
|
||||
const userId = newDecodedAccessToken.sub;
|
||||
|
||||
const vaultTimeoutAction = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(userId),
|
||||
);
|
||||
const vaultTimeout = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId),
|
||||
);
|
||||
|
||||
const refreshedTokens = await this.tokenService.setTokens(
|
||||
tokenResponse.accessToken,
|
||||
vaultTimeoutAction as VaultTimeoutAction,
|
||||
vaultTimeout,
|
||||
tokenResponse.refreshToken,
|
||||
);
|
||||
return refreshedTokens.accessToken;
|
||||
} else {
|
||||
const error = await this.handleError(response, true, true);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
protected async refreshApiToken(): Promise<string> {
|
||||
const clientId = await this.tokenService.getClientId();
|
||||
const clientSecret = await this.tokenService.getClientSecret();
|
||||
|
||||
const appId = await this.appIdService.getAppId();
|
||||
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
|
||||
const tokenRequest = new UserApiTokenRequest(
|
||||
clientId,
|
||||
clientSecret,
|
||||
new TokenTwoFactorRequest(),
|
||||
deviceRequest,
|
||||
);
|
||||
|
||||
const response = await this.postIdentityToken(tokenRequest);
|
||||
if (!(response instanceof IdentityTokenResponse)) {
|
||||
throw new Error("Invalid response received when refreshing api token");
|
||||
}
|
||||
|
||||
const newDecodedAccessToken = await this.tokenService.decodeAccessToken(response.accessToken);
|
||||
const userId = newDecodedAccessToken.sub;
|
||||
|
||||
const vaultTimeoutAction = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(userId),
|
||||
);
|
||||
const vaultTimeout = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId),
|
||||
);
|
||||
|
||||
const refreshedToken = await this.tokenService.setAccessToken(
|
||||
response.accessToken,
|
||||
vaultTimeoutAction as VaultTimeoutAction,
|
||||
vaultTimeout,
|
||||
);
|
||||
return refreshedToken;
|
||||
}
|
||||
}
|
||||
8
libs/api/src/api.spec.ts
Normal file
8
libs/api/src/api.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as lib from "./index";
|
||||
|
||||
describe("api", () => {
|
||||
// This test will fail until something is exported from index.ts
|
||||
it("should work", () => {
|
||||
expect(lib).toBeDefined();
|
||||
});
|
||||
});
|
||||
3
libs/api/src/http-operations.ts
Normal file
3
libs/api/src/http-operations.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type HttpOperations = {
|
||||
createRequest: (url: string, request: RequestInit) => Request;
|
||||
};
|
||||
3
libs/api/src/index.ts
Normal file
3
libs/api/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./api.service";
|
||||
export * from "./api.service.abstraction";
|
||||
export * from "./http-operations";
|
||||
6
libs/api/tsconfig.eslint.json
Normal file
6
libs/api/tsconfig.eslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": ["src/**/*.ts", "src/**/*.js"],
|
||||
"exclude": ["**/build", "**/dist"]
|
||||
}
|
||||
13
libs/api/tsconfig.json
Normal file
13
libs/api/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
libs/api/tsconfig.lib.json
Normal file
10
libs/api/tsconfig.lib.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["jest.config.js", "src/**/*.spec.ts"]
|
||||
}
|
||||
10
libs/api/tsconfig.spec.json
Normal file
10
libs/api/tsconfig.spec.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
5
libs/legacy-api/README.md
Normal file
5
libs/legacy-api/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# legacy-api
|
||||
|
||||
Owned by: platform
|
||||
|
||||
Legacy API implementation for back-compat during extraction from @bitwarden/common
|
||||
3
libs/legacy-api/eslint.config.mjs
Normal file
3
libs/legacy-api/eslint.config.mjs
Normal file
@@ -0,0 +1,3 @@
|
||||
import baseConfig from "../../eslint.config.mjs";
|
||||
|
||||
export default [...baseConfig];
|
||||
10
libs/legacy-api/jest.config.js
Normal file
10
libs/legacy-api/jest.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
displayName: "legacy-api",
|
||||
preset: "../../jest.preset.js",
|
||||
testEnvironment: "node",
|
||||
transform: {
|
||||
"^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }],
|
||||
},
|
||||
moduleFileExtensions: ["ts", "js", "html"],
|
||||
coverageDirectory: "../../coverage/libs/legacy-api",
|
||||
};
|
||||
11
libs/legacy-api/package.json
Normal file
11
libs/legacy-api/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@bitwarden/legacy-api",
|
||||
"version": "0.0.1",
|
||||
"description": "Legacy API implementation for back-compat during extraction from @bitwarden/common",
|
||||
"private": true,
|
||||
"type": "commonjs",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "GPL-3.0",
|
||||
"author": "platform"
|
||||
}
|
||||
33
libs/legacy-api/project.json
Normal file
33
libs/legacy-api/project.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "legacy-api",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/legacy-api/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/legacy-api",
|
||||
"main": "libs/legacy-api/src/index.ts",
|
||||
"tsConfig": "libs/legacy-api/tsconfig.lib.json",
|
||||
"assets": ["libs/legacy-api/*.md"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/legacy-api/**/*.ts"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/legacy-api/jest.config.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
libs/legacy-api/src/index.ts
Normal file
0
libs/legacy-api/src/index.ts
Normal file
8
libs/legacy-api/src/legacy-api.spec.ts
Normal file
8
libs/legacy-api/src/legacy-api.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as lib from "./index";
|
||||
|
||||
describe("legacy-api", () => {
|
||||
// This test will fail until something is exported from index.ts
|
||||
it("should work", () => {
|
||||
expect(lib).toBeDefined();
|
||||
});
|
||||
});
|
||||
6
libs/legacy-api/tsconfig.eslint.json
Normal file
6
libs/legacy-api/tsconfig.eslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": ["src/**/*.ts", "src/**/*.js"],
|
||||
"exclude": ["**/build", "**/dist"]
|
||||
}
|
||||
13
libs/legacy-api/tsconfig.json
Normal file
13
libs/legacy-api/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
libs/legacy-api/tsconfig.lib.json
Normal file
10
libs/legacy-api/tsconfig.lib.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["jest.config.js", "src/**/*.spec.ts"]
|
||||
}
|
||||
10
libs/legacy-api/tsconfig.spec.json
Normal file
10
libs/legacy-api/tsconfig.spec.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -308,6 +308,11 @@
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"libs/api": {
|
||||
"name": "@bitwarden/api",
|
||||
"version": "0.0.1",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"libs/auth": {
|
||||
"name": "@bitwarden/auth",
|
||||
"version": "0.0.0",
|
||||
@@ -372,6 +377,11 @@
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"libs/legacy-api": {
|
||||
"name": "@bitwarden/legacy-api",
|
||||
"version": "0.0.1",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"libs/logging": {
|
||||
"name": "@bitwarden/logging",
|
||||
"version": "0.0.1",
|
||||
@@ -4579,6 +4589,10 @@
|
||||
"resolved": "libs/angular",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/api": {
|
||||
"resolved": "libs/api",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/auth": {
|
||||
"resolved": "libs/auth",
|
||||
"link": true
|
||||
@@ -4667,6 +4681,10 @@
|
||||
"resolved": "libs/key-management-ui",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/legacy-api": {
|
||||
"resolved": "libs/legacy-api",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/logging": {
|
||||
"resolved": "libs/logging",
|
||||
"link": true
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"paths": {
|
||||
"@bitwarden/admin-console/common": ["./libs/admin-console/src/common"],
|
||||
"@bitwarden/angular/*": ["./libs/angular/src/*"],
|
||||
"@bitwarden/api": ["libs/api/src/index.ts"],
|
||||
"@bitwarden/auth/angular": ["./libs/auth/src/angular"],
|
||||
"@bitwarden/auth/common": ["./libs/auth/src/common"],
|
||||
"@bitwarden/billing": ["./libs/billing/src"],
|
||||
@@ -42,6 +43,7 @@
|
||||
"@bitwarden/importer-ui": ["./libs/importer/src/components"],
|
||||
"@bitwarden/key-management": ["./libs/key-management/src"],
|
||||
"@bitwarden/key-management-ui": ["./libs/key-management-ui/src"],
|
||||
"@bitwarden/legacy-api": ["libs/legacy-api/src/index.ts"],
|
||||
"@bitwarden/logging": ["libs/logging/src"],
|
||||
"@bitwarden/messaging": ["libs/messaging/src/index.ts"],
|
||||
"@bitwarden/messaging-internal": ["libs/messaging-internal/src/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user