@sixb/pg 0.1.20 → 0.1.21
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/agents/threads.d.ts +4 -0
- package/dist/agents/threads.d.ts.map +1 -1
- package/dist/auth-storage/sessions.d.ts +2 -1
- package/dist/auth-storage/sessions.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +959 -7
- package/dist/index.js.map +15 -13
- package/dist/migrations.d.ts.map +1 -1
- package/dist/ontology-storage/replacement-plans.d.ts.map +1 -1
- package/dist/ontology-storage/sources.d.ts +2 -1
- package/dist/ontology-storage/sources.d.ts.map +1 -1
- package/dist/pg-action-run-storage.d.ts +4 -0
- package/dist/pg-action-run-storage.d.ts.map +1 -1
- package/dist/pg-activity-storage.d.ts +22 -0
- package/dist/pg-activity-storage.d.ts.map +1 -0
- package/dist/pg-artifact-storage.d.ts +19 -0
- package/dist/pg-artifact-storage.d.ts.map +1 -0
- package/dist/pg-execution-storage.d.ts +2 -1
- package/dist/pg-execution-storage.d.ts.map +1 -1
- package/dist/pg-workflow-run-storage.d.ts +4 -0
- package/dist/pg-workflow-run-storage.d.ts.map +1 -1
- package/dist/share-grant-storage.d.ts +2 -1
- package/dist/share-grant-storage.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/agents/threads.ts +23 -0
- package/src/auth-storage/sessions.ts +20 -0
- package/src/index.ts +10 -0
- package/src/migrations/063-artifacts.sql +67 -0
- package/src/migrations/064-drop-output-diagnostics.sql +14 -0
- package/src/migrations/065-plan-work-payload-statistics.sql +5 -0
- package/src/migrations/066-activity.sql +61 -0
- package/src/migrations/067-activity-timeline.sql +15 -0
- package/src/migrations/068-activity-interactions.sql +20 -0
- package/src/migrations.ts +18 -0
- package/src/ontology-storage/replacement-plans.ts +25 -7
- package/src/ontology-storage/sources.ts +37 -0
- package/src/pg-action-run-storage.ts +23 -0
- package/src/pg-activity-storage.ts +486 -0
- package/src/pg-artifact-storage.ts +325 -0
- package/src/pg-execution-storage.ts +52 -0
- package/src/pg-workflow-run-storage.ts +23 -0
- package/src/share-grant-storage.ts +21 -0
package/dist/index.js
CHANGED
|
@@ -1142,6 +1142,23 @@ class PgAgentThreadStore {
|
|
|
1142
1142
|
`;
|
|
1143
1143
|
return row ? rowToThreadRecord(row) : null;
|
|
1144
1144
|
}
|
|
1145
|
+
async getByIds(params) {
|
|
1146
|
+
const ids = [...new Set(params.ids)];
|
|
1147
|
+
if (ids.length === 0) {
|
|
1148
|
+
return [];
|
|
1149
|
+
}
|
|
1150
|
+
const rows = await this.sql`
|
|
1151
|
+
SELECT *
|
|
1152
|
+
FROM agent_threads
|
|
1153
|
+
WHERE project_id = ${params.projectId}
|
|
1154
|
+
AND id = ANY(${this.sql.array(ids)}::text[])
|
|
1155
|
+
`;
|
|
1156
|
+
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
1157
|
+
return ids.flatMap((id) => {
|
|
1158
|
+
const row = byId.get(id);
|
|
1159
|
+
return row ? [rowToThreadRecord(row)] : [];
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1145
1162
|
async list(input) {
|
|
1146
1163
|
if (input.statuses !== undefined && input.statuses.length === 0) {
|
|
1147
1164
|
return { threads: [], hasMore: false, total: 0 };
|
|
@@ -2739,6 +2756,22 @@ class PgAuthSessionStore {
|
|
|
2739
2756
|
`;
|
|
2740
2757
|
return rows.map(rowToSessionRecord);
|
|
2741
2758
|
}
|
|
2759
|
+
async listByUserId(params) {
|
|
2760
|
+
const rows = await this.sql`
|
|
2761
|
+
SELECT *
|
|
2762
|
+
FROM auth_sessions
|
|
2763
|
+
WHERE project_id = ${params.projectId}
|
|
2764
|
+
AND user_id = ${params.userId}
|
|
2765
|
+
${params.before ? this.sql`AND created_at <= ${params.before.createdAt}` : this.sql``}
|
|
2766
|
+
ORDER BY created_at DESC, id DESC
|
|
2767
|
+
LIMIT ${params.limit + 1}
|
|
2768
|
+
OFFSET ${params.before?.skip ?? 0}
|
|
2769
|
+
`;
|
|
2770
|
+
return {
|
|
2771
|
+
sessions: rows.slice(0, params.limit).map(rowToSessionRecord),
|
|
2772
|
+
hasMore: rows.length > params.limit
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2742
2775
|
async findValidByTokenHash(params) {
|
|
2743
2776
|
const row = await getSessionRowById(this.sql, params);
|
|
2744
2777
|
if (!row || row.refresh_token_hash !== null || row.token_hash !== params.tokenHash || !isLiveSessionRow(row, params.now)) {
|
|
@@ -8119,6 +8152,201 @@ ALTER TABLE auth_magic_links DROP COLUMN audience;
|
|
|
8119
8152
|
ALTER TABLE auth_oidc_authorization_attempts DROP COLUMN audience;
|
|
8120
8153
|
`;
|
|
8121
8154
|
|
|
8155
|
+
// src/migrations/063-artifacts.sql
|
|
8156
|
+
var _063_artifacts_default = `-- Artifacts: versioned documents, sheets, decks, and files. An artifact's kind never
|
|
8157
|
+
-- changes. Versions take \`next_version\`, which only grows, so a number is never reused after its
|
|
8158
|
+
-- version is deleted.
|
|
8159
|
+
CREATE TABLE artifacts (
|
|
8160
|
+
project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
|
|
8161
|
+
id TEXT NOT NULL CHECK (length(trim(id)) > 0),
|
|
8162
|
+
kind TEXT NOT NULL CHECK (kind IN ('document', 'sheet', 'slides', 'file')),
|
|
8163
|
+
title TEXT NOT NULL CHECK (length(trim(title)) > 0),
|
|
8164
|
+
owner_principal_type TEXT NOT NULL
|
|
8165
|
+
CHECK (owner_principal_type IN ('user', 'serviceAccount', 'system')),
|
|
8166
|
+
owner_principal_id TEXT NOT NULL CHECK (length(owner_principal_id) > 0),
|
|
8167
|
+
latest_version INTEGER NOT NULL CHECK (latest_version > 0),
|
|
8168
|
+
next_version INTEGER NOT NULL CHECK (next_version > latest_version),
|
|
8169
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
8170
|
+
updated_at TIMESTAMPTZ NOT NULL,
|
|
8171
|
+
PRIMARY KEY (project_id, id)
|
|
8172
|
+
);
|
|
8173
|
+
|
|
8174
|
+
CREATE INDEX idx_artifacts_project_updated ON artifacts (project_id, updated_at DESC, id);
|
|
8175
|
+
CREATE INDEX idx_artifacts_owner_updated
|
|
8176
|
+
ON artifacts (project_id, owner_principal_type, owner_principal_id, updated_at DESC, id);
|
|
8177
|
+
|
|
8178
|
+
-- \`refs\`, not \`references\`: that word is reserved in SQL.
|
|
8179
|
+
CREATE TABLE artifact_versions (
|
|
8180
|
+
project_id TEXT NOT NULL,
|
|
8181
|
+
artifact_id TEXT NOT NULL,
|
|
8182
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
8183
|
+
title TEXT NOT NULL CHECK (length(trim(title)) > 0),
|
|
8184
|
+
entry TEXT NOT NULL CHECK (length(entry) > 0),
|
|
8185
|
+
files JSONB NOT NULL CHECK (jsonb_typeof(files) = 'array'),
|
|
8186
|
+
outline JSONB NOT NULL CHECK (jsonb_typeof(outline) = 'object'),
|
|
8187
|
+
refs JSONB NOT NULL CHECK (jsonb_typeof(refs) = 'array'),
|
|
8188
|
+
author_principal_type TEXT NOT NULL
|
|
8189
|
+
CHECK (author_principal_type IN ('user', 'serviceAccount', 'system')),
|
|
8190
|
+
author_principal_id TEXT NOT NULL CHECK (length(author_principal_id) > 0),
|
|
8191
|
+
origin_thread_id TEXT,
|
|
8192
|
+
origin_run_id TEXT,
|
|
8193
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
8194
|
+
PRIMARY KEY (project_id, artifact_id, version),
|
|
8195
|
+
FOREIGN KEY (project_id, artifact_id) REFERENCES artifacts (project_id, id) ON DELETE CASCADE,
|
|
8196
|
+
CHECK ((origin_thread_id IS NULL) = (origin_run_id IS NULL))
|
|
8197
|
+
);
|
|
8198
|
+
|
|
8199
|
+
CREATE INDEX idx_artifact_versions_origin_run
|
|
8200
|
+
ON artifact_versions (project_id, origin_run_id) WHERE origin_run_id IS NOT NULL;
|
|
8201
|
+
CREATE INDEX idx_artifact_versions_origin_thread
|
|
8202
|
+
ON artifact_versions (project_id, origin_thread_id, artifact_id) WHERE origin_thread_id IS NOT NULL;
|
|
8203
|
+
|
|
8204
|
+
-- Short-lived links that a sandboxed frame loads a deck's files through. Only the SHA-256 of each
|
|
8205
|
+
-- link's token is stored. \`targets\` is what the frame may show from other artifacts, decided for
|
|
8206
|
+
-- its reader when the link was issued: a JSON array of { address, artifactId, version }.
|
|
8207
|
+
CREATE TABLE artifact_frame_grants (
|
|
8208
|
+
project_id TEXT NOT NULL,
|
|
8209
|
+
token_hash TEXT NOT NULL CHECK (token_hash ~ '^[0-9a-f]{64}$'),
|
|
8210
|
+
artifact_id TEXT NOT NULL,
|
|
8211
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
8212
|
+
targets JSONB NOT NULL CHECK (jsonb_typeof(targets) = 'array'),
|
|
8213
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
8214
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
8215
|
+
PRIMARY KEY (project_id, token_hash),
|
|
8216
|
+
FOREIGN KEY (project_id, artifact_id) REFERENCES artifacts (project_id, id) ON DELETE CASCADE,
|
|
8217
|
+
CHECK (expires_at > created_at)
|
|
8218
|
+
);
|
|
8219
|
+
|
|
8220
|
+
CREATE INDEX idx_artifact_frame_grants_expiry ON artifact_frame_grants (project_id, expires_at);
|
|
8221
|
+
-- Deleting an artifact cascades here, and Postgres does not index a foreign key by itself.
|
|
8222
|
+
CREATE INDEX idx_artifact_frame_grants_artifact ON artifact_frame_grants (project_id, artifact_id);
|
|
8223
|
+
`;
|
|
8224
|
+
|
|
8225
|
+
// src/migrations/064-drop-output-diagnostics.sql
|
|
8226
|
+
var _064_drop_output_diagnostics_default = `-- The Agent delivers files by publishing artifacts, so a run no longer collects an output
|
|
8227
|
+
-- directory or reports on it. Stored \`output_*\` diagnostics go; a run left with none stores none.
|
|
8228
|
+
UPDATE agent_runs
|
|
8229
|
+
SET diagnostics = (
|
|
8230
|
+
SELECT jsonb_agg(item.value ORDER BY item.position)
|
|
8231
|
+
FROM jsonb_array_elements(agent_runs.diagnostics) WITH ORDINALITY AS item(value, position)
|
|
8232
|
+
WHERE item.value->>'code' NOT LIKE 'output\\_%'
|
|
8233
|
+
)
|
|
8234
|
+
WHERE jsonb_typeof(diagnostics) = 'array'
|
|
8235
|
+
AND EXISTS (
|
|
8236
|
+
SELECT 1
|
|
8237
|
+
FROM jsonb_array_elements(agent_runs.diagnostics) AS item(value)
|
|
8238
|
+
WHERE item.value->>'code' LIKE 'output\\_%'
|
|
8239
|
+
);
|
|
8240
|
+
`;
|
|
8241
|
+
|
|
8242
|
+
// src/migrations/065-plan-work-payload-statistics.sql
|
|
8243
|
+
var _065_plan_work_payload_statistics_default = "-- A plan's work is analyzed whenever a stream has staged enough of it, and most of that ANALYZE\n-- went into the statistics of `payload`, which no query reads: the plan queries filter on the\n-- typed columns and only extract values from `payload`, and an extracted value has no statistics.\n-- A query that filters `payload` itself (`@>`, `?`) would need them back.\nALTER TABLE ontology_replacement_plan_work ALTER COLUMN payload SET STATISTICS 0;\n";
|
|
8244
|
+
|
|
8245
|
+
// src/migrations/066-activity.sql
|
|
8246
|
+
var _066_activity_default = `-- App activity: one source per verified session, the visits it opened, and their page views.
|
|
8247
|
+
-- Foreground activity moves a visit's \`last_seen_at\` and stores no row, unless it opens a visit.
|
|
8248
|
+
-- A session id names an \`auth_sessions\` row for a user and a \`share_sessions\` row for a share; it
|
|
8249
|
+
-- is not a secret, and no foreign key holds it, so history outlives its sessions. Ids compare
|
|
8250
|
+
-- byte by byte, as the other providers sort them.
|
|
8251
|
+
CREATE TABLE activity_sources (
|
|
8252
|
+
project_id TEXT NOT NULL,
|
|
8253
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'share')),
|
|
8254
|
+
session_id TEXT NOT NULL CHECK (length(session_id) > 0),
|
|
8255
|
+
visit_id TEXT NOT NULL,
|
|
8256
|
+
last_seen_at TIMESTAMPTZ NOT NULL,
|
|
8257
|
+
sequence INTEGER NOT NULL CHECK (sequence >= 0),
|
|
8258
|
+
window_start TIMESTAMPTZ NOT NULL,
|
|
8259
|
+
window_count INTEGER NOT NULL CHECK (window_count >= 0),
|
|
8260
|
+
PRIMARY KEY (project_id, actor_type, session_id)
|
|
8261
|
+
);
|
|
8262
|
+
|
|
8263
|
+
CREATE TABLE activity_visits (
|
|
8264
|
+
project_id TEXT NOT NULL,
|
|
8265
|
+
id TEXT COLLATE "C" NOT NULL,
|
|
8266
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'share')),
|
|
8267
|
+
user_id TEXT COLLATE "C",
|
|
8268
|
+
grant_id TEXT,
|
|
8269
|
+
session_id TEXT NOT NULL CHECK (length(session_id) > 0),
|
|
8270
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
8271
|
+
last_seen_at TIMESTAMPTZ NOT NULL CHECK (last_seen_at >= started_at),
|
|
8272
|
+
page_views INTEGER NOT NULL CHECK (page_views >= 0),
|
|
8273
|
+
attribution JSONB CHECK (attribution IS NULL OR jsonb_typeof(attribution) = 'object'),
|
|
8274
|
+
PRIMARY KEY (project_id, id),
|
|
8275
|
+
CHECK (
|
|
8276
|
+
(actor_type = 'user' AND user_id IS NOT NULL AND grant_id IS NULL)
|
|
8277
|
+
OR (actor_type = 'share' AND user_id IS NULL AND grant_id IS NOT NULL)
|
|
8278
|
+
)
|
|
8279
|
+
);
|
|
8280
|
+
|
|
8281
|
+
-- A period reads the visits it overlaps. A visit lasts less than a day, so they started in
|
|
8282
|
+
-- \`[from - 1 day, to)\`: both period indexes bound \`started_at\` on both sides. \`last_seen_at\` is
|
|
8283
|
+
-- left out of every index, so the update each batch makes touches none of them. The user index
|
|
8284
|
+
-- also serves deleting one user's history.
|
|
8285
|
+
CREATE INDEX idx_activity_visits_started
|
|
8286
|
+
ON activity_visits (project_id, started_at, id);
|
|
8287
|
+
CREATE INDEX idx_activity_visits_user
|
|
8288
|
+
ON activity_visits (project_id, user_id, started_at, id);
|
|
8289
|
+
-- A session's visits, to join them with the session and the executions it authorized.
|
|
8290
|
+
CREATE INDEX idx_activity_visits_session
|
|
8291
|
+
ON activity_visits (project_id, actor_type, session_id);
|
|
8292
|
+
|
|
8293
|
+
CREATE TABLE activity_page_views (
|
|
8294
|
+
project_id TEXT NOT NULL,
|
|
8295
|
+
visit_id TEXT COLLATE "C" NOT NULL,
|
|
8296
|
+
sequence INTEGER NOT NULL CHECK (sequence > 0),
|
|
8297
|
+
id TEXT NOT NULL,
|
|
8298
|
+
route TEXT NOT NULL,
|
|
8299
|
+
received_at TIMESTAMPTZ NOT NULL,
|
|
8300
|
+
attribution JSONB CHECK (attribution IS NULL OR jsonb_typeof(attribution) = 'object'),
|
|
8301
|
+
PRIMARY KEY (project_id, visit_id, sequence),
|
|
8302
|
+
FOREIGN KEY (project_id, visit_id) REFERENCES activity_visits (project_id, id) ON DELETE CASCADE
|
|
8303
|
+
);
|
|
8304
|
+
|
|
8305
|
+
-- Finds a retried page view by the id its browser chose.
|
|
8306
|
+
CREATE INDEX idx_activity_page_views_id ON activity_page_views (project_id, id);
|
|
8307
|
+
`;
|
|
8308
|
+
|
|
8309
|
+
// src/migrations/067-activity-timeline.sql
|
|
8310
|
+
var _067_activity_timeline_default = `-- A visit's timeline reads what its session started: the roots it authorized in a window, then
|
|
8311
|
+
-- everything they caused. A signed-in session is the credential of its roots, a shared link's
|
|
8312
|
+
-- session their delegation; a child names its parent. Agent executions copy their parent's
|
|
8313
|
+
-- session: the root indexes hold only executions without a parent, so they never see those, and
|
|
8314
|
+
-- end with \`id\` so the root read's order needs no sort. A user's connection history reads
|
|
8315
|
+
-- \`idx_auth_sessions_user_created\`, which already exists.
|
|
8316
|
+
CREATE INDEX idx_executions_session_roots
|
|
8317
|
+
ON executions (project_id, authority_session_id, created_at, id)
|
|
8318
|
+
WHERE authority_session_id IS NOT NULL AND parent_execution_id IS NULL;
|
|
8319
|
+
CREATE INDEX idx_executions_delegation_roots
|
|
8320
|
+
ON executions (project_id, authority_delegation_session_id, created_at, id)
|
|
8321
|
+
WHERE authority_delegation_session_id IS NOT NULL AND parent_execution_id IS NULL;
|
|
8322
|
+
CREATE INDEX idx_executions_parent
|
|
8323
|
+
ON executions (project_id, parent_execution_id)
|
|
8324
|
+
WHERE parent_execution_id IS NOT NULL;
|
|
8325
|
+
`;
|
|
8326
|
+
|
|
8327
|
+
// src/migrations/068-activity-interactions.sql
|
|
8328
|
+
var _068_activity_interactions_default = `-- Interactions an app marks, such as a button: their name and route, never a value. They share
|
|
8329
|
+
-- the visit's sequence with its page views, so a visit reads both in the order they happened, and
|
|
8330
|
+
-- cascade with the visit, which counts them as it counts its page views.
|
|
8331
|
+
ALTER TABLE activity_visits
|
|
8332
|
+
ADD COLUMN interactions INTEGER NOT NULL DEFAULT 0 CHECK (interactions >= 0);
|
|
8333
|
+
|
|
8334
|
+
CREATE TABLE activity_interactions (
|
|
8335
|
+
project_id TEXT NOT NULL,
|
|
8336
|
+
visit_id TEXT COLLATE "C" NOT NULL,
|
|
8337
|
+
sequence INTEGER NOT NULL CHECK (sequence > 0),
|
|
8338
|
+
id TEXT NOT NULL,
|
|
8339
|
+
name TEXT NOT NULL CHECK (length(name) BETWEEN 1 AND 100),
|
|
8340
|
+
route TEXT NOT NULL,
|
|
8341
|
+
received_at TIMESTAMPTZ NOT NULL,
|
|
8342
|
+
PRIMARY KEY (project_id, visit_id, sequence),
|
|
8343
|
+
FOREIGN KEY (project_id, visit_id) REFERENCES activity_visits (project_id, id) ON DELETE CASCADE
|
|
8344
|
+
);
|
|
8345
|
+
|
|
8346
|
+
-- Finds a retried interaction by the id its browser chose.
|
|
8347
|
+
CREATE INDEX idx_activity_interactions_id ON activity_interactions (project_id, id);
|
|
8348
|
+
`;
|
|
8349
|
+
|
|
8122
8350
|
// src/migrations.ts
|
|
8123
8351
|
var POSTGRES_STORAGE_ADAPTER_ID = "SixbPostgresStorage";
|
|
8124
8352
|
function pgSql(id, sqlText) {
|
|
@@ -8347,7 +8575,13 @@ var postgresStorageMigrations = defineMigrations({
|
|
|
8347
8575
|
pgSql("059-file-download-grants", _059_file_download_grants_default),
|
|
8348
8576
|
pgSql("060-drained-source-versions", _060_drained_source_versions_default),
|
|
8349
8577
|
pgSql("061-terminal-action-runs", _061_terminal_action_runs_default),
|
|
8350
|
-
pgSql("062-drop-auth-audience", _062_drop_auth_audience_default)
|
|
8578
|
+
pgSql("062-drop-auth-audience", _062_drop_auth_audience_default),
|
|
8579
|
+
pgSql("063-artifacts", _063_artifacts_default),
|
|
8580
|
+
pgSql("064-drop-output-diagnostics", _064_drop_output_diagnostics_default),
|
|
8581
|
+
pgSql("065-plan-work-payload-statistics", _065_plan_work_payload_statistics_default),
|
|
8582
|
+
pgSql("066-activity", _066_activity_default),
|
|
8583
|
+
pgSql("067-activity-timeline", _067_activity_timeline_default),
|
|
8584
|
+
pgSql("068-activity-interactions", _068_activity_interactions_default)
|
|
8351
8585
|
]
|
|
8352
8586
|
});
|
|
8353
8587
|
|
|
@@ -12675,7 +12909,7 @@ import {
|
|
|
12675
12909
|
var PG_PLAN_WORK_TABLE = "ontology_replacement_plan_work";
|
|
12676
12910
|
var IDENTITIES = "ontology_replacement_plan_identities";
|
|
12677
12911
|
var SORT_KEY = "encode(convert_to(identity_key, 'UTF8'), 'hex')";
|
|
12678
|
-
var ANALYZE_ROWS =
|
|
12912
|
+
var ANALYZE_ROWS = 1000;
|
|
12679
12913
|
function objectRevision(type, id) {
|
|
12680
12914
|
return `jsonb_build_array(
|
|
12681
12915
|
(SELECT jsonb_build_array(version, last_commit_id) FROM objects
|
|
@@ -12812,7 +13046,7 @@ class PgOntologyReplacementPlanStorage {
|
|
|
12812
13046
|
}
|
|
12813
13047
|
async* streamState(input) {
|
|
12814
13048
|
assertPageRows(input.pageRows);
|
|
12815
|
-
await runPgTransaction(this.sql, async (sql) => {
|
|
13049
|
+
const versionId = await runPgTransaction(this.sql, async (sql) => {
|
|
12816
13050
|
const { candidate } = await requirePlan(sql, input);
|
|
12817
13051
|
if (input.entityKind === "object" && candidate.projection_kind !== "object") {
|
|
12818
13052
|
throw new MaterializationConflictError4("source-materialization", "Link projection replacement cannot stream object state.");
|
|
@@ -12825,6 +13059,7 @@ class PgOntologyReplacementPlanStorage {
|
|
|
12825
13059
|
if (expanded >= ANALYZE_ROWS && !isWithinPgTransaction(this.sql))
|
|
12826
13060
|
await analyzePlans(sql);
|
|
12827
13061
|
}
|
|
13062
|
+
return candidate.version_id;
|
|
12828
13063
|
});
|
|
12829
13064
|
let after = "";
|
|
12830
13065
|
let streamed = 0;
|
|
@@ -12854,8 +13089,8 @@ class PgOntologyReplacementPlanStorage {
|
|
|
12854
13089
|
return state;
|
|
12855
13090
|
}, { isolation: "repeatableRead" });
|
|
12856
13091
|
if (!page) {
|
|
12857
|
-
if (streamed
|
|
12858
|
-
await
|
|
13092
|
+
if (streamed > 0 && !isWithinPgTransaction(this.sql)) {
|
|
13093
|
+
await analyzeStagedWork(this.sql, versionId);
|
|
12859
13094
|
}
|
|
12860
13095
|
return;
|
|
12861
13096
|
}
|
|
@@ -13383,6 +13618,16 @@ async function purgeTouches(sql, projectId, limit) {
|
|
|
13383
13618
|
async function analyzePlans(sql) {
|
|
13384
13619
|
await sql`ANALYZE ${sql(IDENTITIES)}, ${sql(PG_PLAN_WORK_TABLE)}`;
|
|
13385
13620
|
}
|
|
13621
|
+
async function analyzeStagedWork(sql, versionId) {
|
|
13622
|
+
const [staged] = await sql`
|
|
13623
|
+
SELECT COUNT(*) AS rows FROM (
|
|
13624
|
+
SELECT 1 FROM ${sql(PG_PLAN_WORK_TABLE)} WHERE work_id = ${versionId} LIMIT ${ANALYZE_ROWS}
|
|
13625
|
+
) AS work
|
|
13626
|
+
`;
|
|
13627
|
+
if (Number(staged?.rows ?? 0) < ANALYZE_ROWS)
|
|
13628
|
+
return;
|
|
13629
|
+
await analyzePlans(sql);
|
|
13630
|
+
}
|
|
13386
13631
|
async function dropUnplannedWork(sql, versionId, kind) {
|
|
13387
13632
|
await sql`
|
|
13388
13633
|
DELETE FROM ${sql(PG_PLAN_WORK_TABLE)}
|
|
@@ -14650,6 +14895,7 @@ import { ONTOLOGY_OUTBOX_FAILURE_CODES as ONTOLOGY_OUTBOX_FAILURE_CODES2 } from
|
|
|
14650
14895
|
|
|
14651
14896
|
// src/pg-execution-storage.ts
|
|
14652
14897
|
import {
|
|
14898
|
+
compareExecutionCreation,
|
|
14653
14899
|
executionRecordFromStorageRow,
|
|
14654
14900
|
executionRecordToStorageRow,
|
|
14655
14901
|
prepareExecutionRecord
|
|
@@ -14743,6 +14989,37 @@ class PgExecutionStorage {
|
|
|
14743
14989
|
`;
|
|
14744
14990
|
return row ? pgExecutionRecord(row) : null;
|
|
14745
14991
|
}
|
|
14992
|
+
async listBySession(input) {
|
|
14993
|
+
const session = input.session.type === "user" ? this.sql`authority_session_id = ${input.session.id}` : this.sql`authority_delegation_session_id = ${input.session.id}`;
|
|
14994
|
+
const rows = await this.sql`
|
|
14995
|
+
WITH RECURSIVE tree AS (
|
|
14996
|
+
(
|
|
14997
|
+
SELECT *, 0 AS depth
|
|
14998
|
+
FROM executions
|
|
14999
|
+
WHERE project_id = ${input.projectId}
|
|
15000
|
+
AND ${session}
|
|
15001
|
+
AND parent_execution_id IS NULL
|
|
15002
|
+
AND created_at >= ${input.from}
|
|
15003
|
+
AND created_at < ${input.to}
|
|
15004
|
+
ORDER BY created_at, id
|
|
15005
|
+
LIMIT ${input.limit + 1}
|
|
15006
|
+
)
|
|
15007
|
+
UNION ALL
|
|
15008
|
+
SELECT child.*, tree.depth + 1
|
|
15009
|
+
FROM tree
|
|
15010
|
+
JOIN executions child
|
|
15011
|
+
ON child.project_id = tree.project_id
|
|
15012
|
+
AND child.parent_execution_id = tree.id
|
|
15013
|
+
)
|
|
15014
|
+
SELECT * FROM tree
|
|
15015
|
+
LIMIT ${input.limit + 1}
|
|
15016
|
+
`;
|
|
15017
|
+
const records = rows.map((row) => ({ depth: row.depth, record: pgExecutionRecord(row) })).sort((left, right) => left.depth - right.depth || compareExecutionCreation(left.record, right.record)).slice(0, input.limit).map(({ record }) => record);
|
|
15018
|
+
return {
|
|
15019
|
+
executions: records.sort(compareExecutionCreation),
|
|
15020
|
+
truncated: rows.length > input.limit
|
|
15021
|
+
};
|
|
15022
|
+
}
|
|
14746
15023
|
}
|
|
14747
15024
|
function pgExecutionRecord(row) {
|
|
14748
15025
|
return executionRecordFromStorageRow(toStorageRow(row));
|
|
@@ -15215,6 +15492,26 @@ class PgOntologySourceStorage {
|
|
|
15215
15492
|
return row ? sourceRecord(row) : null;
|
|
15216
15493
|
});
|
|
15217
15494
|
}
|
|
15495
|
+
async listActive(input) {
|
|
15496
|
+
return this.runRootOperation(async (sql) => {
|
|
15497
|
+
assertNonblank(input.projectId, "Source project id");
|
|
15498
|
+
const rows = await sql`
|
|
15499
|
+
SELECT source_id, dataset_id, dataset_version_id, dataset_version_created_at,
|
|
15500
|
+
projection_revision
|
|
15501
|
+
FROM ontology_sources
|
|
15502
|
+
WHERE project_id = ${input.projectId} AND status = 'active'
|
|
15503
|
+
`;
|
|
15504
|
+
return rows.map((row) => ({
|
|
15505
|
+
source: { projectionId: row.source_id },
|
|
15506
|
+
datasetVersion: {
|
|
15507
|
+
datasetId: row.dataset_id,
|
|
15508
|
+
versionId: row.dataset_version_id,
|
|
15509
|
+
createdAt: toIsoString2(row.dataset_version_created_at)
|
|
15510
|
+
},
|
|
15511
|
+
projectionRevision: row.projection_revision
|
|
15512
|
+
}));
|
|
15513
|
+
});
|
|
15514
|
+
}
|
|
15218
15515
|
async adopt(input) {
|
|
15219
15516
|
return this.runRootOperation(async (sql) => {
|
|
15220
15517
|
assertProjectAndSource(input);
|
|
@@ -15886,6 +16183,23 @@ class PgActionRunStorage {
|
|
|
15886
16183
|
`;
|
|
15887
16184
|
return row ? rowToActionRunRecord(row) : null;
|
|
15888
16185
|
}
|
|
16186
|
+
async getByExecutionIds(params) {
|
|
16187
|
+
const executionIds = [...new Set(params.executionIds)];
|
|
16188
|
+
if (executionIds.length === 0) {
|
|
16189
|
+
return [];
|
|
16190
|
+
}
|
|
16191
|
+
const rows = await this.sql`
|
|
16192
|
+
SELECT *
|
|
16193
|
+
FROM action_runs
|
|
16194
|
+
WHERE project_id = ${params.projectId}
|
|
16195
|
+
AND execution_id = ANY(${this.sql.array(executionIds)}::text[])
|
|
16196
|
+
`;
|
|
16197
|
+
const byExecution = new Map(rows.map((row) => [row.execution_id, row]));
|
|
16198
|
+
return executionIds.flatMap((executionId) => {
|
|
16199
|
+
const row = byExecution.get(executionId);
|
|
16200
|
+
return row ? [rowToActionRunRecord(row)] : [];
|
|
16201
|
+
});
|
|
16202
|
+
}
|
|
15889
16203
|
async list(input) {
|
|
15890
16204
|
if (input.statuses && input.statuses.length === 0 || input.actionIds?.length === 0) {
|
|
15891
16205
|
return {
|
|
@@ -16049,6 +16363,350 @@ function rowToActionSubject(row) {
|
|
|
16049
16363
|
};
|
|
16050
16364
|
}
|
|
16051
16365
|
|
|
16366
|
+
// src/pg-activity-storage.ts
|
|
16367
|
+
import {
|
|
16368
|
+
ACTIVITY_MAX_VISIT_MS,
|
|
16369
|
+
activityActor,
|
|
16370
|
+
activityCalendarDays,
|
|
16371
|
+
parseActivityAttribution,
|
|
16372
|
+
planActivityAppend,
|
|
16373
|
+
serializeActivityAttribution,
|
|
16374
|
+
validateActivityAppend
|
|
16375
|
+
} from "@sixb/core/internal/storage";
|
|
16376
|
+
var NO_CURSOR = { at: new Date("9999-12-31T23:59:59.999Z"), id: "" };
|
|
16377
|
+
|
|
16378
|
+
class PgActivityStorage {
|
|
16379
|
+
sql;
|
|
16380
|
+
constructor(sql) {
|
|
16381
|
+
this.sql = sql;
|
|
16382
|
+
}
|
|
16383
|
+
async append(input) {
|
|
16384
|
+
validateActivityAppend(input);
|
|
16385
|
+
return runPgTransaction(this.sql, async (sql) => {
|
|
16386
|
+
await lockAdvisoryKeys(sql, [
|
|
16387
|
+
JSON.stringify(["activity", input.projectId, input.actor.type, input.sessionId])
|
|
16388
|
+
]);
|
|
16389
|
+
const plan = planActivityAppend(input, await readSource(sql, input), await readStoredEvents(sql, input));
|
|
16390
|
+
if (plan.status !== "append") {
|
|
16391
|
+
return { status: plan.status === "duplicate" ? "accepted" : "throttled", accepted: 0 };
|
|
16392
|
+
}
|
|
16393
|
+
const { state } = plan;
|
|
16394
|
+
const lastSeenAt = new Date(state.lastSeenAt);
|
|
16395
|
+
if (plan.newVisit) {
|
|
16396
|
+
const attribution = plan.newVisit.attribution === null ? null : serializeActivityAttribution(plan.newVisit.attribution);
|
|
16397
|
+
await sql`
|
|
16398
|
+
INSERT INTO activity_visits (
|
|
16399
|
+
project_id, id, actor_type, user_id, grant_id, session_id,
|
|
16400
|
+
started_at, last_seen_at, page_views, interactions, attribution
|
|
16401
|
+
) VALUES (
|
|
16402
|
+
${input.projectId}, ${state.visitId}, ${input.actor.type},
|
|
16403
|
+
${input.actor.type === "user" ? input.actor.userId : null},
|
|
16404
|
+
${input.actor.type === "share" ? input.actor.grantId : null},
|
|
16405
|
+
${input.sessionId}, ${lastSeenAt}, ${lastSeenAt}, ${plan.pageViews.length},
|
|
16406
|
+
${plan.interactions.length}, ${attribution}::text::jsonb
|
|
16407
|
+
)
|
|
16408
|
+
`;
|
|
16409
|
+
} else {
|
|
16410
|
+
await sql`
|
|
16411
|
+
UPDATE activity_visits
|
|
16412
|
+
SET
|
|
16413
|
+
last_seen_at = ${lastSeenAt},
|
|
16414
|
+
page_views = page_views + ${plan.pageViews.length},
|
|
16415
|
+
interactions = interactions + ${plan.interactions.length}
|
|
16416
|
+
WHERE project_id = ${input.projectId} AND id = ${state.visitId}
|
|
16417
|
+
`;
|
|
16418
|
+
}
|
|
16419
|
+
for (const pageView of plan.pageViews) {
|
|
16420
|
+
await sql`
|
|
16421
|
+
INSERT INTO activity_page_views (
|
|
16422
|
+
project_id, visit_id, sequence, id, route, received_at, attribution
|
|
16423
|
+
) VALUES (
|
|
16424
|
+
${input.projectId}, ${pageView.visitId}, ${pageView.sequence}, ${pageView.id},
|
|
16425
|
+
${pageView.route}, ${pageView.receivedAt},
|
|
16426
|
+
${serializeActivityAttribution(pageView.attribution)}::text::jsonb
|
|
16427
|
+
)
|
|
16428
|
+
`;
|
|
16429
|
+
}
|
|
16430
|
+
for (const interaction of plan.interactions) {
|
|
16431
|
+
await sql`
|
|
16432
|
+
INSERT INTO activity_interactions (
|
|
16433
|
+
project_id, visit_id, sequence, id, name, route, received_at
|
|
16434
|
+
) VALUES (
|
|
16435
|
+
${input.projectId}, ${interaction.visitId}, ${interaction.sequence},
|
|
16436
|
+
${interaction.id}, ${interaction.name}, ${interaction.route}, ${interaction.receivedAt}
|
|
16437
|
+
)
|
|
16438
|
+
`;
|
|
16439
|
+
}
|
|
16440
|
+
await sql`
|
|
16441
|
+
INSERT INTO activity_sources (
|
|
16442
|
+
project_id, actor_type, session_id, visit_id, last_seen_at, sequence,
|
|
16443
|
+
window_start, window_count
|
|
16444
|
+
) VALUES (
|
|
16445
|
+
${input.projectId}, ${input.actor.type}, ${input.sessionId}, ${state.visitId},
|
|
16446
|
+
${lastSeenAt}, ${state.sequence}, ${new Date(state.windowStart)}, ${state.windowCount}
|
|
16447
|
+
)
|
|
16448
|
+
ON CONFLICT (project_id, actor_type, session_id) DO UPDATE SET
|
|
16449
|
+
visit_id = excluded.visit_id,
|
|
16450
|
+
last_seen_at = excluded.last_seen_at,
|
|
16451
|
+
sequence = excluded.sequence,
|
|
16452
|
+
window_start = excluded.window_start,
|
|
16453
|
+
window_count = excluded.window_count
|
|
16454
|
+
`;
|
|
16455
|
+
return { status: "accepted", accepted: plan.accepted };
|
|
16456
|
+
});
|
|
16457
|
+
}
|
|
16458
|
+
async list(input) {
|
|
16459
|
+
const before = input.before ?? NO_CURSOR;
|
|
16460
|
+
const rows = await this.sql`
|
|
16461
|
+
SELECT ${this.visitColumns()}
|
|
16462
|
+
FROM activity_visits v
|
|
16463
|
+
WHERE ${this.overlapping(input)}
|
|
16464
|
+
AND (
|
|
16465
|
+
v.last_seen_at < ${before.at}
|
|
16466
|
+
OR (v.last_seen_at = ${before.at} AND v.id < ${before.id})
|
|
16467
|
+
)
|
|
16468
|
+
ORDER BY v.last_seen_at DESC, v.id DESC
|
|
16469
|
+
LIMIT ${input.limit + 1}
|
|
16470
|
+
`;
|
|
16471
|
+
return {
|
|
16472
|
+
visits: rows.slice(0, input.limit).map(visitFromRow),
|
|
16473
|
+
hasMore: rows.length > input.limit
|
|
16474
|
+
};
|
|
16475
|
+
}
|
|
16476
|
+
async users(input) {
|
|
16477
|
+
const before = input.before ?? NO_CURSOR;
|
|
16478
|
+
const rows = await this.sql`
|
|
16479
|
+
SELECT
|
|
16480
|
+
v.user_id,
|
|
16481
|
+
MAX(v.last_seen_at) AS last_seen_at,
|
|
16482
|
+
COUNT(DISTINCT v.id) AS visits,
|
|
16483
|
+
COUNT(p.id) AS page_views
|
|
16484
|
+
FROM activity_visits v
|
|
16485
|
+
LEFT JOIN activity_page_views p
|
|
16486
|
+
ON p.project_id = v.project_id
|
|
16487
|
+
AND p.visit_id = v.id
|
|
16488
|
+
AND p.received_at >= ${input.from}
|
|
16489
|
+
AND p.received_at < ${input.to}
|
|
16490
|
+
WHERE ${this.overlapping(input)} AND v.actor_type = 'user'
|
|
16491
|
+
GROUP BY v.user_id
|
|
16492
|
+
HAVING MAX(v.last_seen_at) < ${before.at}
|
|
16493
|
+
OR (MAX(v.last_seen_at) = ${before.at} AND v.user_id < ${before.id})
|
|
16494
|
+
ORDER BY last_seen_at DESC, v.user_id DESC
|
|
16495
|
+
LIMIT ${input.limit + 1}
|
|
16496
|
+
`;
|
|
16497
|
+
return {
|
|
16498
|
+
users: rows.slice(0, input.limit).map((row) => ({
|
|
16499
|
+
userId: row.user_id,
|
|
16500
|
+
lastSeenAt: new Date(row.last_seen_at),
|
|
16501
|
+
visits: Number(row.visits),
|
|
16502
|
+
pageViews: Number(row.page_views)
|
|
16503
|
+
})),
|
|
16504
|
+
hasMore: rows.length > input.limit
|
|
16505
|
+
};
|
|
16506
|
+
}
|
|
16507
|
+
async summarize(input) {
|
|
16508
|
+
const [row] = await this.sql`
|
|
16509
|
+
SELECT
|
|
16510
|
+
COUNT(DISTINCT v.user_id) AS users,
|
|
16511
|
+
COUNT(DISTINCT v.id) AS visits,
|
|
16512
|
+
COUNT(DISTINCT v.id) FILTER (WHERE v.actor_type = 'share') AS shared_visits,
|
|
16513
|
+
COUNT(p.id) AS page_views
|
|
16514
|
+
FROM activity_visits v
|
|
16515
|
+
LEFT JOIN activity_page_views p
|
|
16516
|
+
ON p.project_id = v.project_id
|
|
16517
|
+
AND p.visit_id = v.id
|
|
16518
|
+
AND p.received_at >= ${input.from}
|
|
16519
|
+
AND p.received_at < ${input.to}
|
|
16520
|
+
WHERE ${this.overlapping(input)}
|
|
16521
|
+
`;
|
|
16522
|
+
return {
|
|
16523
|
+
users: Number(row?.users ?? 0),
|
|
16524
|
+
visits: Number(row?.visits ?? 0),
|
|
16525
|
+
sharedVisits: Number(row?.shared_visits ?? 0),
|
|
16526
|
+
pageViews: Number(row?.page_views ?? 0)
|
|
16527
|
+
};
|
|
16528
|
+
}
|
|
16529
|
+
async calendar(input) {
|
|
16530
|
+
const visits = await this.sql`
|
|
16531
|
+
SELECT v.started_at, v.last_seen_at
|
|
16532
|
+
FROM activity_visits v
|
|
16533
|
+
WHERE ${this.overlapping(input)}
|
|
16534
|
+
`;
|
|
16535
|
+
const days = await this.sql`
|
|
16536
|
+
SELECT
|
|
16537
|
+
to_char(p.received_at AT TIME ZONE 'UTC', 'YYYY-MM-DD') AS day,
|
|
16538
|
+
COUNT(*) AS page_views
|
|
16539
|
+
FROM activity_visits v
|
|
16540
|
+
JOIN activity_page_views p ON p.project_id = v.project_id AND p.visit_id = v.id
|
|
16541
|
+
WHERE ${this.overlapping(input)}
|
|
16542
|
+
AND p.received_at >= ${input.from}
|
|
16543
|
+
AND p.received_at < ${input.to}
|
|
16544
|
+
GROUP BY day
|
|
16545
|
+
`;
|
|
16546
|
+
return activityCalendarDays(input, visits.map((visit) => ({
|
|
16547
|
+
startedAt: new Date(visit.started_at),
|
|
16548
|
+
lastSeenAt: new Date(visit.last_seen_at)
|
|
16549
|
+
})), new Map(days.map((row) => [row.day, Number(row.page_views)])));
|
|
16550
|
+
}
|
|
16551
|
+
async read(input) {
|
|
16552
|
+
const [visit] = await this.sql`
|
|
16553
|
+
SELECT ${this.visitColumns()}, v.session_id
|
|
16554
|
+
FROM activity_visits v
|
|
16555
|
+
WHERE v.project_id = ${input.projectId} AND v.id = ${input.id}
|
|
16556
|
+
`;
|
|
16557
|
+
if (!visit) {
|
|
16558
|
+
return null;
|
|
16559
|
+
}
|
|
16560
|
+
const rows = await this.sql`
|
|
16561
|
+
SELECT id, visit_id, sequence, route, NULL::text AS name, received_at
|
|
16562
|
+
FROM activity_page_views
|
|
16563
|
+
WHERE project_id = ${input.projectId}
|
|
16564
|
+
AND visit_id = ${input.id}
|
|
16565
|
+
UNION ALL
|
|
16566
|
+
SELECT id, visit_id, sequence, route, name, received_at
|
|
16567
|
+
FROM activity_interactions
|
|
16568
|
+
WHERE project_id = ${input.projectId}
|
|
16569
|
+
AND visit_id = ${input.id}
|
|
16570
|
+
ORDER BY sequence
|
|
16571
|
+
LIMIT ${input.limit + 1}
|
|
16572
|
+
`;
|
|
16573
|
+
const pageViews = [];
|
|
16574
|
+
const interactions = [];
|
|
16575
|
+
for (const row of rows.slice(0, input.limit)) {
|
|
16576
|
+
if (row.name === null) {
|
|
16577
|
+
pageViews.push(pageViewFromRow(row));
|
|
16578
|
+
} else {
|
|
16579
|
+
interactions.push({ ...pageViewFromRow(row), name: row.name });
|
|
16580
|
+
}
|
|
16581
|
+
}
|
|
16582
|
+
return {
|
|
16583
|
+
visit: visitFromRow(visit),
|
|
16584
|
+
sessionId: visit.session_id,
|
|
16585
|
+
pageViews,
|
|
16586
|
+
interactions,
|
|
16587
|
+
hasMore: rows.length > input.limit
|
|
16588
|
+
};
|
|
16589
|
+
}
|
|
16590
|
+
async sessionVisits(input) {
|
|
16591
|
+
if (input.sessionIds.length === 0) {
|
|
16592
|
+
return [];
|
|
16593
|
+
}
|
|
16594
|
+
const rows = await this.sql`
|
|
16595
|
+
SELECT *
|
|
16596
|
+
FROM (
|
|
16597
|
+
SELECT
|
|
16598
|
+
${this.visitColumns()},
|
|
16599
|
+
v.session_id,
|
|
16600
|
+
ROW_NUMBER() OVER (
|
|
16601
|
+
PARTITION BY v.session_id ORDER BY v.started_at DESC, v.id DESC
|
|
16602
|
+
) AS position,
|
|
16603
|
+
COUNT(*) OVER (PARTITION BY v.session_id) AS total
|
|
16604
|
+
FROM activity_visits v
|
|
16605
|
+
WHERE v.project_id = ${input.projectId}
|
|
16606
|
+
AND v.actor_type = 'user'
|
|
16607
|
+
AND v.session_id = ANY(${this.sql.array([...input.sessionIds])}::text[])
|
|
16608
|
+
AND v.user_id = ${input.userId}
|
|
16609
|
+
) ranked
|
|
16610
|
+
WHERE position <= ${input.limit}
|
|
16611
|
+
ORDER BY session_id, position
|
|
16612
|
+
`;
|
|
16613
|
+
const bySession = new Map;
|
|
16614
|
+
for (const row of rows) {
|
|
16615
|
+
const session = bySession.get(row.session_id) ?? { visits: Number(row.total), latest: [] };
|
|
16616
|
+
session.latest.push(visitFromRow(row));
|
|
16617
|
+
bySession.set(row.session_id, session);
|
|
16618
|
+
}
|
|
16619
|
+
return [...bySession].map(([sessionId, session]) => ({ sessionId, ...session }));
|
|
16620
|
+
}
|
|
16621
|
+
overlapping(input) {
|
|
16622
|
+
const user = input.userId === undefined ? this.sql`` : this.sql`AND v.user_id = ${input.userId}`;
|
|
16623
|
+
const earliestStart = new Date(input.from.getTime() - ACTIVITY_MAX_VISIT_MS);
|
|
16624
|
+
return this.sql`
|
|
16625
|
+
v.project_id = ${input.projectId}
|
|
16626
|
+
AND v.started_at >= ${earliestStart}
|
|
16627
|
+
AND v.started_at < ${input.to}
|
|
16628
|
+
AND v.last_seen_at >= ${input.from}
|
|
16629
|
+
${user}
|
|
16630
|
+
`;
|
|
16631
|
+
}
|
|
16632
|
+
visitColumns() {
|
|
16633
|
+
return this.sql`
|
|
16634
|
+
v.id, v.actor_type, v.user_id, v.grant_id, v.started_at, v.last_seen_at, v.page_views,
|
|
16635
|
+
v.interactions, v.attribution::text AS attribution
|
|
16636
|
+
`;
|
|
16637
|
+
}
|
|
16638
|
+
}
|
|
16639
|
+
async function readSource(sql, input) {
|
|
16640
|
+
const [row] = await sql`
|
|
16641
|
+
SELECT
|
|
16642
|
+
s.visit_id, v.started_at AS visit_started_at, s.last_seen_at, s.sequence,
|
|
16643
|
+
s.window_start, s.window_count
|
|
16644
|
+
FROM activity_sources s
|
|
16645
|
+
LEFT JOIN activity_visits v ON v.project_id = s.project_id AND v.id = s.visit_id
|
|
16646
|
+
WHERE s.project_id = ${input.projectId}
|
|
16647
|
+
AND s.actor_type = ${input.actor.type}
|
|
16648
|
+
AND s.session_id = ${input.sessionId}
|
|
16649
|
+
`;
|
|
16650
|
+
if (!row) {
|
|
16651
|
+
return;
|
|
16652
|
+
}
|
|
16653
|
+
return {
|
|
16654
|
+
visitId: row.visit_id,
|
|
16655
|
+
visitStartedAt: row.visit_started_at === null ? null : new Date(row.visit_started_at).getTime(),
|
|
16656
|
+
lastSeenAt: new Date(row.last_seen_at).getTime(),
|
|
16657
|
+
sequence: Number(row.sequence),
|
|
16658
|
+
windowStart: new Date(row.window_start).getTime(),
|
|
16659
|
+
windowCount: Number(row.window_count)
|
|
16660
|
+
};
|
|
16661
|
+
}
|
|
16662
|
+
async function readStoredEvents(sql, input) {
|
|
16663
|
+
const ids = input.events.map((event) => event.id);
|
|
16664
|
+
const rows = await sql`
|
|
16665
|
+
SELECT p.id, p.route, NULL::text AS name, p.attribution::text AS attribution
|
|
16666
|
+
FROM activity_page_views p
|
|
16667
|
+
JOIN activity_visits v ON v.project_id = p.project_id AND v.id = p.visit_id
|
|
16668
|
+
WHERE p.project_id = ${input.projectId}
|
|
16669
|
+
AND p.id = ANY(${sql.array(ids)}::text[])
|
|
16670
|
+
AND v.actor_type = ${input.actor.type}
|
|
16671
|
+
AND v.session_id = ${input.sessionId}
|
|
16672
|
+
UNION ALL
|
|
16673
|
+
SELECT i.id, i.route, i.name, NULL::text AS attribution
|
|
16674
|
+
FROM activity_interactions i
|
|
16675
|
+
JOIN activity_visits v ON v.project_id = i.project_id AND v.id = i.visit_id
|
|
16676
|
+
WHERE i.project_id = ${input.projectId}
|
|
16677
|
+
AND i.id = ANY(${sql.array(ids)}::text[])
|
|
16678
|
+
AND v.actor_type = ${input.actor.type}
|
|
16679
|
+
AND v.session_id = ${input.sessionId}
|
|
16680
|
+
`;
|
|
16681
|
+
return rows.map((row) => {
|
|
16682
|
+
if (row.name !== null) {
|
|
16683
|
+
return { id: row.id, route: row.route, name: row.name };
|
|
16684
|
+
}
|
|
16685
|
+
const attribution = parseActivityAttribution(row.attribution);
|
|
16686
|
+
return { id: row.id, route: row.route, ...attribution ? { attribution } : {} };
|
|
16687
|
+
});
|
|
16688
|
+
}
|
|
16689
|
+
function visitFromRow(row) {
|
|
16690
|
+
return {
|
|
16691
|
+
id: row.id,
|
|
16692
|
+
actor: activityActor(row.actor_type, row.user_id, row.grant_id),
|
|
16693
|
+
attribution: parseActivityAttribution(row.attribution),
|
|
16694
|
+
startedAt: new Date(row.started_at),
|
|
16695
|
+
lastSeenAt: new Date(row.last_seen_at),
|
|
16696
|
+
pageViews: Number(row.page_views),
|
|
16697
|
+
interactions: Number(row.interactions)
|
|
16698
|
+
};
|
|
16699
|
+
}
|
|
16700
|
+
function pageViewFromRow(row) {
|
|
16701
|
+
return {
|
|
16702
|
+
id: row.id,
|
|
16703
|
+
visitId: row.visit_id,
|
|
16704
|
+
sequence: Number(row.sequence),
|
|
16705
|
+
route: row.route,
|
|
16706
|
+
receivedAt: new Date(row.received_at)
|
|
16707
|
+
};
|
|
16708
|
+
}
|
|
16709
|
+
|
|
16052
16710
|
// src/pg-ai-cost-storage.ts
|
|
16053
16711
|
import {
|
|
16054
16712
|
aiModelCallCostDetails,
|
|
@@ -17532,6 +18190,260 @@ function invalidState(reservation, operation) {
|
|
|
17532
18190
|
return new AiLimitStorageError("invalid_reservation_state", `[SixbPg] Cannot ${operation} AI model-call reservation '${reservation.callId}' in state '${reservation.state}'.`);
|
|
17533
18191
|
}
|
|
17534
18192
|
|
|
18193
|
+
// src/pg-artifact-storage.ts
|
|
18194
|
+
import {
|
|
18195
|
+
artifactStorageErrors,
|
|
18196
|
+
parseArtifactFrameGrantRow,
|
|
18197
|
+
parseArtifactRow,
|
|
18198
|
+
parseArtifactVersionRow
|
|
18199
|
+
} from "@sixb/core/internal/storage";
|
|
18200
|
+
var ARTIFACT_COLUMNS = `
|
|
18201
|
+
project_id AS "projectId", id, kind, title,
|
|
18202
|
+
owner_principal_type AS "ownerPrincipalType", owner_principal_id AS "ownerPrincipalId",
|
|
18203
|
+
latest_version AS "latestVersion", created_at AS "createdAt", updated_at AS "updatedAt"
|
|
18204
|
+
`;
|
|
18205
|
+
var VERSION_COLUMNS = `
|
|
18206
|
+
project_id AS "projectId", artifact_id AS "artifactId", version, title, entry, files, outline,
|
|
18207
|
+
refs, author_principal_type AS "authorPrincipalType", author_principal_id AS "authorPrincipalId",
|
|
18208
|
+
origin_thread_id AS "originThreadId", origin_run_id AS "originRunId", created_at AS "createdAt"
|
|
18209
|
+
`;
|
|
18210
|
+
var FRAME_GRANT_COLUMNS = `
|
|
18211
|
+
project_id AS "projectId", token_hash AS "tokenHash", artifact_id AS "artifactId", version,
|
|
18212
|
+
targets, created_at AS "createdAt", expires_at AS "expiresAt"
|
|
18213
|
+
`;
|
|
18214
|
+
|
|
18215
|
+
class PgArtifactStorage {
|
|
18216
|
+
sql;
|
|
18217
|
+
constructor(sql) {
|
|
18218
|
+
this.sql = sql;
|
|
18219
|
+
}
|
|
18220
|
+
async create(input) {
|
|
18221
|
+
return runPgTransaction(this.sql, async (tx) => {
|
|
18222
|
+
const [artifact] = await tx`
|
|
18223
|
+
INSERT INTO artifacts (
|
|
18224
|
+
project_id, id, kind, title, owner_principal_type, owner_principal_id,
|
|
18225
|
+
latest_version, next_version, created_at, updated_at
|
|
18226
|
+
) VALUES (
|
|
18227
|
+
${input.projectId}, ${input.id}, ${input.kind}, ${input.version.title},
|
|
18228
|
+
${input.owner.type}, ${input.owner.id}, 1, 2,
|
|
18229
|
+
${input.version.createdAt}, ${input.version.createdAt}
|
|
18230
|
+
)
|
|
18231
|
+
ON CONFLICT (project_id, id) DO NOTHING
|
|
18232
|
+
RETURNING ${tx.unsafe(ARTIFACT_COLUMNS)}
|
|
18233
|
+
`;
|
|
18234
|
+
if (!artifact)
|
|
18235
|
+
throw artifactStorageErrors.exists(input.id);
|
|
18236
|
+
return {
|
|
18237
|
+
artifact: parseArtifactRow(artifact),
|
|
18238
|
+
version: await insertVersion(tx, input.projectId, input.id, 1, input.version)
|
|
18239
|
+
};
|
|
18240
|
+
});
|
|
18241
|
+
}
|
|
18242
|
+
async publish(input) {
|
|
18243
|
+
return runPgTransaction(this.sql, async (tx) => {
|
|
18244
|
+
const [current] = await tx`
|
|
18245
|
+
SELECT kind, next_version FROM artifacts
|
|
18246
|
+
WHERE project_id = ${input.projectId} AND id = ${input.artifactId}
|
|
18247
|
+
FOR UPDATE
|
|
18248
|
+
`;
|
|
18249
|
+
if (!current)
|
|
18250
|
+
throw artifactStorageErrors.notFound(input.artifactId);
|
|
18251
|
+
const kind = current.kind;
|
|
18252
|
+
if (kind !== input.kind) {
|
|
18253
|
+
throw artifactStorageErrors.kindMismatch(input.artifactId, kind, input.kind);
|
|
18254
|
+
}
|
|
18255
|
+
const version = await insertVersion(tx, input.projectId, input.artifactId, Number(current.next_version), input.version);
|
|
18256
|
+
const [artifact] = await tx`
|
|
18257
|
+
UPDATE artifacts
|
|
18258
|
+
SET title = ${input.version.title}, latest_version = ${version.version},
|
|
18259
|
+
next_version = ${version.version + 1}, updated_at = ${input.version.createdAt}
|
|
18260
|
+
WHERE project_id = ${input.projectId} AND id = ${input.artifactId}
|
|
18261
|
+
RETURNING ${tx.unsafe(ARTIFACT_COLUMNS)}
|
|
18262
|
+
`;
|
|
18263
|
+
return { artifact: parseArtifactRow(artifact), version };
|
|
18264
|
+
});
|
|
18265
|
+
}
|
|
18266
|
+
async get(input) {
|
|
18267
|
+
const [row] = await this.sql`
|
|
18268
|
+
SELECT ${this.sql.unsafe(ARTIFACT_COLUMNS)} FROM artifacts
|
|
18269
|
+
WHERE project_id = ${input.projectId} AND id = ${input.id}
|
|
18270
|
+
`;
|
|
18271
|
+
return row ? parseArtifactRow(row) : null;
|
|
18272
|
+
}
|
|
18273
|
+
async list(input) {
|
|
18274
|
+
const where = ["a.project_id = $1"];
|
|
18275
|
+
const params = [input.projectId];
|
|
18276
|
+
if (input.owner) {
|
|
18277
|
+
where.push(`a.owner_principal_type = $${params.length + 1}`);
|
|
18278
|
+
params.push(input.owner.type);
|
|
18279
|
+
where.push(`a.owner_principal_id = $${params.length + 1}`);
|
|
18280
|
+
params.push(input.owner.id);
|
|
18281
|
+
}
|
|
18282
|
+
if (input.originThreadId !== undefined) {
|
|
18283
|
+
where.push(`EXISTS (
|
|
18284
|
+
SELECT 1 FROM artifact_versions v
|
|
18285
|
+
WHERE v.project_id = a.project_id AND v.artifact_id = a.id
|
|
18286
|
+
AND v.origin_thread_id = $${params.length + 1}
|
|
18287
|
+
)`);
|
|
18288
|
+
params.push(input.originThreadId);
|
|
18289
|
+
}
|
|
18290
|
+
if (input.kind !== undefined) {
|
|
18291
|
+
where.push(`a.kind = $${params.length + 1}`);
|
|
18292
|
+
params.push(input.kind);
|
|
18293
|
+
}
|
|
18294
|
+
const clause = where.join(" AND ");
|
|
18295
|
+
const [counted] = await this.sql.unsafe(`SELECT COUNT(*)::int AS total FROM artifacts a WHERE ${clause}`, params);
|
|
18296
|
+
const offset = input.offset ?? 0;
|
|
18297
|
+
const rows = await this.sql.unsafe(`SELECT ${ARTIFACT_COLUMNS} FROM artifacts a WHERE ${clause}
|
|
18298
|
+
ORDER BY a.updated_at DESC, a.id ASC
|
|
18299
|
+
LIMIT $${params.length + 1} OFFSET $${params.length + 2}`, [...params, input.limit ?? null, offset]);
|
|
18300
|
+
const total = counted?.total ?? 0;
|
|
18301
|
+
return { artifacts: rows.map(parseArtifactRow), hasMore: offset + rows.length < total, total };
|
|
18302
|
+
}
|
|
18303
|
+
async getVersion(input) {
|
|
18304
|
+
const [row] = await this.sql`
|
|
18305
|
+
SELECT ${this.sql.unsafe(VERSION_COLUMNS)} FROM artifact_versions
|
|
18306
|
+
WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
|
|
18307
|
+
AND version = ${input.version}
|
|
18308
|
+
`;
|
|
18309
|
+
return row ? parseArtifactVersionRow(row) : null;
|
|
18310
|
+
}
|
|
18311
|
+
async listVersions(input) {
|
|
18312
|
+
const [counted] = await this.sql`
|
|
18313
|
+
SELECT COUNT(*)::int AS total FROM artifact_versions
|
|
18314
|
+
WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
|
|
18315
|
+
`;
|
|
18316
|
+
const offset = input.offset ?? 0;
|
|
18317
|
+
const rows = await this.sql`
|
|
18318
|
+
SELECT ${this.sql.unsafe(VERSION_COLUMNS)} FROM artifact_versions
|
|
18319
|
+
WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
|
|
18320
|
+
ORDER BY version DESC
|
|
18321
|
+
LIMIT ${input.limit ?? null} OFFSET ${offset}
|
|
18322
|
+
`;
|
|
18323
|
+
const total = counted?.total ?? 0;
|
|
18324
|
+
return {
|
|
18325
|
+
versions: rows.map(parseArtifactVersionRow),
|
|
18326
|
+
hasMore: offset + rows.length < total,
|
|
18327
|
+
total
|
|
18328
|
+
};
|
|
18329
|
+
}
|
|
18330
|
+
async listReferencingVersions(input) {
|
|
18331
|
+
const prefix = `artifact:${input.artifactId}`;
|
|
18332
|
+
const rows = await this.sql`
|
|
18333
|
+
SELECT ${this.sql.unsafe(VERSION_COLUMNS)} FROM artifact_versions v
|
|
18334
|
+
WHERE project_id = ${input.projectId} AND (artifact_id, version) IN (
|
|
18335
|
+
SELECT id, latest_version FROM artifacts WHERE project_id = ${input.projectId}
|
|
18336
|
+
) AND EXISTS (
|
|
18337
|
+
SELECT 1 FROM jsonb_array_elements(v.refs) r
|
|
18338
|
+
WHERE left(r ->> 'address', ${prefix.length}::int) = ${prefix}
|
|
18339
|
+
AND substr(r ->> 'address', ${prefix.length + 1}::int, 1) IN ('', '@', '/', '#')
|
|
18340
|
+
)
|
|
18341
|
+
ORDER BY artifact_id
|
|
18342
|
+
`;
|
|
18343
|
+
return rows.map(parseArtifactVersionRow);
|
|
18344
|
+
}
|
|
18345
|
+
async createFrameGrant(record) {
|
|
18346
|
+
const rows = await this.sql`
|
|
18347
|
+
INSERT INTO artifact_frame_grants
|
|
18348
|
+
(project_id, token_hash, artifact_id, version, targets, created_at, expires_at)
|
|
18349
|
+
SELECT ${record.projectId}, ${record.tokenHash}, ${record.artifactId}, ${record.version},
|
|
18350
|
+
${JSON.stringify(record.targets)}::text::jsonb, ${record.createdAt},
|
|
18351
|
+
${record.expiresAt}
|
|
18352
|
+
WHERE EXISTS (
|
|
18353
|
+
SELECT 1 FROM artifacts WHERE project_id = ${record.projectId} AND id = ${record.artifactId}
|
|
18354
|
+
)
|
|
18355
|
+
RETURNING token_hash
|
|
18356
|
+
`;
|
|
18357
|
+
if (rows.length === 0)
|
|
18358
|
+
throw artifactStorageErrors.notFound(record.artifactId);
|
|
18359
|
+
}
|
|
18360
|
+
async findFrameGrant(input) {
|
|
18361
|
+
const [row] = await this.sql`
|
|
18362
|
+
SELECT ${this.sql.unsafe(FRAME_GRANT_COLUMNS)} FROM artifact_frame_grants
|
|
18363
|
+
WHERE project_id = ${input.projectId} AND token_hash = ${input.tokenHash}
|
|
18364
|
+
`;
|
|
18365
|
+
return row ? parseArtifactFrameGrantRow(row) : null;
|
|
18366
|
+
}
|
|
18367
|
+
async deleteExpiredFrameGrants(input) {
|
|
18368
|
+
const rows = await this.sql`
|
|
18369
|
+
DELETE FROM artifact_frame_grants
|
|
18370
|
+
WHERE (project_id, token_hash) IN (
|
|
18371
|
+
SELECT project_id, token_hash FROM artifact_frame_grants
|
|
18372
|
+
WHERE project_id = ${input.projectId} AND expires_at < ${input.expiredBefore}
|
|
18373
|
+
ORDER BY expires_at LIMIT ${input.limit}
|
|
18374
|
+
)
|
|
18375
|
+
RETURNING token_hash
|
|
18376
|
+
`;
|
|
18377
|
+
return rows.length;
|
|
18378
|
+
}
|
|
18379
|
+
async delete(input) {
|
|
18380
|
+
const rows = await this.sql`
|
|
18381
|
+
DELETE FROM artifacts WHERE project_id = ${input.projectId} AND id = ${input.id}
|
|
18382
|
+
RETURNING id
|
|
18383
|
+
`;
|
|
18384
|
+
return rows.length > 0;
|
|
18385
|
+
}
|
|
18386
|
+
async deleteVersionsByOrigin(input) {
|
|
18387
|
+
return runPgTransaction(this.sql, async (tx) => {
|
|
18388
|
+
const locked = await tx`
|
|
18389
|
+
SELECT id FROM artifacts
|
|
18390
|
+
WHERE project_id = ${input.projectId} AND id IN (
|
|
18391
|
+
SELECT artifact_id FROM artifact_versions
|
|
18392
|
+
WHERE project_id = ${input.projectId} AND origin_run_id = ${input.runId}
|
|
18393
|
+
)
|
|
18394
|
+
ORDER BY id
|
|
18395
|
+
FOR UPDATE
|
|
18396
|
+
`;
|
|
18397
|
+
const affected = locked.map((row) => row.id);
|
|
18398
|
+
if (affected.length === 0)
|
|
18399
|
+
return 0;
|
|
18400
|
+
const deleted = await tx`
|
|
18401
|
+
DELETE FROM artifact_versions
|
|
18402
|
+
WHERE project_id = ${input.projectId} AND origin_run_id = ${input.runId}
|
|
18403
|
+
AND artifact_id IN ${tx(affected)}
|
|
18404
|
+
RETURNING version
|
|
18405
|
+
`;
|
|
18406
|
+
await tx`
|
|
18407
|
+
DELETE FROM artifacts a
|
|
18408
|
+
WHERE a.project_id = ${input.projectId} AND a.id IN ${tx(affected)}
|
|
18409
|
+
AND NOT EXISTS (
|
|
18410
|
+
SELECT 1 FROM artifact_versions v
|
|
18411
|
+
WHERE v.project_id = a.project_id AND v.artifact_id = a.id
|
|
18412
|
+
)
|
|
18413
|
+
`;
|
|
18414
|
+
await tx`
|
|
18415
|
+
UPDATE artifacts a
|
|
18416
|
+
SET latest_version = latest.version, title = latest.title, updated_at = latest.created_at
|
|
18417
|
+
FROM (
|
|
18418
|
+
SELECT DISTINCT ON (artifact_id) artifact_id, version, title, created_at
|
|
18419
|
+
FROM artifact_versions
|
|
18420
|
+
WHERE project_id = ${input.projectId} AND artifact_id IN ${tx(affected)}
|
|
18421
|
+
ORDER BY artifact_id, version DESC
|
|
18422
|
+
) latest
|
|
18423
|
+
WHERE a.project_id = ${input.projectId} AND a.id = latest.artifact_id
|
|
18424
|
+
`;
|
|
18425
|
+
return deleted.length;
|
|
18426
|
+
});
|
|
18427
|
+
}
|
|
18428
|
+
}
|
|
18429
|
+
async function insertVersion(sql, projectId, artifactId, version, input) {
|
|
18430
|
+
const [row] = await sql`
|
|
18431
|
+
INSERT INTO artifact_versions (
|
|
18432
|
+
project_id, artifact_id, version, title, entry, files, outline, refs,
|
|
18433
|
+
author_principal_type, author_principal_id, origin_thread_id, origin_run_id, created_at
|
|
18434
|
+
) VALUES (
|
|
18435
|
+
${projectId}, ${artifactId}, ${version}, ${input.title}, ${input.entry},
|
|
18436
|
+
${JSON.stringify(input.files)}::text::jsonb,
|
|
18437
|
+
${JSON.stringify(input.outline)}::text::jsonb,
|
|
18438
|
+
${JSON.stringify(input.references)}::text::jsonb,
|
|
18439
|
+
${input.author.type}, ${input.author.id},
|
|
18440
|
+
${input.origin?.threadId ?? null}, ${input.origin?.runId ?? null}, ${input.createdAt}
|
|
18441
|
+
)
|
|
18442
|
+
RETURNING ${sql.unsafe(VERSION_COLUMNS)}
|
|
18443
|
+
`;
|
|
18444
|
+
return parseArtifactVersionRow(row);
|
|
18445
|
+
}
|
|
18446
|
+
|
|
17535
18447
|
// src/pg-client.ts
|
|
17536
18448
|
import { isIP } from "net";
|
|
17537
18449
|
import { connect as connectTls } from "tls";
|
|
@@ -19956,6 +20868,23 @@ class PgWorkflowRunStorage {
|
|
|
19956
20868
|
`;
|
|
19957
20869
|
return row ? rowToWorkflowRunRecord(row) : null;
|
|
19958
20870
|
}
|
|
20871
|
+
async getByExecutionIds(params) {
|
|
20872
|
+
const executionIds = [...new Set(params.executionIds)];
|
|
20873
|
+
if (executionIds.length === 0) {
|
|
20874
|
+
return [];
|
|
20875
|
+
}
|
|
20876
|
+
const rows = await this.sql`
|
|
20877
|
+
SELECT *
|
|
20878
|
+
FROM workflow_runs
|
|
20879
|
+
WHERE project_id = ${params.projectId}
|
|
20880
|
+
AND execution_id = ANY(${this.sql.array(executionIds)}::text[])
|
|
20881
|
+
`;
|
|
20882
|
+
const byExecution = new Map(rows.map((row) => [row.execution_id, row]));
|
|
20883
|
+
return executionIds.flatMap((executionId) => {
|
|
20884
|
+
const row = byExecution.get(executionId);
|
|
20885
|
+
return row ? [rowToWorkflowRunRecord(row)] : [];
|
|
20886
|
+
});
|
|
20887
|
+
}
|
|
19959
20888
|
async list(input) {
|
|
19960
20889
|
if (hasEmptyStatuses(input) || input.workflowIds?.length === 0) {
|
|
19961
20890
|
return {
|
|
@@ -20559,6 +21488,23 @@ class PgShareGrantStorage {
|
|
|
20559
21488
|
`;
|
|
20560
21489
|
return row ? recordFromRow(row) : null;
|
|
20561
21490
|
}
|
|
21491
|
+
async getByIds(input) {
|
|
21492
|
+
const ids = [...new Set(input.ids)];
|
|
21493
|
+
if (ids.length === 0) {
|
|
21494
|
+
return [];
|
|
21495
|
+
}
|
|
21496
|
+
const rows = await this.sql`
|
|
21497
|
+
SELECT *
|
|
21498
|
+
FROM share_grants
|
|
21499
|
+
WHERE project_id = ${input.projectId}
|
|
21500
|
+
AND id = ANY(${this.sql.array(ids)}::text[])
|
|
21501
|
+
`;
|
|
21502
|
+
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
21503
|
+
return ids.flatMap((id) => {
|
|
21504
|
+
const row = byId.get(id);
|
|
21505
|
+
return row ? [recordFromRow(row)] : [];
|
|
21506
|
+
});
|
|
21507
|
+
}
|
|
20562
21508
|
async list(input) {
|
|
20563
21509
|
const normalized = normalizeListShareGrantsInput(input);
|
|
20564
21510
|
const where = ["project_id = $1"];
|
|
@@ -20778,6 +21724,7 @@ class PostgresStorage {
|
|
|
20778
21724
|
auth;
|
|
20779
21725
|
executions;
|
|
20780
21726
|
agents;
|
|
21727
|
+
artifacts;
|
|
20781
21728
|
aiUsage;
|
|
20782
21729
|
aiCosts;
|
|
20783
21730
|
aiLimits;
|
|
@@ -20795,6 +21742,7 @@ class PostgresStorage {
|
|
|
20795
21742
|
shareSessions;
|
|
20796
21743
|
fileUploadSessions;
|
|
20797
21744
|
fileDownloadGrants;
|
|
21745
|
+
activity;
|
|
20798
21746
|
migrators;
|
|
20799
21747
|
sql;
|
|
20800
21748
|
schemaName;
|
|
@@ -20836,6 +21784,7 @@ class PostgresStorage {
|
|
|
20836
21784
|
this.auth = createAuthOperationScope(stores.auth, scope);
|
|
20837
21785
|
this.executions = createOperationScopedFacade(stores.executions, scope);
|
|
20838
21786
|
this.agents = createAgentOperationScope(stores.agents, scope);
|
|
21787
|
+
this.artifacts = createOperationScopedFacade(stores.artifacts, scope);
|
|
20839
21788
|
this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope);
|
|
20840
21789
|
this.aiCosts = createOperationScopedFacade(stores.aiCosts, scope);
|
|
20841
21790
|
this.aiLimits = createOperationScopedFacade(stores.aiLimits, scope);
|
|
@@ -20853,6 +21802,7 @@ class PostgresStorage {
|
|
|
20853
21802
|
this.shareSessions = createOperationScopedFacade(stores.shareSessions, scope);
|
|
20854
21803
|
this.fileUploadSessions = createOperationScopedFacade(stores.fileUploadSessions, scope);
|
|
20855
21804
|
this.fileDownloadGrants = createOperationScopedFacade(stores.fileDownloadGrants, scope);
|
|
21805
|
+
this.activity = createOperationScopedFacade(stores.activity, scope);
|
|
20856
21806
|
registerPostgresStorageTestingAdapter(this, (durationMs) => stores.connectorConnections.advanceTimeForTesting(durationMs));
|
|
20857
21807
|
}
|
|
20858
21808
|
async ping() {
|
|
@@ -20945,6 +21895,7 @@ function createPostgresStores(sql, options) {
|
|
|
20945
21895
|
auth,
|
|
20946
21896
|
executions,
|
|
20947
21897
|
agents: new PgAgentStorage({ sql, executions }),
|
|
21898
|
+
artifacts: new PgArtifactStorage(sql),
|
|
20948
21899
|
aiUsage: new PgAiUsageStorage(sql),
|
|
20949
21900
|
aiCosts: new PgAiCostStorage(sql),
|
|
20950
21901
|
aiLimits: new PgAiLimitStorage(sql),
|
|
@@ -20961,7 +21912,8 @@ function createPostgresStores(sql, options) {
|
|
|
20961
21912
|
shareGrants,
|
|
20962
21913
|
shareSessions,
|
|
20963
21914
|
fileUploadSessions: new PgFileUploadSessionStorage(sql),
|
|
20964
|
-
fileDownloadGrants: new PgFileDownloadGrantStorage(sql)
|
|
21915
|
+
fileDownloadGrants: new PgFileDownloadGrantStorage(sql),
|
|
21916
|
+
activity: new PgActivityStorage(sql)
|
|
20965
21917
|
};
|
|
20966
21918
|
}
|
|
20967
21919
|
function resolveTimeoutMillis(value, label) {
|
|
@@ -20978,4 +21930,4 @@ export {
|
|
|
20978
21930
|
migratePostgresStorage
|
|
20979
21931
|
};
|
|
20980
21932
|
|
|
20981
|
-
//# debugId=
|
|
21933
|
+
//# debugId=E2B2D974990D4A3064756E2164756E21
|