@sdods/db 0.9.0 → 0.11.1
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/.tsbuildinfo +1 -1
- package/dist/ingest/finalize.d.ts +18 -1
- package/dist/ingest/finalize.js +67 -0
- package/dist/ingest/index.js +22 -2
- package/dist/ingest/types.d.ts +11 -1
- package/dist/migrations/0009_user_profile.d.ts +9 -0
- package/dist/migrations/0009_user_profile.js +11 -0
- package/dist/migrations/index.js +2 -0
- package/dist/repos/auth.d.ts +26 -2
- package/dist/repos/auth.js +44 -3
- package/dist/repos/runs.js +2 -0
- package/dist/schema.d.ts +1 -0
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunTotals } from '@sdods/contracts/types';
|
|
1
|
+
import type { GateResult, RunTotals } from '@sdods/contracts/types';
|
|
2
2
|
import type { SdodsDb } from '../create-db.js';
|
|
3
3
|
export interface LocatorCounter {
|
|
4
4
|
fails: number;
|
|
@@ -25,4 +25,21 @@ export declare function finalizeRun(input: FinalizeInput): Promise<{
|
|
|
25
25
|
totals: RunTotals;
|
|
26
26
|
status: string;
|
|
27
27
|
}>;
|
|
28
|
+
/** The gate verdict the CLI wrote into a run directory (`gates.json`), when there is one. */
|
|
29
|
+
export declare function readGateVerdict(runDir: string): GateResult | undefined;
|
|
30
|
+
/** `value` when it has the shape of a gate verdict. */
|
|
31
|
+
export declare function gateResultOf(value: unknown): GateResult | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Records a process gate verdict on the run row: under `totals_json.gates`, and — when a gate failed
|
|
34
|
+
* — as the run's status, exit code and error. A run whose scenarios all passed and whose gates did
|
|
35
|
+
* not is a failed run; leaving it `passed` is exactly the claim the gate exists to stop. Used by
|
|
36
|
+
* ingest (CLI runs, merged shards) and by the server's run manager, so both record it the same way.
|
|
37
|
+
* Returns the run's status afterwards.
|
|
38
|
+
*/
|
|
39
|
+
export declare function recordGateVerdict(adb: SdodsDb, input: {
|
|
40
|
+
runId: string;
|
|
41
|
+
gates: GateResult;
|
|
42
|
+
cancelled?: boolean;
|
|
43
|
+
exitCode?: number | null;
|
|
44
|
+
}): Promise<string | undefined>;
|
|
28
45
|
//# sourceMappingURL=finalize.d.ts.map
|
package/dist/ingest/finalize.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { runFiles } from '@sdods/contracts/names';
|
|
1
4
|
import { enc, nowIso, readBool, readJson, readTs } from '../col.js';
|
|
2
5
|
import { bumpLocatorStats, recomputeFlakyStats } from '../repos/improvement.js';
|
|
3
6
|
import { emptyTotals } from './types.js';
|
|
@@ -129,4 +132,68 @@ export async function finalizeRun(input) {
|
|
|
129
132
|
void readBool;
|
|
130
133
|
return { totals, status };
|
|
131
134
|
}
|
|
135
|
+
/** The gate verdict the CLI wrote into a run directory (`gates.json`), when there is one. */
|
|
136
|
+
export function readGateVerdict(runDir) {
|
|
137
|
+
const file = join(runDir, runFiles.gates);
|
|
138
|
+
if (!existsSync(file))
|
|
139
|
+
return undefined;
|
|
140
|
+
try {
|
|
141
|
+
return gateResultOf(JSON.parse(readFileSync(file, 'utf8')));
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** `value` when it has the shape of a gate verdict. */
|
|
148
|
+
export function gateResultOf(value) {
|
|
149
|
+
const v = value;
|
|
150
|
+
return v && typeof v === 'object' && typeof v.process === 'string' && Array.isArray(v.rows)
|
|
151
|
+
? v
|
|
152
|
+
: undefined;
|
|
153
|
+
}
|
|
154
|
+
const GATE_ERROR_PREFIX = 'GATE_FAILED:';
|
|
155
|
+
/**
|
|
156
|
+
* Records a process gate verdict on the run row: under `totals_json.gates`, and — when a gate failed
|
|
157
|
+
* — as the run's status, exit code and error. A run whose scenarios all passed and whose gates did
|
|
158
|
+
* not is a failed run; leaving it `passed` is exactly the claim the gate exists to stop. Used by
|
|
159
|
+
* ingest (CLI runs, merged shards) and by the server's run manager, so both record it the same way.
|
|
160
|
+
* Returns the run's status afterwards.
|
|
161
|
+
*/
|
|
162
|
+
export async function recordGateVerdict(adb, input) {
|
|
163
|
+
const { db } = adb;
|
|
164
|
+
const { runId, gates } = input;
|
|
165
|
+
const row = await db
|
|
166
|
+
.selectFrom('runs')
|
|
167
|
+
.select(['status', 'totals_json', 'exit_code', 'error_text'])
|
|
168
|
+
.where('id', '=', runId)
|
|
169
|
+
.executeTakeFirst();
|
|
170
|
+
if (!row)
|
|
171
|
+
return undefined;
|
|
172
|
+
const prev = readJson(row.totals_json) ?? {};
|
|
173
|
+
const cancelled = input.cancelled || row.status === 'cancelled';
|
|
174
|
+
const failed = gates.rows.filter((r) => !r.passed);
|
|
175
|
+
const breached = !gates.passed && !cancelled;
|
|
176
|
+
const status = breached ? 'failed' : row.status;
|
|
177
|
+
const staleGateError = !breached && row.error_text?.startsWith(GATE_ERROR_PREFIX);
|
|
178
|
+
await db
|
|
179
|
+
.updateTable('runs')
|
|
180
|
+
.set({
|
|
181
|
+
totals_json: enc.json({ ...prev, gates }),
|
|
182
|
+
status,
|
|
183
|
+
...(breached
|
|
184
|
+
? {
|
|
185
|
+
exit_code: input.exitCode || row.exit_code || 1,
|
|
186
|
+
error_text: `${GATE_ERROR_PREFIX} process "${gates.process}" did not meet ${failed
|
|
187
|
+
.map((r) => `${r.gate} (${r.actual}, needs ${r.threshold})`)
|
|
188
|
+
.join('; ')}`,
|
|
189
|
+
}
|
|
190
|
+
: staleGateError
|
|
191
|
+
? { error_text: null }
|
|
192
|
+
: {}),
|
|
193
|
+
updated_at: nowIso(),
|
|
194
|
+
})
|
|
195
|
+
.where('id', '=', runId)
|
|
196
|
+
.execute();
|
|
197
|
+
return status;
|
|
198
|
+
}
|
|
132
199
|
//# sourceMappingURL=finalize.js.map
|
package/dist/ingest/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { readJson } from '../col.js';
|
|
|
5
5
|
import { ensureProject, getProjectBySlug } from '../repos/projects.js';
|
|
6
6
|
import { deleteRunChildren, upsertRun } from '../repos/runs.js';
|
|
7
7
|
import { IngestSession } from './cucumber-ingest.js';
|
|
8
|
-
import { finalizeRun } from './finalize.js';
|
|
8
|
+
import { finalizeRun, gateResultOf, readGateVerdict, recordGateVerdict, } from './finalize.js';
|
|
9
9
|
import { fileSha256, parseNdjson } from './parse-ndjson.js';
|
|
10
10
|
import { ingestRunnerJson } from './runner-json-ingest.js';
|
|
11
11
|
export * from './types.js';
|
|
@@ -165,7 +165,7 @@ export async function ingestRun(adb, opts) {
|
|
|
165
165
|
runFinishedAt = r.finishedAt;
|
|
166
166
|
filesIngested.push(key);
|
|
167
167
|
}
|
|
168
|
-
const
|
|
168
|
+
const finalized = await finalizeRun({
|
|
169
169
|
adb,
|
|
170
170
|
runId: opts.runId,
|
|
171
171
|
projectId: project.id,
|
|
@@ -175,11 +175,31 @@ export async function ingestRun(adb, opts) {
|
|
|
175
175
|
ingestedFiles: filesIngested,
|
|
176
176
|
exitCode: manifest?.exitCode ?? null,
|
|
177
177
|
});
|
|
178
|
+
const { totals } = finalized;
|
|
179
|
+
let { status } = finalized;
|
|
180
|
+
// The same verdict a server-started run gets: CLI runs and merged shards look alike in the DB.
|
|
181
|
+
// A re-ingest with nothing to judge keeps the verdict already recorded, rather than resetting the
|
|
182
|
+
// run to the status its scenarios alone would give it.
|
|
183
|
+
const gates = opts.gates === null
|
|
184
|
+
? undefined
|
|
185
|
+
: typeof opts.gates === 'object'
|
|
186
|
+
? opts.gates
|
|
187
|
+
: (readGateVerdict(runDir) ??
|
|
188
|
+
(typeof opts.gates === 'function' ? opts.gates(totals) : undefined) ??
|
|
189
|
+
gateResultOf(prevTotals.gates));
|
|
190
|
+
if (gates)
|
|
191
|
+
status =
|
|
192
|
+
(await recordGateVerdict(adb, {
|
|
193
|
+
runId: opts.runId,
|
|
194
|
+
gates,
|
|
195
|
+
exitCode: manifest?.exitCode ?? null,
|
|
196
|
+
})) ?? status;
|
|
178
197
|
return {
|
|
179
198
|
runId: opts.runId,
|
|
180
199
|
projectSlug,
|
|
181
200
|
totals,
|
|
182
201
|
status,
|
|
202
|
+
gates,
|
|
183
203
|
scenarios: counts.scenarios,
|
|
184
204
|
attempts: counts.attempts,
|
|
185
205
|
steps: counts.steps,
|
package/dist/ingest/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunManifest, RunTotals, SuiteStatus } from '@sdods/contracts/types';
|
|
1
|
+
import type { GateResult, RunManifest, RunTotals, SuiteStatus } from '@sdods/contracts/types';
|
|
2
2
|
import type { SdodsDb } from '../create-db.js';
|
|
3
3
|
export type { RunManifest, RunTotals, SuiteStatus };
|
|
4
4
|
export interface IngestRunOptions {
|
|
@@ -14,12 +14,22 @@ export interface IngestRunOptions {
|
|
|
14
14
|
/** git/ci/trigger overrides when there is no manifest */
|
|
15
15
|
trigger?: string;
|
|
16
16
|
env?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The process gate verdict to record on the run. The `gates.json` the CLI wrote into the run
|
|
19
|
+
* directory (`sdods run --process`, `sdods report merge --process`) wins; without one, a function
|
|
20
|
+
* here judges the gates over the totals ingest computed (the database cannot evaluate them
|
|
21
|
+
* itself: the judge lives in core, which depends on this package); failing that, the verdict
|
|
22
|
+
* already recorded on the run is kept. A `GateResult` records that verdict; `null` records none.
|
|
23
|
+
*/
|
|
24
|
+
gates?: GateResult | null | ((totals: RunTotals) => GateResult | undefined);
|
|
17
25
|
}
|
|
18
26
|
export interface IngestResult {
|
|
19
27
|
runId: string;
|
|
20
28
|
projectSlug: string;
|
|
21
29
|
totals: RunTotals;
|
|
22
30
|
status: string;
|
|
31
|
+
/** The process gate verdict recorded on the run, when there was one. */
|
|
32
|
+
gates?: GateResult;
|
|
23
33
|
scenarios: number;
|
|
24
34
|
attempts: number;
|
|
25
35
|
steps: number;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { Col } from '../col.js';
|
|
3
|
+
/**
|
|
4
|
+
* Self-service profile: a free-text display name next to the immutable username. Nullable, so
|
|
5
|
+
* existing users keep showing their username until they set one.
|
|
6
|
+
*/
|
|
7
|
+
export declare function up(db: Kysely<any>, c: Col): Promise<void>;
|
|
8
|
+
export declare function down(db: Kysely<any>, _c: Col): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=0009_user_profile.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-service profile: a free-text display name next to the immutable username. Nullable, so
|
|
3
|
+
* existing users keep showing their username until they set one.
|
|
4
|
+
*/
|
|
5
|
+
export async function up(db, c) {
|
|
6
|
+
await db.schema.alterTable('users').addColumn('display_name', c.text).execute();
|
|
7
|
+
}
|
|
8
|
+
export async function down(db, _c) {
|
|
9
|
+
await db.schema.alterTable('users').dropColumn('display_name').execute();
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=0009_user_profile.js.map
|
package/dist/migrations/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as m0005 from './0005_integrations_agents_audit.js';
|
|
|
6
6
|
import * as m0006 from './0006_schedules.js';
|
|
7
7
|
import * as m0007 from './0007_hierarchy.js';
|
|
8
8
|
import * as m0008 from './0008_runner_naming.js';
|
|
9
|
+
import * as m0009 from './0009_user_profile.js';
|
|
9
10
|
/**
|
|
10
11
|
* Static registry (bundler-friendly, no runtime file discovery). Order matters: Kysely sorts by key.
|
|
11
12
|
*/
|
|
@@ -18,5 +19,6 @@ export const MIGRATIONS = {
|
|
|
18
19
|
'0006_schedules': m0006,
|
|
19
20
|
'0007_hierarchy': m0007,
|
|
20
21
|
'0008_runner_naming': m0008,
|
|
22
|
+
'0009_user_profile': m0009,
|
|
21
23
|
};
|
|
22
24
|
//# sourceMappingURL=index.js.map
|
package/dist/repos/auth.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare function getUserByUsername(db: Kysely<Database>, username: string
|
|
|
16
16
|
id: string;
|
|
17
17
|
username: string;
|
|
18
18
|
email: string | null;
|
|
19
|
+
displayName: string | null;
|
|
19
20
|
passwordHash: string;
|
|
20
21
|
role: Role;
|
|
21
22
|
active: boolean;
|
|
@@ -27,6 +28,7 @@ export declare function getUserById(db: Kysely<Database>, id: string): Promise<{
|
|
|
27
28
|
id: string;
|
|
28
29
|
username: string;
|
|
29
30
|
email: string | null;
|
|
31
|
+
displayName: string | null;
|
|
30
32
|
passwordHash: string;
|
|
31
33
|
role: Role;
|
|
32
34
|
active: boolean;
|
|
@@ -38,6 +40,7 @@ export declare function listUsers(db: Kysely<Database>): Promise<{
|
|
|
38
40
|
id: string;
|
|
39
41
|
username: string;
|
|
40
42
|
email: string | null;
|
|
43
|
+
displayName: string | null;
|
|
41
44
|
passwordHash: string;
|
|
42
45
|
role: Role;
|
|
43
46
|
active: boolean;
|
|
@@ -50,6 +53,7 @@ export declare function updateUser(db: Kysely<Database>, driver: Driver, id: str
|
|
|
50
53
|
role?: Role;
|
|
51
54
|
active?: boolean;
|
|
52
55
|
email?: string | null;
|
|
56
|
+
displayName?: string | null;
|
|
53
57
|
lastLoginAt?: string;
|
|
54
58
|
}): Promise<void>;
|
|
55
59
|
export declare function countUsers(db: Kysely<Database>): Promise<number>;
|
|
@@ -79,8 +83,28 @@ export declare function getSession(db: Kysely<Database>, token: string): Promise
|
|
|
79
83
|
} | null>;
|
|
80
84
|
export declare function touchSession(db: Kysely<Database>, token: string, ttlMs: number): Promise<void>;
|
|
81
85
|
export declare function deleteSession(db: Kysely<Database>, token: string): Promise<void>;
|
|
82
|
-
/**
|
|
83
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Sign a user out everywhere, e.g. after their password changes. `exceptToken` keeps the session
|
|
88
|
+
* that made the change, so saving a new password does not also sign out the person saving it.
|
|
89
|
+
* Returns how many sessions were removed.
|
|
90
|
+
*/
|
|
91
|
+
export declare function deleteSessionsForUser(db: Kysely<Database>, userId: string, opts?: {
|
|
92
|
+
exceptToken?: string;
|
|
93
|
+
}): Promise<number>;
|
|
94
|
+
/**
|
|
95
|
+
* A user's live sessions, newest activity first. `id` is the stored hash of the cookie token,
|
|
96
|
+
* never the token itself, so listing sessions cannot be used to take one over.
|
|
97
|
+
*/
|
|
98
|
+
export declare function listSessionsForUser(db: Kysely<Database>, userId: string): Promise<{
|
|
99
|
+
id: string;
|
|
100
|
+
ip: string | null;
|
|
101
|
+
userAgent: string | null;
|
|
102
|
+
createdAt: string;
|
|
103
|
+
lastSeenAt: string;
|
|
104
|
+
expiresAt: string;
|
|
105
|
+
}[]>;
|
|
106
|
+
/** Revoke one of a user's sessions by its listed id. False when it is not theirs or already gone. */
|
|
107
|
+
export declare function deleteSessionById(db: Kysely<Database>, userId: string, id: string): Promise<boolean>;
|
|
84
108
|
export declare function purgeExpiredSessions(db: Kysely<Database>): Promise<void>;
|
|
85
109
|
export declare function createApiToken(db: Kysely<Database>, input: {
|
|
86
110
|
userId: string;
|
package/dist/repos/auth.js
CHANGED
|
@@ -33,6 +33,7 @@ export async function createUser(db, driver, input) {
|
|
|
33
33
|
id,
|
|
34
34
|
username: input.username,
|
|
35
35
|
email: input.email ?? null,
|
|
36
|
+
display_name: null,
|
|
36
37
|
password_hash: input.passwordHash,
|
|
37
38
|
role_id: roles[input.role],
|
|
38
39
|
active: enc.bool(driver, true),
|
|
@@ -84,6 +85,7 @@ export async function updateUser(db, driver, id, patch) {
|
|
|
84
85
|
...(roles && patch.role ? { role_id: roles[patch.role] } : {}),
|
|
85
86
|
...(patch.active !== undefined ? { active: enc.bool(driver, patch.active) } : {}),
|
|
86
87
|
...(patch.email !== undefined ? { email: patch.email } : {}),
|
|
88
|
+
...(patch.displayName !== undefined ? { display_name: patch.displayName } : {}),
|
|
87
89
|
...(patch.lastLoginAt ? { last_login_at: patch.lastLoginAt } : {}),
|
|
88
90
|
updated_at: nowIso(),
|
|
89
91
|
})
|
|
@@ -119,6 +121,7 @@ function mapUser(row) {
|
|
|
119
121
|
id: row.id,
|
|
120
122
|
username: row.username,
|
|
121
123
|
email: row.email,
|
|
124
|
+
displayName: (row.display_name ?? null),
|
|
122
125
|
passwordHash: row.password_hash,
|
|
123
126
|
role: row.role,
|
|
124
127
|
active: readBool(row.active),
|
|
@@ -178,9 +181,47 @@ export async function touchSession(db, token, ttlMs) {
|
|
|
178
181
|
export async function deleteSession(db, token) {
|
|
179
182
|
await db.deleteFrom('sessions').where('id', '=', hashToken(token)).execute();
|
|
180
183
|
}
|
|
181
|
-
/**
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Sign a user out everywhere, e.g. after their password changes. `exceptToken` keeps the session
|
|
186
|
+
* that made the change, so saving a new password does not also sign out the person saving it.
|
|
187
|
+
* Returns how many sessions were removed.
|
|
188
|
+
*/
|
|
189
|
+
export async function deleteSessionsForUser(db, userId, opts = {}) {
|
|
190
|
+
let q = db.deleteFrom('sessions').where('user_id', '=', userId);
|
|
191
|
+
if (opts.exceptToken)
|
|
192
|
+
q = q.where('id', '!=', hashToken(opts.exceptToken));
|
|
193
|
+
const res = await q.executeTakeFirst();
|
|
194
|
+
return Number(res?.numDeletedRows ?? 0);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* A user's live sessions, newest activity first. `id` is the stored hash of the cookie token,
|
|
198
|
+
* never the token itself, so listing sessions cannot be used to take one over.
|
|
199
|
+
*/
|
|
200
|
+
export async function listSessionsForUser(db, userId) {
|
|
201
|
+
const rows = await db
|
|
202
|
+
.selectFrom('sessions')
|
|
203
|
+
.selectAll()
|
|
204
|
+
.where('user_id', '=', userId)
|
|
205
|
+
.where('expires_at', '>', nowIso())
|
|
206
|
+
.orderBy('last_seen_at', 'desc')
|
|
207
|
+
.execute();
|
|
208
|
+
return rows.map((r) => ({
|
|
209
|
+
id: r.id,
|
|
210
|
+
ip: r.ip,
|
|
211
|
+
userAgent: r.user_agent,
|
|
212
|
+
createdAt: readTs(r.created_at),
|
|
213
|
+
lastSeenAt: readTs(r.last_seen_at),
|
|
214
|
+
expiresAt: readTs(r.expires_at),
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
/** Revoke one of a user's sessions by its listed id. False when it is not theirs or already gone. */
|
|
218
|
+
export async function deleteSessionById(db, userId, id) {
|
|
219
|
+
const res = await db
|
|
220
|
+
.deleteFrom('sessions')
|
|
221
|
+
.where('id', '=', id)
|
|
222
|
+
.where('user_id', '=', userId)
|
|
223
|
+
.executeTakeFirst();
|
|
224
|
+
return Number(res?.numDeletedRows ?? 0) > 0;
|
|
184
225
|
}
|
|
185
226
|
export async function purgeExpiredSessions(db) {
|
|
186
227
|
await db.deleteFrom('sessions').where('expires_at', '<', nowIso()).execute();
|
package/dist/repos/runs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { enc, nowIso, readBool, readJson, readTs } from '../col.js';
|
|
2
2
|
import { newId } from '../ids.js';
|
|
3
|
+
import { gateResultOf } from '../ingest/finalize.js';
|
|
3
4
|
export async function upsertRun(db, _driver, r) {
|
|
4
5
|
const existing = await db
|
|
5
6
|
.selectFrom('runs')
|
|
@@ -116,6 +117,7 @@ export function mapRun(row) {
|
|
|
116
117
|
finishedAt: readTs(row.finished_at) ?? undefined,
|
|
117
118
|
durationMs: row.duration_ms ?? undefined,
|
|
118
119
|
totals,
|
|
120
|
+
gates: gateResultOf(totalsRaw.gates),
|
|
119
121
|
totalsRaw,
|
|
120
122
|
artifactsDir: row.artifacts_dir ?? undefined,
|
|
121
123
|
exitCode: row.exit_code ?? undefined,
|
package/dist/schema.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdods/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "SDODS platform database: one schema on SQLite or Postgres, migrations, result ingest and insights.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SDODS <admin@sdods.com>",
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
"node": ">=22"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@sdods/contracts": "0.
|
|
26
|
+
"@sdods/contracts": "0.11.1",
|
|
27
27
|
"better-sqlite3": "^13.0.3",
|
|
28
28
|
"kysely": "^0.29.5",
|
|
29
29
|
"pg": "^8.23.0",
|
|
30
|
-
"zod": "^4.5
|
|
30
|
+
"zod": "^4.6.5"
|
|
31
31
|
},
|
|
32
32
|
"homepage": "https://sdods.com",
|
|
33
33
|
"bugs": {
|
|
34
|
-
"url": "https://github.com/
|
|
34
|
+
"url": "https://github.com/YarlisAISolutions/SDODS/issues"
|
|
35
35
|
},
|
|
36
36
|
"repository": {
|
|
37
37
|
"type": "git",
|
|
38
|
-
"url": "git+https://github.com/
|
|
38
|
+
"url": "git+https://github.com/YarlisAISolutions/SDODS.git",
|
|
39
39
|
"directory": "packages/db"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|