@coffre/cli 0.1.1 → 0.1.3
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/README.md +4 -1
- package/dist/access-CO1kGjWW.js +74 -0
- package/dist/connect-BONR52al.js +770 -0
- package/dist/connect-BZdPYNbZ.js +2 -0
- package/dist/esm-B69mMLcR.js +2 -0
- package/dist/esm-Bts-esAl.js +4202 -0
- package/dist/keys-RhB7ayOY.js +726 -0
- package/dist/main.js +10 -137
- package/dist/migrations/postgres/0000_baseline.sql +505 -0
- package/dist/migrations/postgres/meta/0000_snapshot.json +1939 -0
- package/dist/migrations/postgres/meta/_journal.json +13 -0
- package/dist/migrator-W1a-ooWi.js +37 -0
- package/dist/node-postgres-BlNvYfq_.js +2 -0
- package/dist/node-postgres-_y-EeSHQ.js +2831 -0
- package/dist/portable-CPh2thNN.js +87 -0
- package/dist/relations-CJCGPRst.js +2712 -0
- package/dist/schema-sqlite-Byp7B3yI.js +1078 -0
- package/dist/setup-3OJc_VCA.js +756 -0
- package/dist/templates/node/README.md +41 -48
- package/dist/templates/node/package.json +3 -3
- package/dist/templates/node/server.env.example +5 -4
- package/dist/templates/node/src/server.ts +3 -3
- package/dist/templates/node/src/vault.ts +7 -5
- package/dist/templates/node/vault.env.example +7 -7
- package/dist/templates/workers/README.md +47 -57
- package/dist/templates/workers/app/src/worker.ts +2 -2
- package/dist/templates/workers/app/wrangler.jsonc +4 -4
- package/dist/templates/workers/package.json +4 -4
- package/dist/templates/workers/vault/src/worker.ts +8 -7
- package/dist/templates/workers/vault/wrangler.jsonc +8 -6
- package/package.json +7 -4
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
import { d as esm_default$1, m as __toESM, p as require_pg_connection_string } from "./esm-Bts-esAl.js";
|
|
2
|
+
import { n as ROLE_NAMES, r as assignableToEnvironment } from "./access-CO1kGjWW.js";
|
|
3
|
+
import { A as bigint, G as sql, H as SQL, O as customType, T as integer, ct as entityKind, f as primaryKey, g as uuid, it as foreignKey, k as boolean, lt as is, m as pgTable, rt as unique, tt as IndexedColumn, x as text, y as timestamp } from "./relations-CJCGPRst.js";
|
|
4
|
+
import { r as drizzle } from "./node-postgres-_y-EeSHQ.js";
|
|
5
|
+
import { a as trackCommits, i as relationsOf, n as asPostgresDatabase, r as ACTIVE_SUBJECT } from "./portable-CPh2thNN.js";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { checkServerIdentity } from "node:tls";
|
|
8
|
+
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@5.20260920.1_@libsql+client@0.18.0_@types+pg@8.20.0_pg@8.16.3/node_modules/drizzle-orm/pg-core/checks.js
|
|
9
|
+
var CheckBuilder = class {
|
|
10
|
+
constructor(name, value) {
|
|
11
|
+
this.name = name;
|
|
12
|
+
this.value = value;
|
|
13
|
+
}
|
|
14
|
+
static [entityKind] = "PgCheckBuilder";
|
|
15
|
+
brand;
|
|
16
|
+
/** @internal */
|
|
17
|
+
build(table) {
|
|
18
|
+
return new Check(table, this);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var Check = class {
|
|
22
|
+
constructor(table, builder) {
|
|
23
|
+
this.table = table;
|
|
24
|
+
this.name = builder.name;
|
|
25
|
+
this.value = builder.value;
|
|
26
|
+
}
|
|
27
|
+
static [entityKind] = "PgCheck";
|
|
28
|
+
name;
|
|
29
|
+
value;
|
|
30
|
+
};
|
|
31
|
+
function check(name, value) {
|
|
32
|
+
return new CheckBuilder(name, value);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@5.20260920.1_@libsql+client@0.18.0_@types+pg@8.20.0_pg@8.16.3/node_modules/drizzle-orm/pg-core/indexes.js
|
|
36
|
+
var IndexBuilderOn = class {
|
|
37
|
+
constructor(unique, name) {
|
|
38
|
+
this.unique = unique;
|
|
39
|
+
this.name = name;
|
|
40
|
+
}
|
|
41
|
+
static [entityKind] = "PgIndexBuilderOn";
|
|
42
|
+
on(...columns) {
|
|
43
|
+
return new IndexBuilder(columns.map((it) => {
|
|
44
|
+
if (is(it, SQL)) return it;
|
|
45
|
+
it = it;
|
|
46
|
+
const clonedIndexedColumn = new IndexedColumn(it.name, !!it.keyAsName, it.columnType, it.indexConfig);
|
|
47
|
+
it.indexConfig = JSON.parse(JSON.stringify(it.defaultConfig));
|
|
48
|
+
return clonedIndexedColumn;
|
|
49
|
+
}), this.unique, false, this.name);
|
|
50
|
+
}
|
|
51
|
+
onOnly(...columns) {
|
|
52
|
+
return new IndexBuilder(columns.map((it) => {
|
|
53
|
+
if (is(it, SQL)) return it;
|
|
54
|
+
it = it;
|
|
55
|
+
const clonedIndexedColumn = new IndexedColumn(it.name, !!it.keyAsName, it.columnType, it.indexConfig);
|
|
56
|
+
it.indexConfig = it.defaultConfig;
|
|
57
|
+
return clonedIndexedColumn;
|
|
58
|
+
}), this.unique, true, this.name);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Specify what index method to use. Choices are `btree`, `hash`, `gist`, `spgist`, `gin`, `brin`, or user-installed access methods like `bloom`. The default method is `btree.
|
|
62
|
+
*
|
|
63
|
+
* If you have the `pg_vector` extension installed in your database, you can use the `hnsw` and `ivfflat` options, which are predefined types.
|
|
64
|
+
*
|
|
65
|
+
* **You can always specify any string you want in the method, in case Drizzle doesn't have it natively in its types**
|
|
66
|
+
*
|
|
67
|
+
* @param method The name of the index method to be used
|
|
68
|
+
* @param columns
|
|
69
|
+
* @returns
|
|
70
|
+
*/
|
|
71
|
+
using(method, ...columns) {
|
|
72
|
+
return new IndexBuilder(columns.map((it) => {
|
|
73
|
+
if (is(it, SQL)) return it;
|
|
74
|
+
it = it;
|
|
75
|
+
const clonedIndexedColumn = new IndexedColumn(it.name, !!it.keyAsName, it.columnType, it.indexConfig);
|
|
76
|
+
it.indexConfig = JSON.parse(JSON.stringify(it.defaultConfig));
|
|
77
|
+
return clonedIndexedColumn;
|
|
78
|
+
}), this.unique, true, this.name, method);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var IndexBuilder = class {
|
|
82
|
+
static [entityKind] = "PgIndexBuilder";
|
|
83
|
+
/** @internal */
|
|
84
|
+
config;
|
|
85
|
+
constructor(columns, unique, only, name, method = "btree") {
|
|
86
|
+
this.config = {
|
|
87
|
+
name,
|
|
88
|
+
columns,
|
|
89
|
+
unique,
|
|
90
|
+
only,
|
|
91
|
+
method
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
concurrently() {
|
|
95
|
+
this.config.concurrently = true;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
with(obj) {
|
|
99
|
+
this.config.with = obj;
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
where(condition) {
|
|
103
|
+
this.config.where = condition;
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
/** @internal */
|
|
107
|
+
build(table) {
|
|
108
|
+
return new Index(this.config, table);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
var Index = class {
|
|
112
|
+
static [entityKind] = "PgIndex";
|
|
113
|
+
config;
|
|
114
|
+
constructor(config, table) {
|
|
115
|
+
this.config = {
|
|
116
|
+
...config,
|
|
117
|
+
table
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
function index(name) {
|
|
122
|
+
return new IndexBuilderOn(false, name);
|
|
123
|
+
}
|
|
124
|
+
function uniqueIndex(name) {
|
|
125
|
+
return new IndexBuilderOn(true, name);
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region ../db/dist/rolldown-runtime-D7D4PA-g.js
|
|
129
|
+
var __defProp = Object.defineProperty;
|
|
130
|
+
var __exportAll = (all, no_symbols) => {
|
|
131
|
+
let target = {};
|
|
132
|
+
for (var name in all) __defProp(target, name, {
|
|
133
|
+
get: all[name],
|
|
134
|
+
enumerable: true
|
|
135
|
+
});
|
|
136
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
137
|
+
return target;
|
|
138
|
+
};
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region ../db/dist/schema.js
|
|
141
|
+
var schema_exports = /* @__PURE__ */ __exportAll({
|
|
142
|
+
auditChainHead: () => auditChainHead,
|
|
143
|
+
auditLog: () => auditLog,
|
|
144
|
+
credentials: () => credentials,
|
|
145
|
+
deviceAuthorizations: () => deviceAuthorizations,
|
|
146
|
+
environments: () => environments,
|
|
147
|
+
environmentsRelations: () => environmentsRelations,
|
|
148
|
+
identities: () => identities,
|
|
149
|
+
projects: () => projects,
|
|
150
|
+
secretVersions: () => secretVersions,
|
|
151
|
+
secrets: () => secrets,
|
|
152
|
+
secretsRelations: () => secretsRelations,
|
|
153
|
+
syncKeys: () => syncKeys,
|
|
154
|
+
syncKeysRelations: () => syncKeysRelations,
|
|
155
|
+
syncs: () => syncs,
|
|
156
|
+
syncsRelations: () => syncsRelations,
|
|
157
|
+
vaultGrants: () => vaultGrants,
|
|
158
|
+
vaultMembers: () => vaultMembers
|
|
159
|
+
});
|
|
160
|
+
const bytea = customType({ dataType: () => "bytea" });
|
|
161
|
+
const createdAt = () => timestamp("created_at", { withTimezone: true }).notNull().defaultNow();
|
|
162
|
+
const projects = pgTable("projects", {
|
|
163
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
164
|
+
slug: text().notNull(),
|
|
165
|
+
name: text().notNull(),
|
|
166
|
+
createdAt: createdAt(),
|
|
167
|
+
archivedAt: timestamp("archived_at", { withTimezone: true })
|
|
168
|
+
}, (table) => [unique("projects_slug_key").on(table.slug), check("projects_slug_check", sql`${table.slug} ~ '^[a-z0-9][a-z0-9-]{0,62}$'`)]);
|
|
169
|
+
const environments = pgTable("environments", {
|
|
170
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
171
|
+
projectId: uuid("project_id").notNull(),
|
|
172
|
+
slug: text().notNull(),
|
|
173
|
+
name: text().notNull(),
|
|
174
|
+
createdAt: createdAt(),
|
|
175
|
+
archivedAt: timestamp("archived_at", { withTimezone: true })
|
|
176
|
+
}, (table) => [
|
|
177
|
+
unique("environments_project_id_slug_key").on(table.projectId, table.slug),
|
|
178
|
+
unique("environments_project_scoped").on(table.id, table.projectId),
|
|
179
|
+
check("environments_slug_check", sql`${table.slug} ~ '^[a-z0-9][a-z0-9-]{0,62}$'`),
|
|
180
|
+
foreignKey({
|
|
181
|
+
name: "environments_project_id_fkey",
|
|
182
|
+
columns: [table.projectId],
|
|
183
|
+
foreignColumns: [projects.id]
|
|
184
|
+
}).onDelete("restrict")
|
|
185
|
+
]);
|
|
186
|
+
const secrets = pgTable("secrets", {
|
|
187
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
188
|
+
projectId: uuid("project_id").notNull(),
|
|
189
|
+
environmentId: uuid("environment_id").notNull(),
|
|
190
|
+
key: text().notNull(),
|
|
191
|
+
createdAt: createdAt(),
|
|
192
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
193
|
+
currentVersionId: uuid("current_version_id").references(() => secretVersions.id, { onDelete: "no action" }),
|
|
194
|
+
currentVersion: integer("current_version").notNull().default(0),
|
|
195
|
+
archivedAt: timestamp("archived_at", { withTimezone: true })
|
|
196
|
+
}, (table) => [
|
|
197
|
+
unique("secrets_project_id_environment_id_key_key").on(table.projectId, table.environmentId, table.key),
|
|
198
|
+
check("secrets_key_check", sql`${table.key} ~ '^[A-Za-z_][A-Za-z0-9_]{0,127}$'`),
|
|
199
|
+
foreignKey({
|
|
200
|
+
name: "secrets_project_id_fkey",
|
|
201
|
+
columns: [table.projectId],
|
|
202
|
+
foreignColumns: [projects.id]
|
|
203
|
+
}).onDelete("restrict"),
|
|
204
|
+
foreignKey({
|
|
205
|
+
name: "secrets_environment_id_fkey",
|
|
206
|
+
columns: [table.environmentId],
|
|
207
|
+
foreignColumns: [environments.id]
|
|
208
|
+
}).onDelete("restrict"),
|
|
209
|
+
foreignKey({
|
|
210
|
+
name: "secrets_environment_in_project",
|
|
211
|
+
columns: [table.environmentId, table.projectId],
|
|
212
|
+
foreignColumns: [environments.id, environments.projectId]
|
|
213
|
+
}),
|
|
214
|
+
index("secrets_lookup_idx").on(table.projectId, table.environmentId, table.key)
|
|
215
|
+
]);
|
|
216
|
+
const secretVersions = pgTable("secret_versions", {
|
|
217
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
218
|
+
secretId: uuid("secret_id").notNull(),
|
|
219
|
+
version: integer().notNull(),
|
|
220
|
+
envelopeVersion: integer("envelope_version").notNull(),
|
|
221
|
+
ciphertext: bytea().notNull(),
|
|
222
|
+
iv: bytea().notNull(),
|
|
223
|
+
authTag: bytea("auth_tag").notNull(),
|
|
224
|
+
wrappedDek: bytea("wrapped_dek").notNull(),
|
|
225
|
+
kekProvider: text("kek_provider").notNull(),
|
|
226
|
+
kekId: text("kek_id").notNull(),
|
|
227
|
+
kekVersion: text("kek_version").notNull(),
|
|
228
|
+
createdAt: createdAt(),
|
|
229
|
+
createdBy: text("created_by").notNull()
|
|
230
|
+
}, (table) => [
|
|
231
|
+
unique("secret_versions_secret_id_version_key").on(table.secretId, table.version),
|
|
232
|
+
check("secret_versions_version_check", sql`${table.version} > 0`),
|
|
233
|
+
check("secret_versions_iv_check", sql`octet_length(${table.iv}) = 12`),
|
|
234
|
+
check("secret_versions_auth_tag_check", sql`octet_length(${table.authTag}) = 16`),
|
|
235
|
+
foreignKey({
|
|
236
|
+
name: "secret_versions_secret_id_fkey",
|
|
237
|
+
columns: [table.secretId],
|
|
238
|
+
foreignColumns: [secrets.id]
|
|
239
|
+
}).onDelete("restrict"),
|
|
240
|
+
index("secret_versions_secret_idx").on(table.secretId, table.version.desc())
|
|
241
|
+
]);
|
|
242
|
+
/** The role catalogue, as SQL: every role, and those an environment may be granted. */
|
|
243
|
+
const ROLES = sql.raw(ROLE_NAMES.map((role) => `'${role}'`).join(", "));
|
|
244
|
+
const ENVIRONMENT_ROLES = sql.raw(ROLE_NAMES.filter(assignableToEnvironment).map((role) => `'${role}'`).join(", "));
|
|
245
|
+
/**
|
|
246
|
+
* The vault's member directory: everyone it has admitted, written by the
|
|
247
|
+
* vault alone, read by both. A principal with no row is no member.
|
|
248
|
+
* Principals are the strings coffre uses at its edges: `user:<email>`,
|
|
249
|
+
* `token:<id>`, `sync:<id>`. Times are milliseconds since the epoch, each
|
|
250
|
+
* the time of the vault's log entry that set it, so the log replays to
|
|
251
|
+
* these rows exactly.
|
|
252
|
+
*/
|
|
253
|
+
const vaultMembers = pgTable("vault_members", {
|
|
254
|
+
principal: text().primaryKey(),
|
|
255
|
+
status: text().notNull(),
|
|
256
|
+
owner: boolean().notNull().default(false),
|
|
257
|
+
generation: integer().notNull().default(0),
|
|
258
|
+
createdAt: bigint("created_at", { mode: "number" }).notNull(),
|
|
259
|
+
createdBy: text("created_by").notNull(),
|
|
260
|
+
statusChangedAt: bigint("status_changed_at", { mode: "number" }).notNull(),
|
|
261
|
+
statusChangedBy: text("status_changed_by").notNull(),
|
|
262
|
+
accessSeq: bigint("access_seq", { mode: "bigint" }).notNull(),
|
|
263
|
+
mac: bytea().notNull()
|
|
264
|
+
}, (table) => [
|
|
265
|
+
check("vault_members_mac_check", sql`octet_length(${table.mac}) = 32`),
|
|
266
|
+
check("vault_members_principal_check", sql`${table.principal} ~ '^(user|token|sync):[^[:space:]:][^[:space:]]*$'`),
|
|
267
|
+
check("vault_members_user_lowercase", sql`${table.principal} NOT LIKE 'user:%' OR ${table.principal} = lower(${table.principal})`),
|
|
268
|
+
check("vault_members_status_check", sql`${table.status} IN ('active', 'removed')`),
|
|
269
|
+
check("vault_members_owner_check", sql`NOT ${table.owner} OR (${table.status} = 'active' AND ${table.principal} LIKE 'user:%')`),
|
|
270
|
+
check("vault_members_generation_check", sql`${table.generation} >= 0`)
|
|
271
|
+
]);
|
|
272
|
+
/**
|
|
273
|
+
* One role per member per place: a project, or one of its environments,
|
|
274
|
+
* never both. A revoked grant is deleted; an expired one stays until its
|
|
275
|
+
* place is granted again, so the members page can say it lapsed.
|
|
276
|
+
*/
|
|
277
|
+
const vaultGrants = pgTable("vault_grants", {
|
|
278
|
+
principal: text().notNull(),
|
|
279
|
+
projectId: uuid("project_id"),
|
|
280
|
+
environmentId: uuid("environment_id"),
|
|
281
|
+
role: text().notNull(),
|
|
282
|
+
expiresAt: bigint("expires_at", { mode: "number" }),
|
|
283
|
+
grantedAt: bigint("granted_at", { mode: "number" }).notNull(),
|
|
284
|
+
grantedBy: text("granted_by").notNull()
|
|
285
|
+
}, (table) => [
|
|
286
|
+
check("vault_grants_one_place", sql`(${table.projectId} IS NULL) <> (${table.environmentId} IS NULL)`),
|
|
287
|
+
check("vault_grants_role_check", sql`${table.role} IN (${ROLES})`),
|
|
288
|
+
check("vault_grants_environment_role_check", sql`${table.environmentId} IS NULL OR ${table.role} IN (${ENVIRONMENT_ROLES})`),
|
|
289
|
+
unique("vault_grants_on_project").on(table.principal, table.projectId),
|
|
290
|
+
unique("vault_grants_on_environment").on(table.principal, table.environmentId),
|
|
291
|
+
foreignKey({
|
|
292
|
+
name: "vault_grants_principal_fkey",
|
|
293
|
+
columns: [table.principal],
|
|
294
|
+
foreignColumns: [vaultMembers.principal]
|
|
295
|
+
}).onDelete("restrict"),
|
|
296
|
+
foreignKey({
|
|
297
|
+
name: "vault_grants_project_id_fkey",
|
|
298
|
+
columns: [table.projectId],
|
|
299
|
+
foreignColumns: [projects.id]
|
|
300
|
+
}).onDelete("restrict"),
|
|
301
|
+
foreignKey({
|
|
302
|
+
name: "vault_grants_environment_id_fkey",
|
|
303
|
+
columns: [table.environmentId],
|
|
304
|
+
foreignColumns: [environments.id]
|
|
305
|
+
}).onDelete("restrict")
|
|
306
|
+
]);
|
|
307
|
+
const auditLog = pgTable("audit_log", {
|
|
308
|
+
seq: bigint({ mode: "bigint" }).primaryKey(),
|
|
309
|
+
author: text().notNull(),
|
|
310
|
+
keyId: text("key_id").notNull(),
|
|
311
|
+
occurredAt: bigint("occurred_at", { mode: "number" }).notNull(),
|
|
312
|
+
actor: text().notNull(),
|
|
313
|
+
action: text().notNull(),
|
|
314
|
+
decision: text().notNull(),
|
|
315
|
+
code: text(),
|
|
316
|
+
subjectPrincipal: text("subject_principal"),
|
|
317
|
+
projectId: uuid("project_id"),
|
|
318
|
+
environmentId: uuid("environment_id"),
|
|
319
|
+
secretId: uuid("secret_id"),
|
|
320
|
+
secretVersionId: uuid("secret_version_id"),
|
|
321
|
+
operationId: uuid("operation_id"),
|
|
322
|
+
requestId: text("request_id"),
|
|
323
|
+
sourceIp: text("source_ip"),
|
|
324
|
+
relatedSeq: bigint("related_seq", { mode: "bigint" }),
|
|
325
|
+
metadata: text().notNull().default("{}"),
|
|
326
|
+
prevHash: bytea("prev_hash").notNull(),
|
|
327
|
+
mac: bytea().notNull(),
|
|
328
|
+
hash: bytea().notNull()
|
|
329
|
+
}, (table) => [
|
|
330
|
+
check("audit_log_author_check", sql`${table.author} IN ('app', 'vault')`),
|
|
331
|
+
check("audit_log_actor_check", sql`${table.actor} ~ '^(user|token|sync|system):.+$'`),
|
|
332
|
+
check("audit_log_decision_check", sql`${table.decision} IN ('allow', 'deny')`),
|
|
333
|
+
check("audit_log_metadata_check", sql`${table.metadata}::jsonb IS NOT NULL`),
|
|
334
|
+
check("audit_log_prev_hash_check", sql`octet_length(${table.prevHash}) = 32`),
|
|
335
|
+
check("audit_log_mac_check", sql`octet_length(${table.mac}) = 32`),
|
|
336
|
+
check("audit_log_hash_check", sql`octet_length(${table.hash}) = 32`),
|
|
337
|
+
foreignKey({
|
|
338
|
+
name: "audit_log_project_id_fkey",
|
|
339
|
+
columns: [table.projectId],
|
|
340
|
+
foreignColumns: [projects.id]
|
|
341
|
+
}).onDelete("restrict"),
|
|
342
|
+
foreignKey({
|
|
343
|
+
name: "audit_log_environment_id_fkey",
|
|
344
|
+
columns: [table.environmentId],
|
|
345
|
+
foreignColumns: [environments.id]
|
|
346
|
+
}).onDelete("restrict"),
|
|
347
|
+
foreignKey({
|
|
348
|
+
name: "audit_log_secret_id_fkey",
|
|
349
|
+
columns: [table.secretId],
|
|
350
|
+
foreignColumns: [secrets.id]
|
|
351
|
+
}).onDelete("restrict"),
|
|
352
|
+
foreignKey({
|
|
353
|
+
name: "audit_log_secret_version_id_fkey",
|
|
354
|
+
columns: [table.secretVersionId],
|
|
355
|
+
foreignColumns: [secretVersions.id]
|
|
356
|
+
}).onDelete("restrict"),
|
|
357
|
+
foreignKey({
|
|
358
|
+
name: "audit_log_related_seq_fkey",
|
|
359
|
+
columns: [table.relatedSeq],
|
|
360
|
+
foreignColumns: [table.seq]
|
|
361
|
+
}).onDelete("restrict"),
|
|
362
|
+
index("audit_log_project_idx").on(table.projectId, table.seq),
|
|
363
|
+
index("audit_log_environment_idx").on(table.environmentId, table.seq),
|
|
364
|
+
index("audit_log_secret_idx").on(table.secretId, table.seq),
|
|
365
|
+
index("audit_log_actor_idx").on(table.actor, table.seq),
|
|
366
|
+
index("audit_log_operation_idx").on(table.operationId, table.seq),
|
|
367
|
+
index("audit_log_action_idx").on(table.author, table.action, table.seq),
|
|
368
|
+
index("audit_log_releases_idx").on(table.author, table.actor, table.action, table.decision, table.occurredAt),
|
|
369
|
+
index("audit_log_subject_idx").on(table.author, table.subjectPrincipal, table.seq)
|
|
370
|
+
]);
|
|
371
|
+
const auditChainHead = pgTable("audit_chain_head", {
|
|
372
|
+
onlyRow: boolean("only_row").primaryKey().default(true),
|
|
373
|
+
nextSeq: bigint("next_seq", { mode: "bigint" }).notNull().default(sql`0`),
|
|
374
|
+
headHash: bytea("head_hash").notNull()
|
|
375
|
+
}, (table) => [
|
|
376
|
+
check("audit_chain_head_only_row_check", sql`${table.onlyRow}`),
|
|
377
|
+
check("audit_chain_head_next_seq_check", sql`${table.nextSeq} >= 0`),
|
|
378
|
+
check("audit_chain_head_head_hash_check", sql`octet_length(${table.headHash}) = 32`)
|
|
379
|
+
]);
|
|
380
|
+
/**
|
|
381
|
+
* An account at a sign-in provider, bound to one principal.
|
|
382
|
+
*
|
|
383
|
+
* Looked up by (provider, subject), never by email. An email address is
|
|
384
|
+
* recycled when someone leaves; the provider's subject is not, so binding to
|
|
385
|
+
* it is what stops a new holder of an old address from inheriting its access.
|
|
386
|
+
* The email is kept for display only.
|
|
387
|
+
*/
|
|
388
|
+
const identities = pgTable("identities", {
|
|
389
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
390
|
+
provider: text().notNull(),
|
|
391
|
+
authMac: bytea("auth_mac").notNull(),
|
|
392
|
+
subject: text().notNull(),
|
|
393
|
+
issuerHash: text("issuer_hash").notNull(),
|
|
394
|
+
generation: integer().notNull(),
|
|
395
|
+
principal: text().notNull(),
|
|
396
|
+
email: text(),
|
|
397
|
+
createdAt: createdAt(),
|
|
398
|
+
createdBy: text("created_by").notNull(),
|
|
399
|
+
lastSignInAt: timestamp("last_sign_in_at", { withTimezone: true }),
|
|
400
|
+
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
|
401
|
+
revokedBy: text("revoked_by"),
|
|
402
|
+
activeSubject: text("active_subject").generatedAlwaysAs(ACTIVE_SUBJECT)
|
|
403
|
+
}, (table) => [
|
|
404
|
+
check("identities_principal_check", sql`${table.principal} LIKE 'user:%'`),
|
|
405
|
+
check("identities_provider_check", sql`${table.provider} ~ '^[a-z0-9][a-z0-9-]{0,31}$'`),
|
|
406
|
+
check("identities_auth_mac_check", sql`octet_length(${table.authMac}) = 32`),
|
|
407
|
+
unique("identities_member_generation_key").on(table.id, table.principal, table.generation),
|
|
408
|
+
foreignKey({
|
|
409
|
+
name: "identities_principal_fkey",
|
|
410
|
+
columns: [table.principal],
|
|
411
|
+
foreignColumns: [vaultMembers.principal]
|
|
412
|
+
}).onDelete("restrict"),
|
|
413
|
+
uniqueIndex("identities_active_subject").on(table.provider, table.issuerHash, table.activeSubject),
|
|
414
|
+
index("identities_principal_idx").on(table.principal, table.generation)
|
|
415
|
+
]);
|
|
416
|
+
/**
|
|
417
|
+
* Bearer credentials coffre issues itself: browser sessions, CLI sessions and
|
|
418
|
+
* service tokens.
|
|
419
|
+
*
|
|
420
|
+
* One table, so that revoking everything a principal holds is one statement.
|
|
421
|
+
* Only a SHA-256 of each token is stored. Tokens carry 256 bits of entropy, so
|
|
422
|
+
* a fast hash is enough; a database leak yields nothing that authenticates.
|
|
423
|
+
*/
|
|
424
|
+
const credentials = pgTable("credentials", {
|
|
425
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
426
|
+
kind: text().notNull(),
|
|
427
|
+
authMac: bytea("auth_mac").notNull(),
|
|
428
|
+
tokenHash: bytea("token_hash").notNull(),
|
|
429
|
+
tokenHint: text("token_hint").notNull(),
|
|
430
|
+
generation: integer().notNull(),
|
|
431
|
+
principal: text().notNull(),
|
|
432
|
+
identityId: uuid("identity_id"),
|
|
433
|
+
label: text(),
|
|
434
|
+
createdAt: createdAt(),
|
|
435
|
+
createdBy: text("created_by").notNull(),
|
|
436
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
437
|
+
lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
|
|
438
|
+
lastUsedIp: text("last_used_ip"),
|
|
439
|
+
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
|
440
|
+
revokedBy: text("revoked_by")
|
|
441
|
+
}, (table) => [
|
|
442
|
+
unique("credentials_token_hash_key").on(table.tokenHash),
|
|
443
|
+
check("credentials_kind_check", sql`${table.kind} IN ('browser', 'cli', 'service')`),
|
|
444
|
+
check("credentials_principal_check", sql`${table.principal} LIKE 'user:%' OR ${table.principal} LIKE 'token:%'`),
|
|
445
|
+
check("credentials_kind_matches_principal", sql`(${table.kind} = 'service') = (${table.principal} LIKE 'token:%')`),
|
|
446
|
+
check("credentials_token_hash_check", sql`octet_length(${table.tokenHash}) = 32`),
|
|
447
|
+
check("credentials_auth_mac_check", sql`octet_length(${table.authMac}) = 32`),
|
|
448
|
+
foreignKey({
|
|
449
|
+
name: "credentials_principal_fkey",
|
|
450
|
+
columns: [table.principal],
|
|
451
|
+
foreignColumns: [vaultMembers.principal]
|
|
452
|
+
}).onDelete("restrict"),
|
|
453
|
+
foreignKey({
|
|
454
|
+
name: "credentials_identity_id_fkey",
|
|
455
|
+
columns: [
|
|
456
|
+
table.identityId,
|
|
457
|
+
table.principal,
|
|
458
|
+
table.generation
|
|
459
|
+
],
|
|
460
|
+
foreignColumns: [
|
|
461
|
+
identities.id,
|
|
462
|
+
identities.principal,
|
|
463
|
+
identities.generation
|
|
464
|
+
]
|
|
465
|
+
}).onDelete("restrict"),
|
|
466
|
+
index("credentials_principal_idx").on(table.principal, table.generation)
|
|
467
|
+
]);
|
|
468
|
+
/**
|
|
469
|
+
* A CLI asking to be signed in from a browser (RFC 8628, device flow).
|
|
470
|
+
*
|
|
471
|
+
* The CLI holds the device code and polls with it; a signed-in person approves
|
|
472
|
+
* the short user code in their browser. It works the same on a laptop and on
|
|
473
|
+
* a server reached over SSH, which a localhost redirect does not.
|
|
474
|
+
*/
|
|
475
|
+
const deviceAuthorizations = pgTable("device_authorizations", {
|
|
476
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
477
|
+
deviceCodeHash: bytea("device_code_hash").notNull(),
|
|
478
|
+
authMac: bytea("auth_mac").notNull(),
|
|
479
|
+
userCode: text("user_code").notNull(),
|
|
480
|
+
clientLabel: text("client_label"),
|
|
481
|
+
clientIp: text("client_ip"),
|
|
482
|
+
createdAt: createdAt(),
|
|
483
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
484
|
+
decidedAt: timestamp("decided_at", { withTimezone: true }),
|
|
485
|
+
decision: text(),
|
|
486
|
+
generation: integer().notNull().default(0),
|
|
487
|
+
principal: text(),
|
|
488
|
+
consumedAt: timestamp("consumed_at", { withTimezone: true })
|
|
489
|
+
}, (table) => [
|
|
490
|
+
unique("device_authorizations_device_code_hash_key").on(table.deviceCodeHash),
|
|
491
|
+
unique("device_authorizations_user_code_key").on(table.userCode),
|
|
492
|
+
check("device_authorizations_auth_mac_check", sql`octet_length(${table.authMac}) = 32`),
|
|
493
|
+
check("device_authorizations_decision_check", sql`${table.decision} IS NULL OR ${table.decision} IN ('approved', 'denied')`),
|
|
494
|
+
check("device_authorizations_approval_names_principal", sql`(${table.decision} IS NULL AND ${table.decidedAt} IS NULL AND ${table.principal} IS NULL AND ${table.generation} = 0 AND ${table.consumedAt} IS NULL)
|
|
495
|
+
OR (${table.decision} IS NOT NULL AND ${table.decidedAt} IS NOT NULL AND (
|
|
496
|
+
(${table.decision} = 'denied' AND ${table.principal} IS NULL AND ${table.generation} = 0 AND ${table.consumedAt} IS NULL)
|
|
497
|
+
OR (${table.decision} = 'approved' AND ${table.principal} IS NOT NULL AND ${table.principal} LIKE 'user:%')
|
|
498
|
+
))`),
|
|
499
|
+
foreignKey({
|
|
500
|
+
name: "device_authorizations_principal_fkey",
|
|
501
|
+
columns: [table.principal],
|
|
502
|
+
foreignColumns: [vaultMembers.principal]
|
|
503
|
+
}).onDelete("restrict")
|
|
504
|
+
]);
|
|
505
|
+
/**
|
|
506
|
+
* An environment kept in step with a third-party service: GitHub Actions
|
|
507
|
+
* secrets, Vercel or Railway variables, Worker secrets.
|
|
508
|
+
*
|
|
509
|
+
* The destination's API token is itself a coffre secret, referenced by id, so
|
|
510
|
+
* it is encrypted, versioned and audited like everything else and never sits
|
|
511
|
+
* in this table.
|
|
512
|
+
*/
|
|
513
|
+
const syncs = pgTable("syncs", {
|
|
514
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
515
|
+
projectId: uuid("project_id").notNull(),
|
|
516
|
+
environmentId: uuid("environment_id").notNull(),
|
|
517
|
+
provider: text().notNull(),
|
|
518
|
+
config: text().notNull(),
|
|
519
|
+
credentialSecretId: uuid("credential_secret_id").notNull(),
|
|
520
|
+
createdAt: createdAt(),
|
|
521
|
+
createdBy: text("created_by").notNull(),
|
|
522
|
+
pausedAt: timestamp("paused_at", { withTimezone: true }),
|
|
523
|
+
archivedAt: timestamp("archived_at", { withTimezone: true }),
|
|
524
|
+
leaseUntil: timestamp("lease_until", { withTimezone: true }),
|
|
525
|
+
lastRunAt: timestamp("last_run_at", { withTimezone: true }),
|
|
526
|
+
lastStatus: text("last_status"),
|
|
527
|
+
lastError: text("last_error")
|
|
528
|
+
}, (table) => [
|
|
529
|
+
check("syncs_config_check", sql`${table.config}::jsonb IS NOT NULL`),
|
|
530
|
+
check("syncs_last_status_check", sql`${table.lastStatus} IS NULL OR ${table.lastStatus} IN ('ok', 'partial', 'failed')`),
|
|
531
|
+
foreignKey({
|
|
532
|
+
name: "syncs_environment_in_project",
|
|
533
|
+
columns: [table.environmentId, table.projectId],
|
|
534
|
+
foreignColumns: [environments.id, environments.projectId]
|
|
535
|
+
}).onDelete("restrict"),
|
|
536
|
+
foreignKey({
|
|
537
|
+
name: "syncs_credential_secret_id_fkey",
|
|
538
|
+
columns: [table.credentialSecretId],
|
|
539
|
+
foreignColumns: [secrets.id]
|
|
540
|
+
}).onDelete("restrict"),
|
|
541
|
+
index("syncs_environment_idx").on(table.environmentId)
|
|
542
|
+
]);
|
|
543
|
+
/**
|
|
544
|
+
* What a sync last pushed, one row per key.
|
|
545
|
+
*
|
|
546
|
+
* Most destinations are write-only, so coffre cannot diff against them. It
|
|
547
|
+
* diffs against this instead: a key is stale when its secret has moved past
|
|
548
|
+
* the version recorded here. It is also the list of keys coffre may delete at
|
|
549
|
+
* the destination; a key it never pushed is never removed.
|
|
550
|
+
*/
|
|
551
|
+
const syncKeys = pgTable("sync_keys", {
|
|
552
|
+
syncId: uuid("sync_id").notNull(),
|
|
553
|
+
key: text().notNull(),
|
|
554
|
+
secretVersionId: uuid("secret_version_id"),
|
|
555
|
+
pushedAt: timestamp("pushed_at", { withTimezone: true }).notNull().defaultNow(),
|
|
556
|
+
removedAt: timestamp("removed_at", { withTimezone: true })
|
|
557
|
+
}, (table) => [
|
|
558
|
+
primaryKey({
|
|
559
|
+
name: "sync_keys_pkey",
|
|
560
|
+
columns: [table.syncId, table.key]
|
|
561
|
+
}),
|
|
562
|
+
foreignKey({
|
|
563
|
+
name: "sync_keys_sync_id_fkey",
|
|
564
|
+
columns: [table.syncId],
|
|
565
|
+
foreignColumns: [syncs.id]
|
|
566
|
+
}).onDelete("restrict"),
|
|
567
|
+
foreignKey({
|
|
568
|
+
name: "sync_keys_secret_version_id_fkey",
|
|
569
|
+
columns: [table.secretVersionId],
|
|
570
|
+
foreignColumns: [secretVersions.id]
|
|
571
|
+
}).onDelete("restrict")
|
|
572
|
+
]);
|
|
573
|
+
const { environmentsRelations, secretsRelations, syncsRelations, syncKeysRelations } = relationsOf({
|
|
574
|
+
projects,
|
|
575
|
+
environments,
|
|
576
|
+
secrets,
|
|
577
|
+
secretVersions,
|
|
578
|
+
syncs,
|
|
579
|
+
syncKeys
|
|
580
|
+
});
|
|
581
|
+
//#endregion
|
|
582
|
+
//#region ../db/dist/index.js
|
|
583
|
+
function createDatabase(pool) {
|
|
584
|
+
return trackCommits(drizzle(pool, { schema: schema_exports }));
|
|
585
|
+
}
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region ../../node_modules/.pnpm/pg-connection-string@2.14.0/node_modules/pg-connection-string/esm/index.mjs
|
|
588
|
+
var import_pg_connection_string = /* @__PURE__ */ __toESM(require_pg_connection_string(), 1);
|
|
589
|
+
import_pg_connection_string.default.parse;
|
|
590
|
+
import_pg_connection_string.default.parse;
|
|
591
|
+
import_pg_connection_string.default.toClientConfig;
|
|
592
|
+
const parseIntoClientConfig = import_pg_connection_string.default.parseIntoClientConfig;
|
|
593
|
+
//#endregion
|
|
594
|
+
//#region ../db/dist/postgres-Bmar8y0q.js
|
|
595
|
+
/** Keep libpq's system-root shortcut from being read as a certificate filename by pg. */
|
|
596
|
+
function postgresConnection(url) {
|
|
597
|
+
let parsed;
|
|
598
|
+
try {
|
|
599
|
+
parsed = new URL(url);
|
|
600
|
+
} catch {
|
|
601
|
+
throw new Error("invalid Postgres URL");
|
|
602
|
+
}
|
|
603
|
+
const parameters = parsed.searchParams;
|
|
604
|
+
if (parameters.getAll("sslrootcert").at(-1) !== "system") return { connectionString: url };
|
|
605
|
+
const mode = parameters.getAll("sslmode").at(-1);
|
|
606
|
+
if (mode !== void 0 && mode !== "verify-full") throw new Error("sslrootcert=system requires sslmode=verify-full");
|
|
607
|
+
parameters.delete("sslrootcert");
|
|
608
|
+
parameters.set("sslmode", "verify-full");
|
|
609
|
+
const config = parseIntoClientConfig(parsed.toString());
|
|
610
|
+
const host = config.host;
|
|
611
|
+
if (!host) throw new Error("sslrootcert=system requires a Postgres hostname");
|
|
612
|
+
config.ssl = {
|
|
613
|
+
...config.ssl,
|
|
614
|
+
rejectUnauthorized: true,
|
|
615
|
+
checkServerIdentity: (_hostname, certificate) => checkServerIdentity(host, certificate)
|
|
616
|
+
};
|
|
617
|
+
return config;
|
|
618
|
+
}
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region ../db/dist/connect.js
|
|
621
|
+
/**
|
|
622
|
+
* Open a database from its URL, on a Node server:
|
|
623
|
+
*
|
|
624
|
+
* postgres://… or postgresql://… node-postgres
|
|
625
|
+
* file:… or libsql:… @libsql/client (SQLite)
|
|
626
|
+
*
|
|
627
|
+
* The SQLite driver loads only when asked for. The Worker does not
|
|
628
|
+
* come through here; it builds its Postgres database from the Hyperdrive
|
|
629
|
+
* pool with createDatabase.
|
|
630
|
+
*/
|
|
631
|
+
async function openDatabase(url) {
|
|
632
|
+
switch (engineOfUrl(url)) {
|
|
633
|
+
case "postgres": {
|
|
634
|
+
const pool = new esm_default$1.Pool(postgresConnection(url));
|
|
635
|
+
return {
|
|
636
|
+
engine: "postgres",
|
|
637
|
+
db: createDatabase(pool),
|
|
638
|
+
close: () => pool.end()
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
case "sqlite": {
|
|
642
|
+
const [{ createClient }, { drizzle }, schema] = await Promise.all([
|
|
643
|
+
import("@libsql/client"),
|
|
644
|
+
import("drizzle-orm/libsql"),
|
|
645
|
+
import("./schema-sqlite-Byp7B3yI.js")
|
|
646
|
+
]);
|
|
647
|
+
const client = createClient({
|
|
648
|
+
url,
|
|
649
|
+
timeout: 5e3,
|
|
650
|
+
intMode: "bigint"
|
|
651
|
+
});
|
|
652
|
+
if (url.startsWith("file:")) await client.execute("PRAGMA journal_mode = WAL");
|
|
653
|
+
const db = drizzle({
|
|
654
|
+
client: oneTransactionAtATime(client, writeQueueFor(url)),
|
|
655
|
+
schema
|
|
656
|
+
});
|
|
657
|
+
return {
|
|
658
|
+
engine: "sqlite",
|
|
659
|
+
db: asPostgresDatabase(db),
|
|
660
|
+
close: async () => client.close()
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function engineOfUrl(url) {
|
|
666
|
+
const scheme = url.slice(0, url.indexOf(":"));
|
|
667
|
+
if (scheme === "postgres" || scheme === "postgresql") return "postgres";
|
|
668
|
+
if (scheme === "mysql") throw new Error("coffre supports Postgres; MySQL support was removed");
|
|
669
|
+
if (scheme === "file" || scheme === "libsql") return "sqlite";
|
|
670
|
+
throw new Error(`unsupported database URL scheme: ${scheme}:`);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* SQLite lets one connection write at a time and fails the others at once
|
|
674
|
+
* with SQLITE_BUSY rather than queueing them; 24 concurrent audit appends were
|
|
675
|
+
* enough to see it. So transactions queue here instead, in the order they
|
|
676
|
+
* asked, one open at a time per database file, however many clients or
|
|
677
|
+
* connections reach it. Each begins IMMEDIATE (libsql's default), taking the
|
|
678
|
+
* write lock with its first statement, which is what makes a read inside it
|
|
679
|
+
* as good as a locked one (see forUpdate).
|
|
680
|
+
*/
|
|
681
|
+
var WriteQueue = class {
|
|
682
|
+
#tail = Promise.resolve();
|
|
683
|
+
/** Wait for our turn; the returned function ends it. */
|
|
684
|
+
acquire() {
|
|
685
|
+
let release;
|
|
686
|
+
const turn = new Promise((done) => release = done);
|
|
687
|
+
const ready = this.#tail.then(() => release);
|
|
688
|
+
this.#tail = this.#tail.then(() => turn);
|
|
689
|
+
return ready;
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
const queues = /* @__PURE__ */ new Map();
|
|
693
|
+
function writeQueueFor(url) {
|
|
694
|
+
const key = url.startsWith("file:") ? resolve(url.replace(/^file:(\/\/)?/, "").split("?")[0]) : url;
|
|
695
|
+
let queue = queues.get(key);
|
|
696
|
+
if (queue === void 0) queues.set(key, queue = new WriteQueue());
|
|
697
|
+
return queue;
|
|
698
|
+
}
|
|
699
|
+
function oneTransactionAtATime(client, queue) {
|
|
700
|
+
return new Proxy(client, { get(target, key) {
|
|
701
|
+
if (key === "execute") return async (...args) => numbers(await target.execute(...args));
|
|
702
|
+
if (key === "transaction") return async (mode) => {
|
|
703
|
+
const release = await queue.acquire();
|
|
704
|
+
try {
|
|
705
|
+
return settling(await target.transaction(mode), release);
|
|
706
|
+
} catch (error) {
|
|
707
|
+
release();
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
if (key === "batch" || key === "migrate" || key === "executeMultiple") return async (...args) => {
|
|
712
|
+
const release = await queue.acquire();
|
|
713
|
+
try {
|
|
714
|
+
return (await target[key](...args))?.map(numbers);
|
|
715
|
+
} finally {
|
|
716
|
+
release();
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
const value = Reflect.get(target, key, target);
|
|
720
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
721
|
+
} });
|
|
722
|
+
}
|
|
723
|
+
/** Ends the transaction's turn when it commits, rolls back or closes. */
|
|
724
|
+
function settling(tx, release) {
|
|
725
|
+
let settled = false;
|
|
726
|
+
const settle = () => {
|
|
727
|
+
if (settled) return;
|
|
728
|
+
settled = true;
|
|
729
|
+
release();
|
|
730
|
+
};
|
|
731
|
+
const { commit, rollback, close, execute, batch } = tx;
|
|
732
|
+
tx.execute = async (...args) => numbers(await execute.apply(tx, args));
|
|
733
|
+
tx.batch = async (...args) => (await batch.apply(tx, args)).map(numbers);
|
|
734
|
+
tx.commit = async () => {
|
|
735
|
+
try {
|
|
736
|
+
return await commit.call(tx);
|
|
737
|
+
} finally {
|
|
738
|
+
settle();
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
tx.rollback = async () => {
|
|
742
|
+
try {
|
|
743
|
+
return await rollback.call(tx);
|
|
744
|
+
} finally {
|
|
745
|
+
settle();
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
tx.close = () => {
|
|
749
|
+
try {
|
|
750
|
+
close.call(tx);
|
|
751
|
+
} finally {
|
|
752
|
+
settle();
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
return tx;
|
|
756
|
+
}
|
|
757
|
+
/** Keep large counters exact; Drizzle's booleans and dates still need safe numbers. */
|
|
758
|
+
function numbers(result) {
|
|
759
|
+
result.rows = result.rows.map((row) => {
|
|
760
|
+
const properties = Object.getOwnPropertyDescriptors(row);
|
|
761
|
+
for (const property of Object.values(properties)) {
|
|
762
|
+
const value = property.value;
|
|
763
|
+
if (typeof value === "bigint" && value >= BigInt(Number.MIN_SAFE_INTEGER) && value <= BigInt(Number.MAX_SAFE_INTEGER)) property.value = Number(value);
|
|
764
|
+
}
|
|
765
|
+
return Object.create(Object.getPrototypeOf(row), properties);
|
|
766
|
+
});
|
|
767
|
+
return result;
|
|
768
|
+
}
|
|
769
|
+
//#endregion
|
|
770
|
+
export { openDatabase as n, postgresConnection as r, engineOfUrl as t };
|