sanctum-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/approvals.d.ts +97 -0
- package/dist/approvals.js +38 -0
- package/dist/audit.d.ts +28 -0
- package/dist/audit.js +4 -0
- package/dist/auth.d.ts +19 -0
- package/dist/auth.js +8 -0
- package/dist/client.d.ts +44 -0
- package/dist/client.js +145 -0
- package/dist/dotenv.d.ts +14 -0
- package/dist/dotenv.js +91 -0
- package/dist/files.d.ts +59 -0
- package/dist/files.js +52 -0
- package/dist/identities.d.ts +24 -0
- package/dist/identities.js +5 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +27 -0
- package/dist/permissions.d.ts +31 -0
- package/dist/permissions.js +9 -0
- package/dist/projects.d.ts +44 -0
- package/dist/projects.js +11 -0
- package/dist/secrets.d.ts +92 -0
- package/dist/secrets.js +28 -0
- package/dist/webhooks.d.ts +55 -0
- package/dist/webhooks.js +9 -0
- package/package.json +38 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { MrSecretClient, QueryValue } from './client.js';
|
|
2
|
+
export type ApprovalStatus = 'pending' | 'approved' | 'rejected';
|
|
3
|
+
export type ApprovalAction = 'write' | 'delete';
|
|
4
|
+
export interface ApprovalRequester {
|
|
5
|
+
id: string;
|
|
6
|
+
email: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SecretApproval {
|
|
9
|
+
id: string;
|
|
10
|
+
status: ApprovalStatus;
|
|
11
|
+
projectId: string;
|
|
12
|
+
environmentId: string;
|
|
13
|
+
folderId?: string | null;
|
|
14
|
+
secretId?: string | null;
|
|
15
|
+
fileSecretId?: string | null;
|
|
16
|
+
proposedKey?: string | null;
|
|
17
|
+
action: ApprovalAction;
|
|
18
|
+
requestedBy: string;
|
|
19
|
+
requester?: ApprovalRequester;
|
|
20
|
+
reviewedBy?: string | null;
|
|
21
|
+
comment?: string | null;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
updatedAt: string;
|
|
24
|
+
votes?: {
|
|
25
|
+
user: ApprovalRequester;
|
|
26
|
+
}[];
|
|
27
|
+
_count?: {
|
|
28
|
+
votes: number;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface ApprovalVote {
|
|
32
|
+
user: ApprovalRequester;
|
|
33
|
+
}
|
|
34
|
+
export interface ApprovalDetail extends SecretApproval {
|
|
35
|
+
reviewer?: ApprovalRequester | null;
|
|
36
|
+
votes: ApprovalVote[];
|
|
37
|
+
}
|
|
38
|
+
export interface CreateSecretApprovalInput {
|
|
39
|
+
environmentId: string;
|
|
40
|
+
folderId?: string;
|
|
41
|
+
secretId?: string;
|
|
42
|
+
action: ApprovalAction;
|
|
43
|
+
key?: string;
|
|
44
|
+
value?: string;
|
|
45
|
+
comment?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface CreateFileApprovalInput {
|
|
48
|
+
environmentId: string;
|
|
49
|
+
folderId?: string;
|
|
50
|
+
fileSecretId?: string;
|
|
51
|
+
action: ApprovalAction;
|
|
52
|
+
key?: string;
|
|
53
|
+
file?: Blob | Buffer | Uint8Array | string;
|
|
54
|
+
filename?: string;
|
|
55
|
+
contentType?: string;
|
|
56
|
+
comment?: string;
|
|
57
|
+
}
|
|
58
|
+
export type CreateApprovalInput = CreateSecretApprovalInput | CreateFileApprovalInput;
|
|
59
|
+
export interface ApproveOutput {
|
|
60
|
+
id: string;
|
|
61
|
+
status: ApprovalStatus;
|
|
62
|
+
votes?: number;
|
|
63
|
+
minApprovals?: number;
|
|
64
|
+
}
|
|
65
|
+
export interface RejectOutput {
|
|
66
|
+
id: string;
|
|
67
|
+
status: ApprovalStatus;
|
|
68
|
+
}
|
|
69
|
+
export interface ApprovalPolicy {
|
|
70
|
+
id: string;
|
|
71
|
+
environmentId: string;
|
|
72
|
+
minApprovals: number;
|
|
73
|
+
requireForReads: boolean;
|
|
74
|
+
requireForWrites: boolean;
|
|
75
|
+
requireForDeletes: boolean;
|
|
76
|
+
createdAt: string;
|
|
77
|
+
updatedAt: string;
|
|
78
|
+
}
|
|
79
|
+
export interface SetApprovalPolicyInput {
|
|
80
|
+
minApprovals?: number;
|
|
81
|
+
requireForReads?: boolean;
|
|
82
|
+
requireForWrites?: boolean;
|
|
83
|
+
requireForDeletes?: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface ListApprovalsOptions extends Record<string, QueryValue> {
|
|
86
|
+
status?: 'pending' | 'approved' | 'rejected';
|
|
87
|
+
}
|
|
88
|
+
export interface ApprovalsApi {
|
|
89
|
+
listApprovals(projectId: string, options?: ListApprovalsOptions): Promise<SecretApproval[]>;
|
|
90
|
+
createApproval(projectId: string, input: CreateApprovalInput): Promise<SecretApproval>;
|
|
91
|
+
getApproval(projectId: string, id: string): Promise<ApprovalDetail>;
|
|
92
|
+
approve(projectId: string, id: string): Promise<ApproveOutput>;
|
|
93
|
+
reject(projectId: string, id: string): Promise<RejectOutput>;
|
|
94
|
+
getApprovalPolicy(projectId: string, envId: string): Promise<ApprovalPolicy>;
|
|
95
|
+
setApprovalPolicy(projectId: string, envId: string, input: SetApprovalPolicyInput): Promise<ApprovalPolicy>;
|
|
96
|
+
}
|
|
97
|
+
export declare const createApprovals: (client: MrSecretClient) => ApprovalsApi;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const toBlob = (value, contentType) => {
|
|
2
|
+
if (value instanceof Blob)
|
|
3
|
+
return value;
|
|
4
|
+
return new Blob([value], { type: contentType ?? 'application/octet-stream' });
|
|
5
|
+
};
|
|
6
|
+
const buildApprovalBody = (input) => {
|
|
7
|
+
if ('file' in input) {
|
|
8
|
+
const form = new FormData();
|
|
9
|
+
if (input.file !== undefined) {
|
|
10
|
+
const filename = input.filename ?? input.key ?? 'file';
|
|
11
|
+
form.append('file', toBlob(input.file, input.contentType), filename);
|
|
12
|
+
}
|
|
13
|
+
form.append('environmentId', input.environmentId);
|
|
14
|
+
form.append('action', input.action);
|
|
15
|
+
if (input.folderId)
|
|
16
|
+
form.append('folderId', input.folderId);
|
|
17
|
+
if (input.fileSecretId)
|
|
18
|
+
form.append('fileSecretId', input.fileSecretId);
|
|
19
|
+
if (input.key)
|
|
20
|
+
form.append('key', input.key);
|
|
21
|
+
if (input.comment)
|
|
22
|
+
form.append('comment', input.comment);
|
|
23
|
+
return form;
|
|
24
|
+
}
|
|
25
|
+
return input;
|
|
26
|
+
};
|
|
27
|
+
export const createApprovals = (client) => ({
|
|
28
|
+
listApprovals: (projectId, options) => client.get(`/api/v1/projects/${projectId}/approvals`, { query: options }),
|
|
29
|
+
createApproval: (projectId, input) => {
|
|
30
|
+
const body = buildApprovalBody(input);
|
|
31
|
+
return client.post(`/api/v1/projects/${projectId}/approvals`, body);
|
|
32
|
+
},
|
|
33
|
+
getApproval: (projectId, id) => client.get(`/api/v1/projects/${projectId}/approvals/${id}`),
|
|
34
|
+
approve: (projectId, id) => client.post(`/api/v1/projects/${projectId}/approvals/${id}/approve`),
|
|
35
|
+
reject: (projectId, id) => client.post(`/api/v1/projects/${projectId}/approvals/${id}/reject`),
|
|
36
|
+
getApprovalPolicy: (projectId, envId) => client.get(`/api/v1/projects/${projectId}/environments/${envId}/approval-policies`),
|
|
37
|
+
setApprovalPolicy: (projectId, envId, input) => client.post(`/api/v1/projects/${projectId}/environments/${envId}/approval-policies`, input),
|
|
38
|
+
});
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MrSecretClient, QueryValue } from './client.js';
|
|
2
|
+
export interface AuditLog {
|
|
3
|
+
id: string;
|
|
4
|
+
actorType: string;
|
|
5
|
+
actorId: string;
|
|
6
|
+
action: string;
|
|
7
|
+
resourceType: string;
|
|
8
|
+
resourceId?: string | null;
|
|
9
|
+
organizationId?: string | null;
|
|
10
|
+
projectId?: string | null;
|
|
11
|
+
environmentId?: string | null;
|
|
12
|
+
ipAddress?: string | null;
|
|
13
|
+
userAgent?: string | null;
|
|
14
|
+
metadata: unknown;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ListAuditLogsOptions extends Record<string, QueryValue> {
|
|
18
|
+
action?: string;
|
|
19
|
+
resourceType?: string;
|
|
20
|
+
actorId?: string;
|
|
21
|
+
from?: string;
|
|
22
|
+
to?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AuditApi {
|
|
25
|
+
listProjectAuditLogs(projectId: string, options?: ListAuditLogsOptions): Promise<AuditLog[]>;
|
|
26
|
+
listOrganizationAuditLogs(orgId: string, options?: ListAuditLogsOptions): Promise<AuditLog[]>;
|
|
27
|
+
}
|
|
28
|
+
export declare const createAudit: (client: MrSecretClient) => AuditApi;
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const createAudit = (client) => ({
|
|
2
|
+
listProjectAuditLogs: (projectId, options) => client.get(`/api/v1/projects/${projectId}/audit-logs`, { query: options }),
|
|
3
|
+
listOrganizationAuditLogs: (orgId, options) => client.get(`/api/v1/organizations/${orgId}/audit-logs`, { query: options }),
|
|
4
|
+
});
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SanctumClient } from "./client.js";
|
|
2
|
+
export interface UniversalAuthTokens {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
expiresIn: number;
|
|
5
|
+
accessTokenMaxTTL: number;
|
|
6
|
+
tokenType: "Bearer";
|
|
7
|
+
}
|
|
8
|
+
export interface UniversalAuthLoginInput {
|
|
9
|
+
clientId: string;
|
|
10
|
+
clientSecret: string;
|
|
11
|
+
organizationSlug?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface AuthApi {
|
|
14
|
+
universalAuthLogin(input: UniversalAuthLoginInput): Promise<UniversalAuthTokens>;
|
|
15
|
+
renewAccessToken(): Promise<{
|
|
16
|
+
token: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export declare const createAuth: (client: SanctumClient) => AuthApi;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const createAuth = (client) => ({
|
|
2
|
+
universalAuthLogin: async (input) => {
|
|
3
|
+
const tokens = await client.post("/api/v1/auth/universal-auth/login", input);
|
|
4
|
+
client.setToken(tokens.accessToken);
|
|
5
|
+
return tokens;
|
|
6
|
+
},
|
|
7
|
+
renewAccessToken: () => client.post("/api/v1/auth/token")
|
|
8
|
+
});
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface ClientOptions {
|
|
2
|
+
baseUrl?: string;
|
|
3
|
+
accessToken?: string;
|
|
4
|
+
clientId?: string;
|
|
5
|
+
clientSecret?: string;
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}
|
|
8
|
+
export type HttpMethod = "GET" | "POST" | "PATCH" | "DELETE" | "PUT";
|
|
9
|
+
export type QueryValue = string | number | boolean | undefined | null;
|
|
10
|
+
export interface RequestOptions {
|
|
11
|
+
method?: HttpMethod;
|
|
12
|
+
body?: unknown;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
query?: Record<string, QueryValue>;
|
|
15
|
+
raw?: boolean;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class SanctumApiError extends Error {
|
|
19
|
+
readonly code: string;
|
|
20
|
+
readonly status: number;
|
|
21
|
+
readonly response?: unknown | undefined;
|
|
22
|
+
constructor(message: string, code: string, status: number, response?: unknown | undefined);
|
|
23
|
+
}
|
|
24
|
+
export declare class SanctumClient {
|
|
25
|
+
baseUrl: string;
|
|
26
|
+
accessToken?: string;
|
|
27
|
+
clientId?: string;
|
|
28
|
+
clientSecret?: string;
|
|
29
|
+
timeout: number;
|
|
30
|
+
constructor(options?: ClientOptions);
|
|
31
|
+
setToken(token: string): void;
|
|
32
|
+
clearToken(): void;
|
|
33
|
+
buildUrl(path: string, query?: Record<string, QueryValue>): string;
|
|
34
|
+
private buildHeaders;
|
|
35
|
+
private prepareBody;
|
|
36
|
+
requestRaw(path: string, options?: RequestOptions): Promise<Response>;
|
|
37
|
+
request<T = unknown>(path: string, options?: RequestOptions): Promise<T>;
|
|
38
|
+
get<T = unknown>(path: string, options?: Omit<RequestOptions, "method" | "body">): Promise<T>;
|
|
39
|
+
post<T = unknown>(path: string, body?: unknown, options?: Omit<RequestOptions, "method" | "body">): Promise<T>;
|
|
40
|
+
patch<T = unknown>(path: string, body?: unknown, options?: Omit<RequestOptions, "method" | "body">): Promise<T>;
|
|
41
|
+
del<T = unknown>(path: string, options?: Omit<RequestOptions, "method">): Promise<T>;
|
|
42
|
+
authenticate(): Promise<import("./auth.js").UniversalAuthTokens>;
|
|
43
|
+
private throwError;
|
|
44
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const DEFAULT_BASE_URL = "http://localhost:4000";
|
|
2
|
+
export class SanctumApiError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
status;
|
|
5
|
+
response;
|
|
6
|
+
constructor(message, code, status, response) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.response = response;
|
|
11
|
+
this.name = "SanctumApiError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class SanctumClient {
|
|
15
|
+
baseUrl;
|
|
16
|
+
accessToken;
|
|
17
|
+
clientId;
|
|
18
|
+
clientSecret;
|
|
19
|
+
timeout;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
22
|
+
this.accessToken = options.accessToken;
|
|
23
|
+
this.clientId = options.clientId;
|
|
24
|
+
this.clientSecret = options.clientSecret;
|
|
25
|
+
this.timeout = options.timeout ?? 30000;
|
|
26
|
+
}
|
|
27
|
+
setToken(token) {
|
|
28
|
+
this.accessToken = token;
|
|
29
|
+
}
|
|
30
|
+
clearToken() {
|
|
31
|
+
this.accessToken = undefined;
|
|
32
|
+
}
|
|
33
|
+
buildUrl(path, query) {
|
|
34
|
+
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
35
|
+
let url = `${this.baseUrl}${normalized}`;
|
|
36
|
+
if (query) {
|
|
37
|
+
const params = new URLSearchParams();
|
|
38
|
+
for (const [key, value] of Object.entries(query)) {
|
|
39
|
+
if (value === undefined || value === null)
|
|
40
|
+
continue;
|
|
41
|
+
params.append(key, String(value));
|
|
42
|
+
}
|
|
43
|
+
const queryString = params.toString();
|
|
44
|
+
if (queryString)
|
|
45
|
+
url += `?${queryString}`;
|
|
46
|
+
}
|
|
47
|
+
return url;
|
|
48
|
+
}
|
|
49
|
+
buildHeaders(init) {
|
|
50
|
+
const headers = new Headers(init);
|
|
51
|
+
headers.set("Accept", "application/json");
|
|
52
|
+
if (this.accessToken) {
|
|
53
|
+
headers.set("Authorization", `Bearer ${this.accessToken}`);
|
|
54
|
+
}
|
|
55
|
+
return headers;
|
|
56
|
+
}
|
|
57
|
+
prepareBody(body) {
|
|
58
|
+
if (body instanceof FormData ||
|
|
59
|
+
body instanceof Blob ||
|
|
60
|
+
body instanceof URLSearchParams ||
|
|
61
|
+
typeof body === "string") {
|
|
62
|
+
return { body, headers: this.buildHeaders() };
|
|
63
|
+
}
|
|
64
|
+
const headers = this.buildHeaders({ "Content-Type": "application/json" });
|
|
65
|
+
return { body: JSON.stringify(body), headers };
|
|
66
|
+
}
|
|
67
|
+
async requestRaw(path, options = {}) {
|
|
68
|
+
const url = this.buildUrl(path, options.query);
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timeout = options.timeout ?? this.timeout;
|
|
71
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
72
|
+
const init = {
|
|
73
|
+
method: options.method ?? "GET",
|
|
74
|
+
headers: this.buildHeaders(options.headers),
|
|
75
|
+
signal: controller.signal
|
|
76
|
+
};
|
|
77
|
+
if (options.body !== undefined) {
|
|
78
|
+
const prepared = this.prepareBody(options.body);
|
|
79
|
+
init.body = prepared.body;
|
|
80
|
+
init.headers = prepared.headers;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch(url, init);
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
await this.throwError(response);
|
|
86
|
+
}
|
|
87
|
+
return response;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async request(path, options = {}) {
|
|
94
|
+
if (options.raw) {
|
|
95
|
+
return (await this.requestRaw(path, options));
|
|
96
|
+
}
|
|
97
|
+
const response = await this.requestRaw(path, options);
|
|
98
|
+
if (response.status === 204)
|
|
99
|
+
return undefined;
|
|
100
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
101
|
+
if (contentType.includes("text/plain")) {
|
|
102
|
+
return (await response.text());
|
|
103
|
+
}
|
|
104
|
+
return (await response.json());
|
|
105
|
+
}
|
|
106
|
+
async get(path, options) {
|
|
107
|
+
return this.request(path, { ...options, method: "GET" });
|
|
108
|
+
}
|
|
109
|
+
async post(path, body, options) {
|
|
110
|
+
return this.request(path, { ...options, method: "POST", body });
|
|
111
|
+
}
|
|
112
|
+
async patch(path, body, options) {
|
|
113
|
+
return this.request(path, { ...options, method: "PATCH", body });
|
|
114
|
+
}
|
|
115
|
+
async del(path, options) {
|
|
116
|
+
return this.request(path, { ...options, method: "DELETE" });
|
|
117
|
+
}
|
|
118
|
+
async authenticate() {
|
|
119
|
+
if (!this.clientId || !this.clientSecret) {
|
|
120
|
+
throw new Error("clientId and clientSecret are required to authenticate");
|
|
121
|
+
}
|
|
122
|
+
const tokens = await this.post("/api/v1/auth/universal-auth/login", { clientId: this.clientId, clientSecret: this.clientSecret });
|
|
123
|
+
this.setToken(tokens.accessToken);
|
|
124
|
+
return tokens;
|
|
125
|
+
}
|
|
126
|
+
async throwError(response) {
|
|
127
|
+
let payload;
|
|
128
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
129
|
+
try {
|
|
130
|
+
payload = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
payload = response.statusText;
|
|
134
|
+
}
|
|
135
|
+
const message = typeof payload === "object" && payload !== null && "message" in payload
|
|
136
|
+
? String(payload.message)
|
|
137
|
+
: typeof payload === "string"
|
|
138
|
+
? payload
|
|
139
|
+
: response.statusText;
|
|
140
|
+
const code = typeof payload === "object" && payload !== null && "code" in payload
|
|
141
|
+
? String(payload.code)
|
|
142
|
+
: `HTTP_${response.status}`;
|
|
143
|
+
throw new SanctumApiError(message, code, response.status, payload);
|
|
144
|
+
}
|
|
145
|
+
}
|
package/dist/dotenv.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface DotenvApi {
|
|
2
|
+
parseDotenv(text: string): Record<string, string>;
|
|
3
|
+
renderDotenv(values: Record<string, string | undefined>): string;
|
|
4
|
+
resolveReferences(values: Record<string, string>, options?: ResolveReferencesOptions): Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export interface ResolveReferencesOptions {
|
|
7
|
+
prefix?: string;
|
|
8
|
+
suffix?: string;
|
|
9
|
+
maxDepth?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const parseDotenv: (text: string) => Record<string, string>;
|
|
12
|
+
export declare const renderDotenv: (values: Record<string, string | undefined>) => string;
|
|
13
|
+
export declare const resolveReferences: (values: Record<string, string>, options?: ResolveReferencesOptions) => Record<string, string>;
|
|
14
|
+
export declare const createDotenv: () => DotenvApi;
|
package/dist/dotenv.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const MOBILE_FILE_KEYS = [
|
|
2
|
+
'GOOGLE_SERVICES_JSON',
|
|
3
|
+
'GOOGLE_SERVICE_INFO_PLIST',
|
|
4
|
+
'RELEASE_KEYSTORE',
|
|
5
|
+
];
|
|
6
|
+
const isMobileFileKey = (key) => MOBILE_FILE_KEYS.includes(key) ||
|
|
7
|
+
key.toLowerCase().endsWith('_base64') ||
|
|
8
|
+
/google-services|googleservice-info|keystore|plist/i.test(key);
|
|
9
|
+
export const parseDotenv = (text) => {
|
|
10
|
+
const result = {};
|
|
11
|
+
for (let line of text.split('\n')) {
|
|
12
|
+
line = line.trim();
|
|
13
|
+
if (!line || line.startsWith('#'))
|
|
14
|
+
continue;
|
|
15
|
+
const index = line.indexOf('=');
|
|
16
|
+
if (index === -1)
|
|
17
|
+
continue;
|
|
18
|
+
const key = line.slice(0, index).trim();
|
|
19
|
+
let value = line.slice(index + 1).trim();
|
|
20
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
21
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
22
|
+
value = value.slice(1, -1);
|
|
23
|
+
}
|
|
24
|
+
result[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
export const renderDotenv = (values) => {
|
|
29
|
+
const lines = [];
|
|
30
|
+
for (const [key, value] of Object.entries(values)) {
|
|
31
|
+
if (value === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
if (isMobileFileKey(key) || value === '') {
|
|
34
|
+
lines.push(`${key}=${value}`);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const needsQuotes = value.includes(' ') ||
|
|
38
|
+
value.includes('#') ||
|
|
39
|
+
value.includes('\n') ||
|
|
40
|
+
value.includes('"') ||
|
|
41
|
+
value.includes("'");
|
|
42
|
+
if (needsQuotes) {
|
|
43
|
+
lines.push(`${key}="${value.replace(/"/g, '\\"')}"`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
lines.push(`${key}=${value}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return lines.join('\n');
|
|
50
|
+
};
|
|
51
|
+
export const resolveReferences = (values, options = {}) => {
|
|
52
|
+
const prefix = options.prefix ?? '${';
|
|
53
|
+
const suffix = options.suffix ?? '}';
|
|
54
|
+
const maxDepth = options.maxDepth ?? 10;
|
|
55
|
+
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
56
|
+
const pattern = new RegExp(`${escapeRegex(prefix)}(.*?)${escapeRegex(suffix)}`, 'g');
|
|
57
|
+
const resolved = { ...values };
|
|
58
|
+
const resolveOne = (value, stack) => {
|
|
59
|
+
return value.replace(pattern, (match, refKey) => {
|
|
60
|
+
const trimmed = refKey.trim();
|
|
61
|
+
if (stack.has(trimmed))
|
|
62
|
+
return match;
|
|
63
|
+
const refValue = resolved[trimmed];
|
|
64
|
+
if (refValue === undefined)
|
|
65
|
+
return match;
|
|
66
|
+
stack.add(trimmed);
|
|
67
|
+
const inner = resolveOne(refValue, stack);
|
|
68
|
+
stack.delete(trimmed);
|
|
69
|
+
return inner;
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
73
|
+
let changed = false;
|
|
74
|
+
for (const [key, value] of Object.entries(resolved)) {
|
|
75
|
+
const stack = new Set([key]);
|
|
76
|
+
const next = resolveOne(value, stack);
|
|
77
|
+
if (next !== resolved[key]) {
|
|
78
|
+
resolved[key] = next;
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!changed)
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
return resolved;
|
|
86
|
+
};
|
|
87
|
+
export const createDotenv = () => ({
|
|
88
|
+
parseDotenv,
|
|
89
|
+
renderDotenv,
|
|
90
|
+
resolveReferences,
|
|
91
|
+
});
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { MrSecretClient, QueryValue } from './client.js';
|
|
2
|
+
export interface FileSecret {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
storageKey?: string;
|
|
6
|
+
contentType?: string | null;
|
|
7
|
+
size: number;
|
|
8
|
+
version: number;
|
|
9
|
+
folderId?: string;
|
|
10
|
+
environmentId: string;
|
|
11
|
+
projectId: string;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
updatedAt: string;
|
|
14
|
+
}
|
|
15
|
+
export interface FileSecretVersion {
|
|
16
|
+
id: string;
|
|
17
|
+
versionNumber: number;
|
|
18
|
+
storageKey: string;
|
|
19
|
+
contentType?: string | null;
|
|
20
|
+
size: number;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
comment?: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface FileUploadInput {
|
|
25
|
+
name?: string;
|
|
26
|
+
folderId?: string;
|
|
27
|
+
contentType?: string;
|
|
28
|
+
file: Blob | Buffer | Uint8Array | string;
|
|
29
|
+
filename?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface FileMetadataInput {
|
|
32
|
+
name?: string;
|
|
33
|
+
folderId?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface PresignedDownload {
|
|
36
|
+
url: string;
|
|
37
|
+
expiresIn: number;
|
|
38
|
+
}
|
|
39
|
+
export interface StreamedDownload {
|
|
40
|
+
name: string;
|
|
41
|
+
contentType: string;
|
|
42
|
+
size?: number;
|
|
43
|
+
buffer: Buffer;
|
|
44
|
+
}
|
|
45
|
+
export interface ListFilesOptions extends Record<string, QueryValue> {
|
|
46
|
+
folderId?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface FilesApi {
|
|
49
|
+
listFiles(projectId: string, envId: string, options?: ListFilesOptions): Promise<FileSecret[]>;
|
|
50
|
+
uploadFile(projectId: string, envId: string, input: FileUploadInput): Promise<Omit<FileSecret, 'projectId' | 'environmentId' | 'updatedAt'>>;
|
|
51
|
+
getFile(projectId: string, envId: string, id: string): Promise<FileSecret>;
|
|
52
|
+
downloadFile(projectId: string, envId: string, id: string, mode?: 'presigned' | 'stream'): Promise<PresignedDownload | StreamedDownload>;
|
|
53
|
+
updateFile(projectId: string, envId: string, id: string, input: FileUploadInput | FileMetadataInput): Promise<Omit<FileSecret, 'projectId' | 'environmentId'>>;
|
|
54
|
+
deleteFile(projectId: string, envId: string, id: string): Promise<{
|
|
55
|
+
success: true;
|
|
56
|
+
}>;
|
|
57
|
+
listFileVersions(projectId: string, envId: string, id: string): Promise<FileSecretVersion[]>;
|
|
58
|
+
}
|
|
59
|
+
export declare const createFiles: (client: MrSecretClient) => FilesApi;
|
package/dist/files.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const toBlob = (input, contentType) => {
|
|
2
|
+
if (input instanceof Blob)
|
|
3
|
+
return input;
|
|
4
|
+
return new Blob([input], { type: contentType ?? 'application/octet-stream' });
|
|
5
|
+
};
|
|
6
|
+
const buildFormData = (input, hasFile) => {
|
|
7
|
+
const form = new FormData();
|
|
8
|
+
if ('file' in input && input.file !== undefined && hasFile) {
|
|
9
|
+
const filename = input.filename ?? input.name ?? 'file';
|
|
10
|
+
const blob = toBlob(input.file, input.contentType);
|
|
11
|
+
form.append('file', blob, filename);
|
|
12
|
+
}
|
|
13
|
+
if (input.name)
|
|
14
|
+
form.append('name', input.name);
|
|
15
|
+
if (input.folderId)
|
|
16
|
+
form.append('folderId', input.folderId);
|
|
17
|
+
return form;
|
|
18
|
+
};
|
|
19
|
+
export const createFiles = (client) => ({
|
|
20
|
+
listFiles: (projectId, envId, options) => client.get(`/api/v1/projects/${projectId}/environments/${envId}/files`, { query: options }),
|
|
21
|
+
uploadFile: (projectId, envId, input) => {
|
|
22
|
+
const body = buildFormData(input, true);
|
|
23
|
+
return client.post(`/api/v1/projects/${projectId}/environments/${envId}/files`, body);
|
|
24
|
+
},
|
|
25
|
+
getFile: (projectId, envId, id) => client.get(`/api/v1/projects/${projectId}/environments/${envId}/files/${id}`),
|
|
26
|
+
downloadFile: async (projectId, envId, id, mode) => {
|
|
27
|
+
const query = { mode: mode ?? 'stream' };
|
|
28
|
+
if (mode === 'presigned') {
|
|
29
|
+
return client.get(`/api/v1/projects/${projectId}/environments/${envId}/files/${id}/download`, { query });
|
|
30
|
+
}
|
|
31
|
+
const response = await client.requestRaw(`/api/v1/projects/${projectId}/environments/${envId}/files/${id}/download`, { query });
|
|
32
|
+
const contentType = response.headers.get('content-type') ?? 'application/octet-stream';
|
|
33
|
+
const contentLength = response.headers.get('content-length');
|
|
34
|
+
const disposition = response.headers.get('content-disposition');
|
|
35
|
+
const nameMatch = disposition?.match(/filename="([^"]+)"/);
|
|
36
|
+
const name = nameMatch?.[1] ?? id;
|
|
37
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
38
|
+
return {
|
|
39
|
+
name,
|
|
40
|
+
contentType,
|
|
41
|
+
size: contentLength ? Number(contentLength) : undefined,
|
|
42
|
+
buffer,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
updateFile: (projectId, envId, id, input) => {
|
|
46
|
+
const hasFile = 'file' in input && input.file !== undefined;
|
|
47
|
+
const body = hasFile ? buildFormData(input, true) : input;
|
|
48
|
+
return client.patch(`/api/v1/projects/${projectId}/environments/${envId}/files/${id}`, body);
|
|
49
|
+
},
|
|
50
|
+
deleteFile: (projectId, envId, id) => client.del(`/api/v1/projects/${projectId}/environments/${envId}/files/${id}`),
|
|
51
|
+
listFileVersions: (projectId, envId, id) => client.get(`/api/v1/projects/${projectId}/environments/${envId}/files/${id}/versions`),
|
|
52
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MrSecretClient } from './client.js';
|
|
2
|
+
export interface Identity {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
projectId: string;
|
|
7
|
+
isActive: boolean;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export interface IdentityWithSecret extends Identity {
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CreateIdentityInput {
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
export interface IdentitiesApi {
|
|
18
|
+
listIdentities(projectId: string): Promise<Identity[]>;
|
|
19
|
+
createIdentity(projectId: string, input: CreateIdentityInput): Promise<IdentityWithSecret>;
|
|
20
|
+
deleteIdentity(projectId: string, id: string): Promise<{
|
|
21
|
+
success: true;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export declare const createIdentities: (client: MrSecretClient) => IdentitiesApi;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const createIdentities = (client) => ({
|
|
2
|
+
listIdentities: (projectId) => client.get(`/api/v1/projects/${projectId}/identities`),
|
|
3
|
+
createIdentity: (projectId, input) => client.post(`/api/v1/projects/${projectId}/identities`, input),
|
|
4
|
+
deleteIdentity: (projectId, id) => client.del(`/api/v1/projects/${projectId}/identities/${id}`),
|
|
5
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SanctumClient, type ClientOptions } from "./client.js";
|
|
2
|
+
import { type AuthApi } from "./auth.js";
|
|
3
|
+
import { type ProjectsApi } from "./projects.js";
|
|
4
|
+
import { type SecretsApi } from "./secrets.js";
|
|
5
|
+
import { type DotenvApi } from "./dotenv.js";
|
|
6
|
+
export * from "./client.js";
|
|
7
|
+
export * from "./auth.js";
|
|
8
|
+
export * from "./projects.js";
|
|
9
|
+
export * from "./secrets.js";
|
|
10
|
+
export * from "./dotenv.js";
|
|
11
|
+
export interface SanctumSdk extends SanctumClient {
|
|
12
|
+
auth: AuthApi;
|
|
13
|
+
projects: ProjectsApi;
|
|
14
|
+
secrets: SecretsApi;
|
|
15
|
+
dotenv: DotenvApi;
|
|
16
|
+
}
|
|
17
|
+
export declare class SanctumSdk extends SanctumClient {
|
|
18
|
+
auth: AuthApi;
|
|
19
|
+
projects: ProjectsApi;
|
|
20
|
+
secrets: SecretsApi;
|
|
21
|
+
dotenv: DotenvApi;
|
|
22
|
+
constructor(options?: ClientOptions);
|
|
23
|
+
}
|
|
24
|
+
export declare const createClient: (options?: ClientOptions) => SanctumSdk;
|
|
25
|
+
export default createClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SanctumClient } from "./client.js";
|
|
2
|
+
import { createAuth } from "./auth.js";
|
|
3
|
+
import { createProjects } from "./projects.js";
|
|
4
|
+
import { createSecrets } from "./secrets.js";
|
|
5
|
+
import { createDotenv } from "./dotenv.js";
|
|
6
|
+
export * from "./client.js";
|
|
7
|
+
export * from "./auth.js";
|
|
8
|
+
export * from "./projects.js";
|
|
9
|
+
export * from "./secrets.js";
|
|
10
|
+
export * from "./dotenv.js";
|
|
11
|
+
export class SanctumSdk extends SanctumClient {
|
|
12
|
+
auth;
|
|
13
|
+
projects;
|
|
14
|
+
secrets;
|
|
15
|
+
dotenv;
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
super(options);
|
|
18
|
+
this.auth = createAuth(this);
|
|
19
|
+
this.projects = createProjects(this);
|
|
20
|
+
this.secrets = createSecrets(this);
|
|
21
|
+
this.dotenv = createDotenv();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export const createClient = (options = {}) => {
|
|
25
|
+
return new SanctumSdk(options);
|
|
26
|
+
};
|
|
27
|
+
export default createClient;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MrSecretClient } from './client.js';
|
|
2
|
+
export type Action = 'read' | 'write' | 'delete' | 'admin' | 'manage_permissions';
|
|
3
|
+
export interface Permission {
|
|
4
|
+
id: string;
|
|
5
|
+
subjectType: 'User' | 'Identity';
|
|
6
|
+
subjectId: string;
|
|
7
|
+
resourceType: 'Project' | 'ProjectGroup';
|
|
8
|
+
resourceId: string;
|
|
9
|
+
action: Action;
|
|
10
|
+
grantedBy?: string | null;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
}
|
|
13
|
+
export interface CreatePermissionInput {
|
|
14
|
+
subjectType: 'User' | 'Identity';
|
|
15
|
+
subjectId: string;
|
|
16
|
+
action: Action;
|
|
17
|
+
}
|
|
18
|
+
export interface PermissionsApi {
|
|
19
|
+
listProjectPermissions(projectId: string): Promise<Permission[]>;
|
|
20
|
+
createProjectPermission(projectId: string, input: CreatePermissionInput): Promise<Permission>;
|
|
21
|
+
deleteProjectPermission(projectId: string, id: string): Promise<{
|
|
22
|
+
success: true;
|
|
23
|
+
}>;
|
|
24
|
+
listGroupPermissions(groupId: string): Promise<Permission[]>;
|
|
25
|
+
createGroupPermission(groupId: string, input: CreatePermissionInput): Promise<Permission>;
|
|
26
|
+
deleteGroupPermission(groupId: string, id: string): Promise<{
|
|
27
|
+
success: true;
|
|
28
|
+
}>;
|
|
29
|
+
listMyPermissions(): Promise<Permission[]>;
|
|
30
|
+
}
|
|
31
|
+
export declare const createPermissions: (client: MrSecretClient) => PermissionsApi;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const createPermissions = (client) => ({
|
|
2
|
+
listProjectPermissions: (projectId) => client.get(`/api/v1/projects/${projectId}/permissions`),
|
|
3
|
+
createProjectPermission: (projectId, input) => client.post(`/api/v1/projects/${projectId}/permissions`, input),
|
|
4
|
+
deleteProjectPermission: (projectId, id) => client.del(`/api/v1/projects/${projectId}/permissions/${id}`),
|
|
5
|
+
listGroupPermissions: (groupId) => client.get(`/api/v1/groups/${groupId}/permissions`),
|
|
6
|
+
createGroupPermission: (groupId, input) => client.post(`/api/v1/groups/${groupId}/permissions`, input),
|
|
7
|
+
deleteGroupPermission: (groupId, id) => client.del(`/api/v1/groups/${groupId}/permissions/${id}`),
|
|
8
|
+
listMyPermissions: () => client.get('/api/v1/users/me/permissions'),
|
|
9
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { SanctumClient } from "./client.js";
|
|
2
|
+
export interface Project {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
orgId: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ProjectEnvironment {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
slug: string;
|
|
15
|
+
position: number;
|
|
16
|
+
projectId: string;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
updatedAt: string;
|
|
19
|
+
}
|
|
20
|
+
export interface CreateProjectInput {
|
|
21
|
+
projectName: string;
|
|
22
|
+
slug?: string;
|
|
23
|
+
type?: "secret-manager" | "cert-manager" | "kms" | "secret-scanning" | "pam";
|
|
24
|
+
shouldCreateDefaultEnvs?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ProjectsApi {
|
|
27
|
+
list(): Promise<{
|
|
28
|
+
projects: Project[];
|
|
29
|
+
}>;
|
|
30
|
+
get(projectId: string): Promise<{
|
|
31
|
+
project: Project;
|
|
32
|
+
}>;
|
|
33
|
+
getBySlug(slug: string): Promise<Project>;
|
|
34
|
+
create(input: CreateProjectInput): Promise<{
|
|
35
|
+
project: Project;
|
|
36
|
+
}>;
|
|
37
|
+
listEnvironments(projectId: string): Promise<{
|
|
38
|
+
environments: ProjectEnvironment[];
|
|
39
|
+
}>;
|
|
40
|
+
getEnvironmentBySlug(projectId: string, envSlug: string): Promise<{
|
|
41
|
+
environment: ProjectEnvironment;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
export declare const createProjects: (client: SanctumClient) => ProjectsApi;
|
package/dist/projects.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const createProjects = (client) => ({
|
|
2
|
+
list: () => client.get("/api/v1/projects"),
|
|
3
|
+
get: (projectId) => client.get(`/api/v1/projects/${projectId}`),
|
|
4
|
+
getBySlug: (slug) => client.get(`/api/v1/projects/slug/${encodeURIComponent(slug)}`),
|
|
5
|
+
create: (input) => client.post("/api/v1/projects", input),
|
|
6
|
+
listEnvironments: async (projectId) => {
|
|
7
|
+
const { project } = await client.get(`/api/v1/projects/${projectId}`);
|
|
8
|
+
return { environments: project.environments ?? [] };
|
|
9
|
+
},
|
|
10
|
+
getEnvironmentBySlug: (projectId, envSlug) => client.get(`/api/v1/projects/${projectId}/environments/slug/${encodeURIComponent(envSlug)}`)
|
|
11
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { QueryValue, SanctumClient } from "./client.js";
|
|
2
|
+
export interface Secret {
|
|
3
|
+
id: string;
|
|
4
|
+
secretKey: string;
|
|
5
|
+
secretValue?: string;
|
|
6
|
+
secretComment?: string;
|
|
7
|
+
version: number;
|
|
8
|
+
type: "shared" | "personal";
|
|
9
|
+
secretPath?: string;
|
|
10
|
+
secretValueHidden: boolean;
|
|
11
|
+
tags?: {
|
|
12
|
+
id: string;
|
|
13
|
+
slug: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
color?: string;
|
|
16
|
+
}[];
|
|
17
|
+
secretMetadata?: {
|
|
18
|
+
key: string;
|
|
19
|
+
value: string;
|
|
20
|
+
}[];
|
|
21
|
+
createdAt: string;
|
|
22
|
+
updatedAt: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ListSecretsOptions extends Record<string, QueryValue> {
|
|
25
|
+
workspaceId?: string;
|
|
26
|
+
workspaceSlug?: string;
|
|
27
|
+
environment?: string;
|
|
28
|
+
secretPath?: string;
|
|
29
|
+
viewSecretValue?: boolean;
|
|
30
|
+
expandSecretReferences?: boolean;
|
|
31
|
+
recursive?: boolean;
|
|
32
|
+
include_imports?: boolean;
|
|
33
|
+
tagSlugs?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ListSecretsOutput {
|
|
36
|
+
secrets: Secret[];
|
|
37
|
+
imports?: {
|
|
38
|
+
secretPath: string;
|
|
39
|
+
environment: string;
|
|
40
|
+
folderId?: string;
|
|
41
|
+
secrets: Secret[];
|
|
42
|
+
}[];
|
|
43
|
+
}
|
|
44
|
+
export interface SecretScopeInput {
|
|
45
|
+
workspaceId?: string;
|
|
46
|
+
workspaceSlug?: string;
|
|
47
|
+
projectSlug?: string;
|
|
48
|
+
environment: string;
|
|
49
|
+
secretPath?: string;
|
|
50
|
+
type?: "shared" | "personal";
|
|
51
|
+
}
|
|
52
|
+
export interface CreateSecretInput extends SecretScopeInput {
|
|
53
|
+
secretValue: string;
|
|
54
|
+
secretComment?: string;
|
|
55
|
+
skipMultilineEncoding?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface UpdateSecretInput extends SecretScopeInput {
|
|
58
|
+
secretValue?: string;
|
|
59
|
+
secretComment?: string;
|
|
60
|
+
tags?: string[];
|
|
61
|
+
skipMultilineEncoding?: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface BatchSecretOperation {
|
|
64
|
+
type: "create" | "update" | "delete";
|
|
65
|
+
secretName: string;
|
|
66
|
+
secretValue?: string;
|
|
67
|
+
secretComment?: string;
|
|
68
|
+
tags?: string[];
|
|
69
|
+
}
|
|
70
|
+
export interface SecretWriteResult {
|
|
71
|
+
secret?: Secret;
|
|
72
|
+
/** present when a change-approval policy intercepted the write instead */
|
|
73
|
+
approval?: {
|
|
74
|
+
id: string;
|
|
75
|
+
slug?: string;
|
|
76
|
+
status?: string;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export interface SecretsApi {
|
|
80
|
+
list(options: ListSecretsOptions): Promise<ListSecretsOutput>;
|
|
81
|
+
get(secretName: string, scope: SecretScopeInput & {
|
|
82
|
+
expandSecretReferences?: boolean;
|
|
83
|
+
version?: number;
|
|
84
|
+
}): Promise<{
|
|
85
|
+
secret: Secret;
|
|
86
|
+
}>;
|
|
87
|
+
create(secretName: string, input: CreateSecretInput): Promise<SecretWriteResult>;
|
|
88
|
+
update(secretName: string, input: UpdateSecretInput): Promise<SecretWriteResult>;
|
|
89
|
+
delete(secretName: string, scope: SecretScopeInput): Promise<SecretWriteResult>;
|
|
90
|
+
batch(scope: SecretScopeInput, operations: BatchSecretOperation[]): Promise<unknown>;
|
|
91
|
+
}
|
|
92
|
+
export declare const createSecrets: (client: SanctumClient) => SecretsApi;
|
package/dist/secrets.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const createSecrets = (client) => ({
|
|
2
|
+
list: (options) => client.get("/api/v3/secrets/raw", {
|
|
3
|
+
query: options
|
|
4
|
+
}),
|
|
5
|
+
get: (secretName, scope) => client.get(`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`, {
|
|
6
|
+
query: scope
|
|
7
|
+
}),
|
|
8
|
+
create: (secretName, input) => client.post(`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`, {
|
|
9
|
+
type: "shared",
|
|
10
|
+
secretPath: "/",
|
|
11
|
+
...input
|
|
12
|
+
}),
|
|
13
|
+
update: (secretName, input) => client.patch(`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`, {
|
|
14
|
+
type: "shared",
|
|
15
|
+
secretPath: "/",
|
|
16
|
+
...input
|
|
17
|
+
}),
|
|
18
|
+
delete: (secretName, scope) => client.request(`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`, {
|
|
19
|
+
method: "DELETE",
|
|
20
|
+
body: { type: "shared", secretPath: "/", ...scope }
|
|
21
|
+
}),
|
|
22
|
+
batch: (scope, operations) => client.post("/api/v3/secrets/batch/raw", {
|
|
23
|
+
secretPath: "/",
|
|
24
|
+
type: "shared",
|
|
25
|
+
...scope,
|
|
26
|
+
secrets: operations
|
|
27
|
+
})
|
|
28
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { MrSecretClient } from './client.js';
|
|
2
|
+
export type DeliveryStatus = 'pending' | 'delivered' | 'failed' | 'retrying';
|
|
3
|
+
export interface Webhook {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
url: string;
|
|
7
|
+
events: string[];
|
|
8
|
+
active: boolean;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
export interface WebhookWithSecret extends Webhook {
|
|
13
|
+
secret?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface WebhookDelivery {
|
|
16
|
+
id: string;
|
|
17
|
+
webhookId: string;
|
|
18
|
+
event: string;
|
|
19
|
+
payload: unknown;
|
|
20
|
+
responseBody?: string | null;
|
|
21
|
+
httpStatus?: number | null;
|
|
22
|
+
status: DeliveryStatus;
|
|
23
|
+
attempts: number;
|
|
24
|
+
nextRetryAt?: string | null;
|
|
25
|
+
deliveredAt?: string | null;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface CreateWebhookInput {
|
|
30
|
+
name: string;
|
|
31
|
+
url: string;
|
|
32
|
+
events: string[];
|
|
33
|
+
active?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface UpdateWebhookInput {
|
|
36
|
+
name?: string;
|
|
37
|
+
url?: string;
|
|
38
|
+
events?: string[];
|
|
39
|
+
active?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface WebhooksApi {
|
|
42
|
+
listWebhooks(projectId: string): Promise<Webhook[]>;
|
|
43
|
+
createWebhook(projectId: string, input: CreateWebhookInput): Promise<WebhookWithSecret>;
|
|
44
|
+
getWebhook(projectId: string, id: string): Promise<Webhook>;
|
|
45
|
+
updateWebhook(projectId: string, id: string, input: UpdateWebhookInput): Promise<Webhook>;
|
|
46
|
+
deleteWebhook(projectId: string, id: string): Promise<{
|
|
47
|
+
success: true;
|
|
48
|
+
}>;
|
|
49
|
+
listDeliveries(projectId: string, id: string): Promise<WebhookDelivery[]>;
|
|
50
|
+
testWebhook(projectId: string, id: string): Promise<{
|
|
51
|
+
success: true;
|
|
52
|
+
deliveryId?: string;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
export declare const createWebhooks: (client: MrSecretClient) => WebhooksApi;
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const createWebhooks = (client) => ({
|
|
2
|
+
listWebhooks: (projectId) => client.get(`/api/v1/projects/${projectId}/webhooks`),
|
|
3
|
+
createWebhook: (projectId, input) => client.post(`/api/v1/projects/${projectId}/webhooks`, input),
|
|
4
|
+
getWebhook: (projectId, id) => client.get(`/api/v1/projects/${projectId}/webhooks/${id}`),
|
|
5
|
+
updateWebhook: (projectId, id, input) => client.patch(`/api/v1/projects/${projectId}/webhooks/${id}`, input),
|
|
6
|
+
deleteWebhook: (projectId, id) => client.del(`/api/v1/projects/${projectId}/webhooks/${id}`),
|
|
7
|
+
listDeliveries: (projectId, id) => client.get(`/api/v1/projects/${projectId}/webhooks/${id}/deliveries`),
|
|
8
|
+
testWebhook: (projectId, id) => client.post(`/api/v1/projects/${projectId}/webhooks/${id}/test`),
|
|
9
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sanctum-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for the Sanctum secrets platform",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/neang-mengseang/sanctum.git",
|
|
9
|
+
"directory": "packages/sdk"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"dev": "tsc --watch",
|
|
27
|
+
"lint": "tsc --noEmit",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\""
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"typescript": "^5.4.0"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
}
|
|
38
|
+
}
|