@sixb/sqlite 0.1.19 → 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/action-run-storage.d.ts +4 -0
- package/dist/action-run-storage.d.ts.map +1 -1
- package/dist/activity-storage.d.ts +20 -0
- package/dist/activity-storage.d.ts.map +1 -0
- package/dist/agents/agent-storage.d.ts.map +1 -1
- package/dist/agents/threads.d.ts +4 -0
- package/dist/agents/threads.d.ts.map +1 -1
- package/dist/ai-limit-storage.d.ts.map +1 -1
- package/dist/ai-usage-storage.d.ts.map +1 -1
- package/dist/artifact-storage.d.ts +22 -0
- package/dist/artifact-storage.d.ts.map +1 -0
- package/dist/auth-storage/auth-storage.d.ts.map +1 -1
- package/dist/auth-storage/magic-links.d.ts.map +1 -1
- package/dist/auth-storage/oidc-attempts.d.ts.map +1 -1
- package/dist/auth-storage/rows.d.ts +0 -3
- package/dist/auth-storage/rows.d.ts.map +1 -1
- package/dist/auth-storage/sessions.d.ts +2 -11
- package/dist/auth-storage/sessions.d.ts.map +1 -1
- package/dist/auth-storage/shared.d.ts +0 -2
- package/dist/auth-storage/shared.d.ts.map +1 -1
- package/dist/execution-storage.d.ts +2 -1
- package/dist/execution-storage.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +933 -128
- package/dist/index.js.map +32 -30
- package/dist/migrations.d.ts +2 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/objects/storage.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/pipeline-run-storage.d.ts.map +1 -1
- package/dist/projection-run-storage.d.ts.map +1 -1
- package/dist/rules-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/dist/share-session-storage.d.ts.map +1 -1
- package/dist/sync-run-storage.d.ts.map +1 -1
- package/dist/timeseries-storage.d.ts.map +1 -1
- package/dist/transactions.d.ts +0 -1
- package/dist/transactions.d.ts.map +1 -1
- package/dist/webhook-run-storage.d.ts.map +1 -1
- package/dist/workflow-intervention-storage.d.ts.map +1 -1
- package/dist/workflow-run-storage.d.ts +4 -0
- package/dist/workflow-run-storage.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/action-run-storage.ts +27 -5
- package/src/activity-storage.ts +588 -0
- package/src/agents/agent-storage.ts +0 -5
- package/src/agents/threads.ts +27 -0
- package/src/ai-limit-storage.ts +0 -2
- package/src/ai-usage-storage.ts +0 -4
- package/src/artifact-storage.ts +356 -0
- package/src/auth-storage/auth-storage.ts +0 -23
- package/src/auth-storage/magic-links.ts +1 -6
- package/src/auth-storage/oidc-attempts.ts +1 -6
- package/src/auth-storage/rows.ts +0 -6
- package/src/auth-storage/sessions.ts +21 -42
- package/src/auth-storage/shared.ts +3 -20
- package/src/execution-storage.ts +56 -0
- package/src/index.ts +11 -10
- package/src/migrations/061-drop-auth-audience.sql +16 -0
- package/src/migrations/062-artifacts.sql +71 -0
- package/src/migrations/063-drop-output-diagnostics.sql +19 -0
- package/src/migrations/064-activity.sql +61 -0
- package/src/migrations/065-activity-timeline.sql +19 -0
- package/src/migrations/066-activity-interactions.sql +20 -0
- package/src/migrations.ts +36 -0
- package/src/objects/storage.ts +0 -5
- package/src/ontology-storage/sources.ts +41 -0
- package/src/pipeline-run-storage.ts +0 -5
- package/src/projection-run-storage.ts +0 -5
- package/src/rules-storage.ts +0 -5
- package/src/share-grant-storage.ts +25 -2
- package/src/share-session-storage.ts +0 -2
- package/src/sync-run-storage.ts +0 -5
- package/src/timeseries-storage.ts +0 -5
- package/src/transactions.ts +8 -8
- package/src/webhook-run-storage.ts +0 -3
- package/src/workflow-intervention-storage.ts +0 -5
- package/src/workflow-run-storage.ts +27 -5
package/dist/index.js
CHANGED
|
@@ -28,6 +28,11 @@ import { assertActionRunExecution } from "@sixb/core/internal/action-run-storage
|
|
|
28
28
|
import { normalizeRecordActionRunInput, resolveActionRunEffects } from "@sixb/core/internal/storage";
|
|
29
29
|
import { ActionRunError } from "@sixb/core/storage";
|
|
30
30
|
|
|
31
|
+
// src/transactions.ts
|
|
32
|
+
import { Database as SqliteDatabase } from "bun:sqlite";
|
|
33
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
34
|
+
import { dirname as dirname2 } from "path";
|
|
35
|
+
|
|
31
36
|
// src/migrations.ts
|
|
32
37
|
import { Database } from "bun:sqlite";
|
|
33
38
|
import { createHash } from "crypto";
|
|
@@ -5142,6 +5147,230 @@ CREATE INDEX idx_action_runs_project_status_started
|
|
|
5142
5147
|
ON action_runs(project_id, status, started_at DESC);
|
|
5143
5148
|
`;
|
|
5144
5149
|
|
|
5150
|
+
// src/migrations/061-drop-auth-audience.sql
|
|
5151
|
+
var _061_drop_auth_audience_default = `-- A project has one browser app, so sessions, magic links and sign-in attempts no longer name the
|
|
5152
|
+
-- one they belong to. Atlas's browser sessions end here: their cookie is no longer read. Native
|
|
5153
|
+
-- sessions carried Atlas's name only because the column needed a value, so they stay.
|
|
5154
|
+
UPDATE auth_sessions
|
|
5155
|
+
SET revoked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
5156
|
+
WHERE audience = 'atlas'
|
|
5157
|
+
AND refresh_token_hash IS NULL
|
|
5158
|
+
AND revoked_at IS NULL;
|
|
5159
|
+
|
|
5160
|
+
DROP INDEX idx_auth_sessions_user_active;
|
|
5161
|
+
ALTER TABLE auth_sessions DROP COLUMN audience;
|
|
5162
|
+
CREATE INDEX idx_auth_sessions_user_active
|
|
5163
|
+
ON auth_sessions(project_id, user_id, revoked_at, expires_at);
|
|
5164
|
+
|
|
5165
|
+
ALTER TABLE auth_magic_links DROP COLUMN audience;
|
|
5166
|
+
ALTER TABLE auth_oidc_authorization_attempts DROP COLUMN audience;
|
|
5167
|
+
`;
|
|
5168
|
+
|
|
5169
|
+
// src/migrations/062-artifacts.sql
|
|
5170
|
+
var _062_artifacts_default = `-- Artifacts: versioned documents, sheets, decks, and files. An artifact's kind never
|
|
5171
|
+
-- changes. Versions take \`next_version\`, which only grows, so a number is never reused after its
|
|
5172
|
+
-- version is deleted. Timestamps are ISO-8601 UTC text and compare lexicographically.
|
|
5173
|
+
CREATE TABLE artifacts (
|
|
5174
|
+
project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
|
|
5175
|
+
id TEXT NOT NULL CHECK (length(trim(id)) > 0),
|
|
5176
|
+
kind TEXT NOT NULL CHECK (kind IN ('document', 'sheet', 'slides', 'file')),
|
|
5177
|
+
title TEXT NOT NULL CHECK (length(trim(title)) > 0),
|
|
5178
|
+
owner_principal_type TEXT NOT NULL
|
|
5179
|
+
CHECK (owner_principal_type IN ('user', 'serviceAccount', 'system')),
|
|
5180
|
+
owner_principal_id TEXT NOT NULL CHECK (length(owner_principal_id) > 0),
|
|
5181
|
+
latest_version INTEGER NOT NULL CHECK (latest_version > 0),
|
|
5182
|
+
next_version INTEGER NOT NULL CHECK (next_version > latest_version),
|
|
5183
|
+
created_at TEXT NOT NULL,
|
|
5184
|
+
updated_at TEXT NOT NULL,
|
|
5185
|
+
PRIMARY KEY (project_id, id)
|
|
5186
|
+
);
|
|
5187
|
+
|
|
5188
|
+
CREATE INDEX idx_artifacts_project_updated ON artifacts (project_id, updated_at DESC, id);
|
|
5189
|
+
CREATE INDEX idx_artifacts_owner_updated
|
|
5190
|
+
ON artifacts (project_id, owner_principal_type, owner_principal_id, updated_at DESC, id);
|
|
5191
|
+
|
|
5192
|
+
-- \`refs\`, not \`references\`: that word is reserved in SQL.
|
|
5193
|
+
CREATE TABLE artifact_versions (
|
|
5194
|
+
project_id TEXT NOT NULL,
|
|
5195
|
+
artifact_id TEXT NOT NULL,
|
|
5196
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
5197
|
+
title TEXT NOT NULL CHECK (length(trim(title)) > 0),
|
|
5198
|
+
entry TEXT NOT NULL CHECK (length(entry) > 0),
|
|
5199
|
+
files TEXT NOT NULL CHECK (json_type(files) = 'array'),
|
|
5200
|
+
outline TEXT NOT NULL CHECK (json_type(outline) = 'object'),
|
|
5201
|
+
refs TEXT NOT NULL CHECK (json_type(refs) = 'array'),
|
|
5202
|
+
author_principal_type TEXT NOT NULL
|
|
5203
|
+
CHECK (author_principal_type IN ('user', 'serviceAccount', 'system')),
|
|
5204
|
+
author_principal_id TEXT NOT NULL CHECK (length(author_principal_id) > 0),
|
|
5205
|
+
origin_thread_id TEXT,
|
|
5206
|
+
origin_run_id TEXT,
|
|
5207
|
+
created_at TEXT NOT NULL,
|
|
5208
|
+
PRIMARY KEY (project_id, artifact_id, version),
|
|
5209
|
+
FOREIGN KEY (project_id, artifact_id) REFERENCES artifacts (project_id, id) ON DELETE CASCADE,
|
|
5210
|
+
CHECK ((origin_thread_id IS NULL) = (origin_run_id IS NULL))
|
|
5211
|
+
);
|
|
5212
|
+
|
|
5213
|
+
CREATE INDEX idx_artifact_versions_origin_run
|
|
5214
|
+
ON artifact_versions (project_id, origin_run_id) WHERE origin_run_id IS NOT NULL;
|
|
5215
|
+
CREATE INDEX idx_artifact_versions_origin_thread
|
|
5216
|
+
ON artifact_versions (project_id, origin_thread_id, artifact_id) WHERE origin_thread_id IS NOT NULL;
|
|
5217
|
+
|
|
5218
|
+
-- Short-lived links that a sandboxed frame loads a deck's files through. Only the SHA-256 of each
|
|
5219
|
+
-- link's token is stored. \`targets\` is what the frame may show from other artifacts, decided for
|
|
5220
|
+
-- its reader when the link was issued: a JSON array of { address, artifactId, version }.
|
|
5221
|
+
CREATE TABLE artifact_frame_grants (
|
|
5222
|
+
project_id TEXT NOT NULL,
|
|
5223
|
+
token_hash TEXT NOT NULL CHECK (
|
|
5224
|
+
length(token_hash) = 64
|
|
5225
|
+
AND token_hash = lower(token_hash)
|
|
5226
|
+
AND token_hash NOT GLOB '*[^0-9a-f]*'
|
|
5227
|
+
),
|
|
5228
|
+
artifact_id TEXT NOT NULL,
|
|
5229
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
5230
|
+
targets TEXT NOT NULL CHECK (json_type(targets) = 'array'),
|
|
5231
|
+
created_at TEXT NOT NULL,
|
|
5232
|
+
expires_at TEXT NOT NULL,
|
|
5233
|
+
PRIMARY KEY (project_id, token_hash),
|
|
5234
|
+
FOREIGN KEY (project_id, artifact_id) REFERENCES artifacts (project_id, id) ON DELETE CASCADE,
|
|
5235
|
+
CHECK (expires_at > created_at)
|
|
5236
|
+
);
|
|
5237
|
+
|
|
5238
|
+
CREATE INDEX idx_artifact_frame_grants_expiry ON artifact_frame_grants (project_id, expires_at);
|
|
5239
|
+
-- Deleting an artifact cascades here.
|
|
5240
|
+
CREATE INDEX idx_artifact_frame_grants_artifact ON artifact_frame_grants (project_id, artifact_id);
|
|
5241
|
+
`;
|
|
5242
|
+
|
|
5243
|
+
// src/migrations/063-drop-output-diagnostics.sql
|
|
5244
|
+
var _063_drop_output_diagnostics_default = `-- The Agent delivers files by publishing artifacts, so a run no longer collects an output
|
|
5245
|
+
-- directory or reports on it. Stored \`output_*\` diagnostics go; a run left with none stores none.
|
|
5246
|
+
UPDATE agent_runs
|
|
5247
|
+
SET diagnostics = (
|
|
5248
|
+
SELECT CASE WHEN count(*) = 0 THEN NULL ELSE json_group_array(json(value)) END
|
|
5249
|
+
FROM (
|
|
5250
|
+
SELECT value
|
|
5251
|
+
FROM json_each(agent_runs.diagnostics)
|
|
5252
|
+
WHERE json_extract(value, '$.code') NOT LIKE 'output\\_%' ESCAPE '\\'
|
|
5253
|
+
ORDER BY key
|
|
5254
|
+
)
|
|
5255
|
+
)
|
|
5256
|
+
WHERE diagnostics IS NOT NULL
|
|
5257
|
+
AND json_valid(diagnostics)
|
|
5258
|
+
AND EXISTS (
|
|
5259
|
+
SELECT 1
|
|
5260
|
+
FROM json_each(agent_runs.diagnostics)
|
|
5261
|
+
WHERE json_extract(value, '$.code') LIKE 'output\\_%' ESCAPE '\\'
|
|
5262
|
+
);
|
|
5263
|
+
`;
|
|
5264
|
+
|
|
5265
|
+
// src/migrations/064-activity.sql
|
|
5266
|
+
var _064_activity_default = `-- App activity: one source per verified session, the visits it opened, and their page views.
|
|
5267
|
+
-- Foreground activity moves a visit's \`last_seen_at\` and stores no row, unless it opens a visit.
|
|
5268
|
+
-- A session id names an \`auth_sessions\` row for a user and a \`share_sessions\` row for a share; it
|
|
5269
|
+
-- is not a secret, and no foreign key holds it, so history outlives its sessions. Timestamps are
|
|
5270
|
+
-- ISO-8601 UTC text and compare lexicographically.
|
|
5271
|
+
CREATE TABLE activity_sources (
|
|
5272
|
+
project_id TEXT NOT NULL,
|
|
5273
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'share')),
|
|
5274
|
+
session_id TEXT NOT NULL CHECK (length(session_id) > 0),
|
|
5275
|
+
visit_id TEXT NOT NULL,
|
|
5276
|
+
last_seen_at TEXT NOT NULL,
|
|
5277
|
+
sequence INTEGER NOT NULL CHECK (sequence >= 0),
|
|
5278
|
+
window_start TEXT NOT NULL,
|
|
5279
|
+
window_count INTEGER NOT NULL CHECK (window_count >= 0),
|
|
5280
|
+
PRIMARY KEY (project_id, actor_type, session_id)
|
|
5281
|
+
);
|
|
5282
|
+
|
|
5283
|
+
CREATE TABLE activity_visits (
|
|
5284
|
+
project_id TEXT NOT NULL,
|
|
5285
|
+
id TEXT NOT NULL,
|
|
5286
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'share')),
|
|
5287
|
+
user_id TEXT,
|
|
5288
|
+
grant_id TEXT,
|
|
5289
|
+
session_id TEXT NOT NULL CHECK (length(session_id) > 0),
|
|
5290
|
+
started_at TEXT NOT NULL,
|
|
5291
|
+
last_seen_at TEXT NOT NULL CHECK (last_seen_at >= started_at),
|
|
5292
|
+
page_views INTEGER NOT NULL CHECK (page_views >= 0),
|
|
5293
|
+
attribution TEXT CHECK (attribution IS NULL OR json_type(attribution) = 'object'),
|
|
5294
|
+
PRIMARY KEY (project_id, id),
|
|
5295
|
+
CHECK (
|
|
5296
|
+
(actor_type = 'user' AND user_id IS NOT NULL AND grant_id IS NULL)
|
|
5297
|
+
OR (actor_type = 'share' AND user_id IS NULL AND grant_id IS NOT NULL)
|
|
5298
|
+
)
|
|
5299
|
+
);
|
|
5300
|
+
|
|
5301
|
+
-- A period reads the visits it overlaps. A visit lasts less than a day, so they started in
|
|
5302
|
+
-- \`[from - 1 day, to)\`: both period indexes bound \`started_at\` on both sides. \`last_seen_at\` is
|
|
5303
|
+
-- left out of every index, so the update each batch makes touches none of them. The user index
|
|
5304
|
+
-- also serves deleting one user's history.
|
|
5305
|
+
CREATE INDEX idx_activity_visits_started
|
|
5306
|
+
ON activity_visits (project_id, started_at, id);
|
|
5307
|
+
CREATE INDEX idx_activity_visits_user
|
|
5308
|
+
ON activity_visits (project_id, user_id, started_at, id);
|
|
5309
|
+
-- A session's visits, to join them with the session and the executions it authorized.
|
|
5310
|
+
CREATE INDEX idx_activity_visits_session
|
|
5311
|
+
ON activity_visits (project_id, actor_type, session_id);
|
|
5312
|
+
|
|
5313
|
+
CREATE TABLE activity_page_views (
|
|
5314
|
+
project_id TEXT NOT NULL,
|
|
5315
|
+
visit_id TEXT NOT NULL,
|
|
5316
|
+
sequence INTEGER NOT NULL CHECK (sequence > 0),
|
|
5317
|
+
id TEXT NOT NULL,
|
|
5318
|
+
route TEXT NOT NULL,
|
|
5319
|
+
received_at TEXT NOT NULL,
|
|
5320
|
+
attribution TEXT CHECK (attribution IS NULL OR json_type(attribution) = 'object'),
|
|
5321
|
+
PRIMARY KEY (project_id, visit_id, sequence),
|
|
5322
|
+
FOREIGN KEY (project_id, visit_id) REFERENCES activity_visits (project_id, id) ON DELETE CASCADE
|
|
5323
|
+
);
|
|
5324
|
+
|
|
5325
|
+
-- Finds a retried page view by the id its browser chose.
|
|
5326
|
+
CREATE INDEX idx_activity_page_views_id ON activity_page_views (project_id, id);
|
|
5327
|
+
`;
|
|
5328
|
+
|
|
5329
|
+
// src/migrations/065-activity-timeline.sql
|
|
5330
|
+
var _065_activity_timeline_default = `-- A visit's timeline reads what its session started: the roots it authorized in a window, then
|
|
5331
|
+
-- everything they caused. A signed-in session is the credential of its roots, a shared link's
|
|
5332
|
+
-- session their delegation; a child names its parent. Agent executions copy their parent's
|
|
5333
|
+
-- session: the root indexes hold only executions without a parent, so they never see those, and
|
|
5334
|
+
-- end with \`id\` so the root read's order needs no sort.
|
|
5335
|
+
CREATE INDEX idx_executions_session_roots
|
|
5336
|
+
ON executions (project_id, authority_session_id, created_at, id)
|
|
5337
|
+
WHERE authority_session_id IS NOT NULL AND parent_execution_id IS NULL;
|
|
5338
|
+
CREATE INDEX idx_executions_delegation_roots
|
|
5339
|
+
ON executions (project_id, authority_delegation_session_id, created_at, id)
|
|
5340
|
+
WHERE authority_delegation_session_id IS NOT NULL AND parent_execution_id IS NULL;
|
|
5341
|
+
CREATE INDEX idx_executions_parent
|
|
5342
|
+
ON executions (project_id, parent_execution_id)
|
|
5343
|
+
WHERE parent_execution_id IS NOT NULL;
|
|
5344
|
+
|
|
5345
|
+
-- A user's connection history lists all their sessions, revoked and expired ones too, newest
|
|
5346
|
+
-- first.
|
|
5347
|
+
CREATE INDEX idx_auth_sessions_user_created
|
|
5348
|
+
ON auth_sessions (project_id, user_id, created_at, id);
|
|
5349
|
+
`;
|
|
5350
|
+
|
|
5351
|
+
// src/migrations/066-activity-interactions.sql
|
|
5352
|
+
var _066_activity_interactions_default = `-- Interactions an app marks, such as a button: their name and route, never a value. They share
|
|
5353
|
+
-- the visit's sequence with its page views, so a visit reads both in the order they happened, and
|
|
5354
|
+
-- cascade with the visit, which counts them as it counts its page views.
|
|
5355
|
+
ALTER TABLE activity_visits
|
|
5356
|
+
ADD COLUMN interactions INTEGER NOT NULL DEFAULT 0 CHECK (interactions >= 0);
|
|
5357
|
+
|
|
5358
|
+
CREATE TABLE activity_interactions (
|
|
5359
|
+
project_id TEXT NOT NULL,
|
|
5360
|
+
visit_id TEXT NOT NULL,
|
|
5361
|
+
sequence INTEGER NOT NULL CHECK (sequence > 0),
|
|
5362
|
+
id TEXT NOT NULL,
|
|
5363
|
+
name TEXT NOT NULL CHECK (length(name) BETWEEN 1 AND 100),
|
|
5364
|
+
route TEXT NOT NULL,
|
|
5365
|
+
received_at TEXT NOT NULL,
|
|
5366
|
+
PRIMARY KEY (project_id, visit_id, sequence),
|
|
5367
|
+
FOREIGN KEY (project_id, visit_id) REFERENCES activity_visits (project_id, id) ON DELETE CASCADE
|
|
5368
|
+
);
|
|
5369
|
+
|
|
5370
|
+
-- Finds a retried interaction by the id its browser chose.
|
|
5371
|
+
CREATE INDEX idx_activity_interactions_id ON activity_interactions (project_id, id);
|
|
5372
|
+
`;
|
|
5373
|
+
|
|
5145
5374
|
// src/migrations.ts
|
|
5146
5375
|
var MIGRATIONS_TABLE_SQL = `
|
|
5147
5376
|
CREATE TABLE IF NOT EXISTS sixb_migrations (
|
|
@@ -5249,7 +5478,13 @@ var sqliteStorageMigrations = defineMigrations({
|
|
|
5249
5478
|
sqliteSql("057-reranking-model-kind", _057_reranking_model_kind_default),
|
|
5250
5479
|
sqliteSql("058-file-download-grants", _058_file_download_grants_default),
|
|
5251
5480
|
sqliteSql("059-drained-source-versions", _059_drained_source_versions_default),
|
|
5252
|
-
sqliteSql("060-terminal-action-runs", _060_terminal_action_runs_default)
|
|
5481
|
+
sqliteSql("060-terminal-action-runs", _060_terminal_action_runs_default),
|
|
5482
|
+
sqliteSql("061-drop-auth-audience", _061_drop_auth_audience_default),
|
|
5483
|
+
sqliteSql("062-artifacts", _062_artifacts_default),
|
|
5484
|
+
sqliteSql("063-drop-output-diagnostics", _063_drop_output_diagnostics_default),
|
|
5485
|
+
sqliteSql("064-activity", _064_activity_default),
|
|
5486
|
+
sqliteSql("065-activity-timeline", _065_activity_timeline_default),
|
|
5487
|
+
sqliteSql("066-activity-interactions", _066_activity_interactions_default)
|
|
5253
5488
|
]
|
|
5254
5489
|
});
|
|
5255
5490
|
function sqliteStoragePath(basePath) {
|
|
@@ -5263,6 +5498,20 @@ function sqliteSql(id, sqlText) {
|
|
|
5263
5498
|
function sqliteStep(id, up, options = {}) {
|
|
5264
5499
|
return step(id, up, options);
|
|
5265
5500
|
}
|
|
5501
|
+
var freshSchema;
|
|
5502
|
+
function freshSqliteDatabase() {
|
|
5503
|
+
if (!freshSchema) {
|
|
5504
|
+
const template = new Database(":memory:");
|
|
5505
|
+
try {
|
|
5506
|
+
template.run("PRAGMA foreign_keys = ON");
|
|
5507
|
+
installFreshSqliteSchema(template);
|
|
5508
|
+
freshSchema = template.serialize();
|
|
5509
|
+
} finally {
|
|
5510
|
+
template.close();
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5513
|
+
return Database.deserialize(freshSchema);
|
|
5514
|
+
}
|
|
5266
5515
|
function installFreshSqliteSchema(db) {
|
|
5267
5516
|
for (const migration of sqliteStorageMigrations.steps) {
|
|
5268
5517
|
const installed = migration.up(db);
|
|
@@ -5480,28 +5729,20 @@ function rollbackQuietly(db) {
|
|
|
5480
5729
|
}
|
|
5481
5730
|
|
|
5482
5731
|
// src/transactions.ts
|
|
5483
|
-
import { Database as SqliteDatabase } from "bun:sqlite";
|
|
5484
|
-
import { mkdirSync as mkdirSync2 } from "fs";
|
|
5485
|
-
import { dirname as dirname2 } from "path";
|
|
5486
5732
|
function openSqliteStoreConnection(options = {}) {
|
|
5487
5733
|
if (options.connection) {
|
|
5488
5734
|
return {
|
|
5489
5735
|
db: options.connection.db,
|
|
5490
|
-
ownsConnection: false
|
|
5491
|
-
installFreshSchema: false
|
|
5736
|
+
ownsConnection: false
|
|
5492
5737
|
};
|
|
5493
5738
|
}
|
|
5494
5739
|
const path = options.path ?? ":memory:";
|
|
5495
5740
|
if (path !== ":memory:" && !options.readonly)
|
|
5496
5741
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
5497
|
-
const db = new SqliteDatabase(path, options.readonly ? { readonly: true } : undefined);
|
|
5742
|
+
const db = path === ":memory:" && !options.readonly ? freshSqliteDatabase() : new SqliteDatabase(path, options.readonly ? { readonly: true } : undefined);
|
|
5498
5743
|
db.run("PRAGMA foreign_keys = ON");
|
|
5499
5744
|
db.run("PRAGMA busy_timeout = 5000");
|
|
5500
|
-
return {
|
|
5501
|
-
db,
|
|
5502
|
-
ownsConnection: true,
|
|
5503
|
-
installFreshSchema: path === ":memory:" && !options.readonly
|
|
5504
|
-
};
|
|
5745
|
+
return { db, ownsConnection: true };
|
|
5505
5746
|
}
|
|
5506
5747
|
function closeSqliteStoreConnection(connection) {
|
|
5507
5748
|
if (connection.ownsConnection) {
|
|
@@ -5582,9 +5823,6 @@ class SqliteActionRunStorage {
|
|
|
5582
5823
|
this.connection = openSqliteStoreConnection(options);
|
|
5583
5824
|
this.db = this.connection.db;
|
|
5584
5825
|
this.executions = options.executions;
|
|
5585
|
-
if (this.connection.installFreshSchema) {
|
|
5586
|
-
installFreshSqliteSchema(this.db);
|
|
5587
|
-
}
|
|
5588
5826
|
}
|
|
5589
5827
|
async record(input) {
|
|
5590
5828
|
const record = normalizeRecordActionRunInput(input);
|
|
@@ -5646,6 +5884,23 @@ class SqliteActionRunStorage {
|
|
|
5646
5884
|
const row = this.db.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
|
|
5647
5885
|
return row ? rowToActionRunRecord(row) : null;
|
|
5648
5886
|
}
|
|
5887
|
+
async getByExecutionIds(params) {
|
|
5888
|
+
const executionIds = [...new Set(params.executionIds)];
|
|
5889
|
+
if (executionIds.length === 0) {
|
|
5890
|
+
return [];
|
|
5891
|
+
}
|
|
5892
|
+
const rows = this.db.query(`
|
|
5893
|
+
SELECT *
|
|
5894
|
+
FROM action_runs
|
|
5895
|
+
WHERE project_id = ?
|
|
5896
|
+
AND execution_id IN (SELECT value FROM json_each(?))
|
|
5897
|
+
`).all(params.projectId, JSON.stringify(executionIds));
|
|
5898
|
+
const byExecution = new Map(rows.map((row) => [row.execution_id, row]));
|
|
5899
|
+
return executionIds.flatMap((executionId) => {
|
|
5900
|
+
const row = byExecution.get(executionId);
|
|
5901
|
+
return row ? [rowToActionRunRecord(row)] : [];
|
|
5902
|
+
});
|
|
5903
|
+
}
|
|
5649
5904
|
async list(input) {
|
|
5650
5905
|
if (input.statuses && input.statuses.length === 0 || input.actionIds?.length === 0) {
|
|
5651
5906
|
return {
|
|
@@ -5817,6 +6072,342 @@ function rowToActionSubject(row) {
|
|
|
5817
6072
|
};
|
|
5818
6073
|
}
|
|
5819
6074
|
|
|
6075
|
+
// src/activity-storage.ts
|
|
6076
|
+
import {
|
|
6077
|
+
ACTIVITY_MAX_VISIT_MS,
|
|
6078
|
+
activityActor,
|
|
6079
|
+
activityCalendarDays,
|
|
6080
|
+
parseActivityAttribution,
|
|
6081
|
+
planActivityAppend,
|
|
6082
|
+
serializeActivityAttribution,
|
|
6083
|
+
validateActivityAppend
|
|
6084
|
+
} from "@sixb/core/internal/storage";
|
|
6085
|
+
var VISIT_COLUMNS = `
|
|
6086
|
+
v.id, v.actor_type, v.user_id, v.grant_id, v.started_at, v.last_seen_at, v.page_views,
|
|
6087
|
+
v.interactions, v.attribution
|
|
6088
|
+
`;
|
|
6089
|
+
var NO_CURSOR = { at: "9999-12-31T23:59:59.999Z", id: "" };
|
|
6090
|
+
|
|
6091
|
+
class SqliteActivityStorage {
|
|
6092
|
+
db;
|
|
6093
|
+
constructor(connection) {
|
|
6094
|
+
this.db = connection.db;
|
|
6095
|
+
}
|
|
6096
|
+
async append(input) {
|
|
6097
|
+
validateActivityAppend(input);
|
|
6098
|
+
return runImmediateTransaction(this.db, () => {
|
|
6099
|
+
const plan = planActivityAppend(input, this.source(input), this.storedEvents(input));
|
|
6100
|
+
if (plan.status !== "append") {
|
|
6101
|
+
return { status: plan.status === "duplicate" ? "accepted" : "throttled", accepted: 0 };
|
|
6102
|
+
}
|
|
6103
|
+
const { state } = plan;
|
|
6104
|
+
const lastSeenAt = iso(state.lastSeenAt);
|
|
6105
|
+
if (plan.newVisit) {
|
|
6106
|
+
this.run(`
|
|
6107
|
+
INSERT INTO activity_visits (
|
|
6108
|
+
project_id, id, actor_type, user_id, grant_id, session_id,
|
|
6109
|
+
started_at, last_seen_at, page_views, interactions, attribution
|
|
6110
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6111
|
+
`, input.projectId, state.visitId, input.actor.type, input.actor.type === "user" ? input.actor.userId : null, input.actor.type === "share" ? input.actor.grantId : null, input.sessionId, lastSeenAt, lastSeenAt, plan.pageViews.length, plan.interactions.length, plan.newVisit.attribution === null ? null : serializeActivityAttribution(plan.newVisit.attribution));
|
|
6112
|
+
} else {
|
|
6113
|
+
this.run(`
|
|
6114
|
+
UPDATE activity_visits
|
|
6115
|
+
SET
|
|
6116
|
+
last_seen_at = ?,
|
|
6117
|
+
page_views = page_views + ?,
|
|
6118
|
+
interactions = interactions + ?
|
|
6119
|
+
WHERE project_id = ? AND id = ?
|
|
6120
|
+
`, lastSeenAt, plan.pageViews.length, plan.interactions.length, input.projectId, state.visitId);
|
|
6121
|
+
}
|
|
6122
|
+
for (const pageView of plan.pageViews) {
|
|
6123
|
+
this.run(`
|
|
6124
|
+
INSERT INTO activity_page_views (
|
|
6125
|
+
project_id, visit_id, sequence, id, route, received_at, attribution
|
|
6126
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
6127
|
+
`, input.projectId, pageView.visitId, pageView.sequence, pageView.id, pageView.route, pageView.receivedAt.toISOString(), serializeActivityAttribution(pageView.attribution));
|
|
6128
|
+
}
|
|
6129
|
+
for (const interaction of plan.interactions) {
|
|
6130
|
+
this.run(`
|
|
6131
|
+
INSERT INTO activity_interactions (
|
|
6132
|
+
project_id, visit_id, sequence, id, name, route, received_at
|
|
6133
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
6134
|
+
`, input.projectId, interaction.visitId, interaction.sequence, interaction.id, interaction.name, interaction.route, interaction.receivedAt.toISOString());
|
|
6135
|
+
}
|
|
6136
|
+
this.run(`
|
|
6137
|
+
INSERT INTO activity_sources (
|
|
6138
|
+
project_id, actor_type, session_id, visit_id, last_seen_at, sequence,
|
|
6139
|
+
window_start, window_count
|
|
6140
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
6141
|
+
ON CONFLICT (project_id, actor_type, session_id) DO UPDATE SET
|
|
6142
|
+
visit_id = excluded.visit_id,
|
|
6143
|
+
last_seen_at = excluded.last_seen_at,
|
|
6144
|
+
sequence = excluded.sequence,
|
|
6145
|
+
window_start = excluded.window_start,
|
|
6146
|
+
window_count = excluded.window_count
|
|
6147
|
+
`, input.projectId, input.actor.type, input.sessionId, state.visitId, lastSeenAt, state.sequence, iso(state.windowStart), state.windowCount);
|
|
6148
|
+
return { status: "accepted", accepted: plan.accepted };
|
|
6149
|
+
});
|
|
6150
|
+
}
|
|
6151
|
+
async list(input) {
|
|
6152
|
+
const period = overlapping(input);
|
|
6153
|
+
const before = cursorOf(input.before);
|
|
6154
|
+
const rows = this.all(`
|
|
6155
|
+
SELECT ${VISIT_COLUMNS}
|
|
6156
|
+
FROM activity_visits v
|
|
6157
|
+
WHERE ${period.where}
|
|
6158
|
+
AND (v.last_seen_at < ? OR (v.last_seen_at = ? AND v.id < ?))
|
|
6159
|
+
ORDER BY v.last_seen_at DESC, v.id DESC
|
|
6160
|
+
LIMIT ?
|
|
6161
|
+
`, ...period.params, before.at, before.at, before.id, input.limit + 1);
|
|
6162
|
+
return {
|
|
6163
|
+
visits: rows.slice(0, input.limit).map(visitFromRow),
|
|
6164
|
+
hasMore: rows.length > input.limit
|
|
6165
|
+
};
|
|
6166
|
+
}
|
|
6167
|
+
async users(input) {
|
|
6168
|
+
const period = overlapping(input);
|
|
6169
|
+
const before = cursorOf(input.before);
|
|
6170
|
+
const rows = this.all(`
|
|
6171
|
+
SELECT
|
|
6172
|
+
v.user_id,
|
|
6173
|
+
MAX(v.last_seen_at) AS last_seen_at,
|
|
6174
|
+
COUNT(DISTINCT v.id) AS visits,
|
|
6175
|
+
COUNT(p.id) AS page_views
|
|
6176
|
+
FROM activity_visits v
|
|
6177
|
+
LEFT JOIN activity_page_views p
|
|
6178
|
+
ON p.project_id = v.project_id
|
|
6179
|
+
AND p.visit_id = v.id
|
|
6180
|
+
AND p.received_at >= ?
|
|
6181
|
+
AND p.received_at < ?
|
|
6182
|
+
WHERE ${period.where} AND v.actor_type = 'user'
|
|
6183
|
+
GROUP BY v.user_id
|
|
6184
|
+
HAVING MAX(v.last_seen_at) < ? OR (MAX(v.last_seen_at) = ? AND v.user_id < ?)
|
|
6185
|
+
ORDER BY last_seen_at DESC, v.user_id DESC
|
|
6186
|
+
LIMIT ?
|
|
6187
|
+
`, input.from.toISOString(), input.to.toISOString(), ...period.params, before.at, before.at, before.id, input.limit + 1);
|
|
6188
|
+
return {
|
|
6189
|
+
users: rows.slice(0, input.limit).map((row) => ({
|
|
6190
|
+
userId: row.user_id,
|
|
6191
|
+
lastSeenAt: new Date(row.last_seen_at),
|
|
6192
|
+
visits: Number(row.visits),
|
|
6193
|
+
pageViews: Number(row.page_views)
|
|
6194
|
+
})),
|
|
6195
|
+
hasMore: rows.length > input.limit
|
|
6196
|
+
};
|
|
6197
|
+
}
|
|
6198
|
+
async summarize(input) {
|
|
6199
|
+
const period = overlapping(input);
|
|
6200
|
+
const [row] = this.all(`
|
|
6201
|
+
SELECT
|
|
6202
|
+
COUNT(DISTINCT v.user_id) AS users,
|
|
6203
|
+
COUNT(DISTINCT v.id) AS visits,
|
|
6204
|
+
COUNT(DISTINCT CASE WHEN v.actor_type = 'share' THEN v.id END) AS shared_visits,
|
|
6205
|
+
COUNT(p.id) AS page_views
|
|
6206
|
+
FROM activity_visits v
|
|
6207
|
+
LEFT JOIN activity_page_views p
|
|
6208
|
+
ON p.project_id = v.project_id
|
|
6209
|
+
AND p.visit_id = v.id
|
|
6210
|
+
AND p.received_at >= ?
|
|
6211
|
+
AND p.received_at < ?
|
|
6212
|
+
WHERE ${period.where}
|
|
6213
|
+
`, input.from.toISOString(), input.to.toISOString(), ...period.params);
|
|
6214
|
+
return {
|
|
6215
|
+
users: Number(row?.users ?? 0),
|
|
6216
|
+
visits: Number(row?.visits ?? 0),
|
|
6217
|
+
sharedVisits: Number(row?.shared_visits ?? 0),
|
|
6218
|
+
pageViews: Number(row?.page_views ?? 0)
|
|
6219
|
+
};
|
|
6220
|
+
}
|
|
6221
|
+
async calendar(input) {
|
|
6222
|
+
const period = overlapping(input);
|
|
6223
|
+
const visits = this.all(`
|
|
6224
|
+
SELECT v.started_at, v.last_seen_at
|
|
6225
|
+
FROM activity_visits v
|
|
6226
|
+
WHERE ${period.where}
|
|
6227
|
+
`, ...period.params);
|
|
6228
|
+
const days = this.all(`
|
|
6229
|
+
SELECT substr(p.received_at, 1, 10) AS day, COUNT(*) AS page_views
|
|
6230
|
+
FROM activity_visits v
|
|
6231
|
+
JOIN activity_page_views p ON p.project_id = v.project_id AND p.visit_id = v.id
|
|
6232
|
+
WHERE ${period.where}
|
|
6233
|
+
AND p.received_at >= ?
|
|
6234
|
+
AND p.received_at < ?
|
|
6235
|
+
GROUP BY day
|
|
6236
|
+
`, ...period.params, input.from.toISOString(), input.to.toISOString());
|
|
6237
|
+
return activityCalendarDays(input, visits.map((visit) => ({
|
|
6238
|
+
startedAt: new Date(visit.started_at),
|
|
6239
|
+
lastSeenAt: new Date(visit.last_seen_at)
|
|
6240
|
+
})), new Map(days.map((row) => [row.day, Number(row.page_views)])));
|
|
6241
|
+
}
|
|
6242
|
+
async read(input) {
|
|
6243
|
+
const [visit] = this.all(`
|
|
6244
|
+
SELECT ${VISIT_COLUMNS}, v.session_id
|
|
6245
|
+
FROM activity_visits v
|
|
6246
|
+
WHERE v.project_id = ? AND v.id = ?
|
|
6247
|
+
`, input.projectId, input.id);
|
|
6248
|
+
if (!visit) {
|
|
6249
|
+
return null;
|
|
6250
|
+
}
|
|
6251
|
+
const rows = this.all(`
|
|
6252
|
+
SELECT id, visit_id, sequence, route, NULL AS name, received_at
|
|
6253
|
+
FROM activity_page_views
|
|
6254
|
+
WHERE project_id = ? AND visit_id = ?
|
|
6255
|
+
UNION ALL
|
|
6256
|
+
SELECT id, visit_id, sequence, route, name, received_at
|
|
6257
|
+
FROM activity_interactions
|
|
6258
|
+
WHERE project_id = ? AND visit_id = ?
|
|
6259
|
+
ORDER BY sequence
|
|
6260
|
+
LIMIT ?
|
|
6261
|
+
`, input.projectId, input.id, input.projectId, input.id, input.limit + 1);
|
|
6262
|
+
const pageViews = [];
|
|
6263
|
+
const interactions = [];
|
|
6264
|
+
for (const row of rows.slice(0, input.limit)) {
|
|
6265
|
+
if (row.name === null) {
|
|
6266
|
+
pageViews.push(pageViewFromRow(row));
|
|
6267
|
+
} else {
|
|
6268
|
+
interactions.push({ ...pageViewFromRow(row), name: row.name });
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
6271
|
+
return {
|
|
6272
|
+
visit: visitFromRow(visit),
|
|
6273
|
+
sessionId: visit.session_id,
|
|
6274
|
+
pageViews,
|
|
6275
|
+
interactions,
|
|
6276
|
+
hasMore: rows.length > input.limit
|
|
6277
|
+
};
|
|
6278
|
+
}
|
|
6279
|
+
async sessionVisits(input) {
|
|
6280
|
+
if (input.sessionIds.length === 0) {
|
|
6281
|
+
return [];
|
|
6282
|
+
}
|
|
6283
|
+
const rows = this.all(`
|
|
6284
|
+
SELECT *
|
|
6285
|
+
FROM (
|
|
6286
|
+
SELECT
|
|
6287
|
+
${VISIT_COLUMNS},
|
|
6288
|
+
v.session_id,
|
|
6289
|
+
ROW_NUMBER() OVER (
|
|
6290
|
+
PARTITION BY v.session_id ORDER BY v.started_at DESC, v.id DESC
|
|
6291
|
+
) AS position,
|
|
6292
|
+
COUNT(*) OVER (PARTITION BY v.session_id) AS total
|
|
6293
|
+
FROM activity_visits v
|
|
6294
|
+
WHERE v.project_id = ?
|
|
6295
|
+
AND v.actor_type = 'user'
|
|
6296
|
+
AND v.session_id IN (SELECT value FROM json_each(?))
|
|
6297
|
+
AND v.user_id = ?
|
|
6298
|
+
)
|
|
6299
|
+
WHERE position <= ?
|
|
6300
|
+
ORDER BY session_id, position
|
|
6301
|
+
`, input.projectId, JSON.stringify(input.sessionIds), input.userId, input.limit);
|
|
6302
|
+
const bySession = new Map;
|
|
6303
|
+
for (const row of rows) {
|
|
6304
|
+
const session = bySession.get(row.session_id) ?? { visits: Number(row.total), latest: [] };
|
|
6305
|
+
session.latest.push(visitFromRow(row));
|
|
6306
|
+
bySession.set(row.session_id, session);
|
|
6307
|
+
}
|
|
6308
|
+
return [...bySession].map(([sessionId, session]) => ({ sessionId, ...session }));
|
|
6309
|
+
}
|
|
6310
|
+
source(input) {
|
|
6311
|
+
const [row] = this.all(`
|
|
6312
|
+
SELECT
|
|
6313
|
+
s.visit_id, v.started_at AS visit_started_at, s.last_seen_at, s.sequence,
|
|
6314
|
+
s.window_start, s.window_count
|
|
6315
|
+
FROM activity_sources s
|
|
6316
|
+
LEFT JOIN activity_visits v ON v.project_id = s.project_id AND v.id = s.visit_id
|
|
6317
|
+
WHERE s.project_id = ? AND s.actor_type = ? AND s.session_id = ?
|
|
6318
|
+
`, input.projectId, input.actor.type, input.sessionId);
|
|
6319
|
+
if (!row) {
|
|
6320
|
+
return;
|
|
6321
|
+
}
|
|
6322
|
+
return {
|
|
6323
|
+
visitId: row.visit_id,
|
|
6324
|
+
visitStartedAt: row.visit_started_at === null ? null : Date.parse(row.visit_started_at),
|
|
6325
|
+
lastSeenAt: Date.parse(row.last_seen_at),
|
|
6326
|
+
sequence: Number(row.sequence),
|
|
6327
|
+
windowStart: Date.parse(row.window_start),
|
|
6328
|
+
windowCount: Number(row.window_count)
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
6331
|
+
storedEvents(input) {
|
|
6332
|
+
const ids = JSON.stringify(input.events.map((event) => event.id));
|
|
6333
|
+
const rows = this.all(`
|
|
6334
|
+
SELECT p.id, p.route, NULL AS name, p.attribution
|
|
6335
|
+
FROM activity_page_views p
|
|
6336
|
+
JOIN activity_visits v ON v.project_id = p.project_id AND v.id = p.visit_id
|
|
6337
|
+
WHERE p.project_id = ?
|
|
6338
|
+
AND p.id IN (SELECT value FROM json_each(?))
|
|
6339
|
+
AND v.actor_type = ?
|
|
6340
|
+
AND v.session_id = ?
|
|
6341
|
+
UNION ALL
|
|
6342
|
+
SELECT i.id, i.route, i.name, NULL AS attribution
|
|
6343
|
+
FROM activity_interactions i
|
|
6344
|
+
JOIN activity_visits v ON v.project_id = i.project_id AND v.id = i.visit_id
|
|
6345
|
+
WHERE i.project_id = ?
|
|
6346
|
+
AND i.id IN (SELECT value FROM json_each(?))
|
|
6347
|
+
AND v.actor_type = ?
|
|
6348
|
+
AND v.session_id = ?
|
|
6349
|
+
`, input.projectId, ids, input.actor.type, input.sessionId, input.projectId, ids, input.actor.type, input.sessionId);
|
|
6350
|
+
return rows.map((row) => {
|
|
6351
|
+
if (row.name !== null) {
|
|
6352
|
+
return { id: row.id, route: row.route, name: row.name };
|
|
6353
|
+
}
|
|
6354
|
+
const attribution = parseActivityAttribution(row.attribution);
|
|
6355
|
+
return { id: row.id, route: row.route, ...attribution ? { attribution } : {} };
|
|
6356
|
+
});
|
|
6357
|
+
}
|
|
6358
|
+
run(sql, ...params) {
|
|
6359
|
+
this.db.query(sql).run(...params);
|
|
6360
|
+
}
|
|
6361
|
+
all(sql, ...params) {
|
|
6362
|
+
return this.db.query(sql).all(...params);
|
|
6363
|
+
}
|
|
6364
|
+
}
|
|
6365
|
+
function overlapping(input) {
|
|
6366
|
+
const conditions = [
|
|
6367
|
+
"v.project_id = ?",
|
|
6368
|
+
"v.started_at >= ?",
|
|
6369
|
+
"v.started_at < ?",
|
|
6370
|
+
"v.last_seen_at >= ?"
|
|
6371
|
+
];
|
|
6372
|
+
const params = [
|
|
6373
|
+
input.projectId,
|
|
6374
|
+
new Date(input.from.getTime() - ACTIVITY_MAX_VISIT_MS).toISOString(),
|
|
6375
|
+
input.to.toISOString(),
|
|
6376
|
+
input.from.toISOString()
|
|
6377
|
+
];
|
|
6378
|
+
if (input.userId !== undefined) {
|
|
6379
|
+
conditions.push("v.user_id = ?");
|
|
6380
|
+
params.push(input.userId);
|
|
6381
|
+
}
|
|
6382
|
+
return { where: conditions.join(" AND "), params };
|
|
6383
|
+
}
|
|
6384
|
+
function cursorOf(cursor) {
|
|
6385
|
+
return cursor ? { at: cursor.at.toISOString(), id: cursor.id } : NO_CURSOR;
|
|
6386
|
+
}
|
|
6387
|
+
function visitFromRow(row) {
|
|
6388
|
+
return {
|
|
6389
|
+
id: row.id,
|
|
6390
|
+
actor: activityActor(row.actor_type, row.user_id, row.grant_id),
|
|
6391
|
+
attribution: parseActivityAttribution(row.attribution),
|
|
6392
|
+
startedAt: new Date(row.started_at),
|
|
6393
|
+
lastSeenAt: new Date(row.last_seen_at),
|
|
6394
|
+
pageViews: Number(row.page_views),
|
|
6395
|
+
interactions: Number(row.interactions)
|
|
6396
|
+
};
|
|
6397
|
+
}
|
|
6398
|
+
function pageViewFromRow(row) {
|
|
6399
|
+
return {
|
|
6400
|
+
id: row.id,
|
|
6401
|
+
visitId: row.visit_id,
|
|
6402
|
+
sequence: Number(row.sequence),
|
|
6403
|
+
route: row.route,
|
|
6404
|
+
receivedAt: new Date(row.received_at)
|
|
6405
|
+
};
|
|
6406
|
+
}
|
|
6407
|
+
function iso(epochMs) {
|
|
6408
|
+
return new Date(epochMs).toISOString();
|
|
6409
|
+
}
|
|
6410
|
+
|
|
5820
6411
|
// src/agents/checkpoints.ts
|
|
5821
6412
|
import {
|
|
5822
6413
|
agentContextCheckpointMatchesCreateInput,
|
|
@@ -6599,6 +7190,23 @@ class SqliteAgentThreadStore {
|
|
|
6599
7190
|
const row = this.db.query("SELECT * FROM agent_threads WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
|
|
6600
7191
|
return row ? rowToThreadRecord(row) : null;
|
|
6601
7192
|
}
|
|
7193
|
+
async getByIds(params) {
|
|
7194
|
+
const ids = [...new Set(params.ids)];
|
|
7195
|
+
if (ids.length === 0) {
|
|
7196
|
+
return [];
|
|
7197
|
+
}
|
|
7198
|
+
const rows = this.db.query(`
|
|
7199
|
+
SELECT *
|
|
7200
|
+
FROM agent_threads
|
|
7201
|
+
WHERE project_id = ?
|
|
7202
|
+
AND id IN (SELECT value FROM json_each(?))
|
|
7203
|
+
`).all(params.projectId, JSON.stringify(ids));
|
|
7204
|
+
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
7205
|
+
return ids.flatMap((id) => {
|
|
7206
|
+
const row = byId.get(id);
|
|
7207
|
+
return row ? [rowToThreadRecord(row)] : [];
|
|
7208
|
+
});
|
|
7209
|
+
}
|
|
6602
7210
|
async list(input) {
|
|
6603
7211
|
if (input.statuses !== undefined && input.statuses.length === 0) {
|
|
6604
7212
|
return { threads: [], hasMore: false, total: 0 };
|
|
@@ -6643,9 +7251,6 @@ class SqliteAgentStorage {
|
|
|
6643
7251
|
connection;
|
|
6644
7252
|
constructor(options) {
|
|
6645
7253
|
this.connection = openSqliteStoreConnection(options);
|
|
6646
|
-
if (this.connection.installFreshSchema) {
|
|
6647
|
-
installFreshSqliteSchema(this.connection.db);
|
|
6648
|
-
}
|
|
6649
7254
|
this.threads = new SqliteAgentThreadStore(this.connection.db);
|
|
6650
7255
|
this.runs = new SqliteAgentRunStore(this.connection.db, options.executions);
|
|
6651
7256
|
this.messages = new SqliteAgentMessageStore(this.connection.db);
|
|
@@ -6777,9 +7382,6 @@ class SqliteAiUsageStorage {
|
|
|
6777
7382
|
connection;
|
|
6778
7383
|
constructor(options = {}) {
|
|
6779
7384
|
this.connection = openSqliteStoreConnection(options);
|
|
6780
|
-
if (this.connection.installFreshSchema) {
|
|
6781
|
-
installFreshSqliteSchema(this.connection.db);
|
|
6782
|
-
}
|
|
6783
7385
|
}
|
|
6784
7386
|
async recordModelCall(input) {
|
|
6785
7387
|
const record = normalizeAiModelCallRecord(input);
|
|
@@ -7511,8 +8113,6 @@ class SqliteAiLimitStorage {
|
|
|
7511
8113
|
connection;
|
|
7512
8114
|
constructor(options = {}) {
|
|
7513
8115
|
this.connection = openSqliteStoreConnection(options);
|
|
7514
|
-
if (this.connection.installFreshSchema)
|
|
7515
|
-
installFreshSqliteSchema(this.connection.db);
|
|
7516
8116
|
}
|
|
7517
8117
|
async createPolicy(input) {
|
|
7518
8118
|
const policy = normalizeCreateAiLimitPolicy(input);
|
|
@@ -8045,6 +8645,185 @@ function isConstraintError(error) {
|
|
|
8045
8645
|
return error instanceof Error && /constraint/i.test(error.message);
|
|
8046
8646
|
}
|
|
8047
8647
|
|
|
8648
|
+
// src/artifact-storage.ts
|
|
8649
|
+
import {
|
|
8650
|
+
artifactStorageErrors,
|
|
8651
|
+
parseArtifactFrameGrantRow,
|
|
8652
|
+
parseArtifactRow,
|
|
8653
|
+
parseArtifactVersionRow
|
|
8654
|
+
} from "@sixb/core/internal/storage";
|
|
8655
|
+
var ARTIFACT_COLUMNS = `
|
|
8656
|
+
project_id AS projectId, id, kind, title,
|
|
8657
|
+
owner_principal_type AS ownerPrincipalType, owner_principal_id AS ownerPrincipalId,
|
|
8658
|
+
latest_version AS latestVersion, created_at AS createdAt, updated_at AS updatedAt
|
|
8659
|
+
`;
|
|
8660
|
+
var VERSION_COLUMNS = `
|
|
8661
|
+
project_id AS projectId, artifact_id AS artifactId, version, title, entry, files, outline, refs,
|
|
8662
|
+
author_principal_type AS authorPrincipalType, author_principal_id AS authorPrincipalId,
|
|
8663
|
+
origin_thread_id AS originThreadId, origin_run_id AS originRunId, created_at AS createdAt
|
|
8664
|
+
`;
|
|
8665
|
+
var FRAME_GRANT_COLUMNS = `
|
|
8666
|
+
project_id AS projectId, token_hash AS tokenHash, artifact_id AS artifactId, version, targets,
|
|
8667
|
+
created_at AS createdAt, expires_at AS expiresAt
|
|
8668
|
+
`;
|
|
8669
|
+
|
|
8670
|
+
class SqliteArtifactStorage {
|
|
8671
|
+
db;
|
|
8672
|
+
constructor(connection) {
|
|
8673
|
+
this.db = connection.db;
|
|
8674
|
+
}
|
|
8675
|
+
async create(input) {
|
|
8676
|
+
return runImmediateTransaction(this.db, () => {
|
|
8677
|
+
const createdAt = input.version.createdAt.toISOString();
|
|
8678
|
+
const artifact = this.db.query(`INSERT INTO artifacts (
|
|
8679
|
+
project_id, id, kind, title, owner_principal_type, owner_principal_id,
|
|
8680
|
+
latest_version, next_version, created_at, updated_at
|
|
8681
|
+
) VALUES (?, ?, ?, ?, ?, ?, 1, 2, ?, ?)
|
|
8682
|
+
ON CONFLICT (project_id, id) DO NOTHING
|
|
8683
|
+
RETURNING ${ARTIFACT_COLUMNS}`).get(input.projectId, input.id, input.kind, input.version.title, input.owner.type, input.owner.id, createdAt, createdAt);
|
|
8684
|
+
if (!artifact)
|
|
8685
|
+
throw artifactStorageErrors.exists(input.id);
|
|
8686
|
+
return {
|
|
8687
|
+
artifact: parseArtifactRow(artifact),
|
|
8688
|
+
version: this.insertVersion(input.projectId, input.id, 1, input.version)
|
|
8689
|
+
};
|
|
8690
|
+
});
|
|
8691
|
+
}
|
|
8692
|
+
async publish(input) {
|
|
8693
|
+
return runImmediateTransaction(this.db, () => {
|
|
8694
|
+
const current = this.db.query("SELECT kind, next_version AS nextVersion FROM artifacts WHERE project_id = ? AND id = ?").get(input.projectId, input.artifactId);
|
|
8695
|
+
if (!current)
|
|
8696
|
+
throw artifactStorageErrors.notFound(input.artifactId);
|
|
8697
|
+
if (current.kind !== input.kind) {
|
|
8698
|
+
throw artifactStorageErrors.kindMismatch(input.artifactId, current.kind, input.kind);
|
|
8699
|
+
}
|
|
8700
|
+
const version = this.insertVersion(input.projectId, input.artifactId, current.nextVersion, input.version);
|
|
8701
|
+
const artifact = this.db.query(`UPDATE artifacts
|
|
8702
|
+
SET title = ?, latest_version = ?, next_version = ?, updated_at = ?
|
|
8703
|
+
WHERE project_id = ? AND id = ?
|
|
8704
|
+
RETURNING ${ARTIFACT_COLUMNS}`).get(input.version.title, version.version, version.version + 1, input.version.createdAt.toISOString(), input.projectId, input.artifactId);
|
|
8705
|
+
return { artifact: parseArtifactRow(artifact), version };
|
|
8706
|
+
});
|
|
8707
|
+
}
|
|
8708
|
+
async get(input) {
|
|
8709
|
+
const row = this.db.query(`SELECT ${ARTIFACT_COLUMNS} FROM artifacts WHERE project_id = ? AND id = ?`).get(input.projectId, input.id);
|
|
8710
|
+
return row ? parseArtifactRow(row) : null;
|
|
8711
|
+
}
|
|
8712
|
+
async list(input) {
|
|
8713
|
+
const where = ["a.project_id = ?"];
|
|
8714
|
+
const params = [input.projectId];
|
|
8715
|
+
if (input.owner) {
|
|
8716
|
+
where.push("a.owner_principal_type = ? AND a.owner_principal_id = ?");
|
|
8717
|
+
params.push(input.owner.type, input.owner.id);
|
|
8718
|
+
}
|
|
8719
|
+
if (input.originThreadId !== undefined) {
|
|
8720
|
+
where.push(`EXISTS (
|
|
8721
|
+
SELECT 1 FROM artifact_versions v
|
|
8722
|
+
WHERE v.project_id = a.project_id AND v.artifact_id = a.id AND v.origin_thread_id = ?
|
|
8723
|
+
)`);
|
|
8724
|
+
params.push(input.originThreadId);
|
|
8725
|
+
}
|
|
8726
|
+
if (input.kind !== undefined) {
|
|
8727
|
+
where.push("a.kind = ?");
|
|
8728
|
+
params.push(input.kind);
|
|
8729
|
+
}
|
|
8730
|
+
const clause = where.join(" AND ");
|
|
8731
|
+
const { total } = this.db.query(`SELECT COUNT(*) AS total FROM artifacts a WHERE ${clause}`).get(...params);
|
|
8732
|
+
const offset = input.offset ?? 0;
|
|
8733
|
+
const rows = this.db.query(`SELECT ${ARTIFACT_COLUMNS} FROM artifacts a WHERE ${clause}
|
|
8734
|
+
ORDER BY a.updated_at DESC, a.id ASC LIMIT ? OFFSET ?`).all(...params, input.limit ?? -1, offset);
|
|
8735
|
+
return {
|
|
8736
|
+
artifacts: rows.map(parseArtifactRow),
|
|
8737
|
+
hasMore: offset + rows.length < total,
|
|
8738
|
+
total
|
|
8739
|
+
};
|
|
8740
|
+
}
|
|
8741
|
+
async getVersion(input) {
|
|
8742
|
+
const row = this.db.query(`SELECT ${VERSION_COLUMNS} FROM artifact_versions
|
|
8743
|
+
WHERE project_id = ? AND artifact_id = ? AND version = ?`).get(input.projectId, input.artifactId, input.version);
|
|
8744
|
+
return row ? parseArtifactVersionRow(row) : null;
|
|
8745
|
+
}
|
|
8746
|
+
async listVersions(input) {
|
|
8747
|
+
const { total } = this.db.query("SELECT COUNT(*) AS total FROM artifact_versions WHERE project_id = ? AND artifact_id = ?").get(input.projectId, input.artifactId);
|
|
8748
|
+
const offset = input.offset ?? 0;
|
|
8749
|
+
const rows = this.db.query(`SELECT ${VERSION_COLUMNS} FROM artifact_versions
|
|
8750
|
+
WHERE project_id = ? AND artifact_id = ?
|
|
8751
|
+
ORDER BY version DESC LIMIT ? OFFSET ?`).all(input.projectId, input.artifactId, input.limit ?? -1, offset);
|
|
8752
|
+
return {
|
|
8753
|
+
versions: rows.map(parseArtifactVersionRow),
|
|
8754
|
+
hasMore: offset + rows.length < total,
|
|
8755
|
+
total
|
|
8756
|
+
};
|
|
8757
|
+
}
|
|
8758
|
+
async listReferencingVersions(input) {
|
|
8759
|
+
const prefix = `artifact:${input.artifactId}`;
|
|
8760
|
+
const rows = this.db.query(`SELECT ${VERSION_COLUMNS} FROM artifact_versions v
|
|
8761
|
+
WHERE (project_id, artifact_id, version) IN (
|
|
8762
|
+
SELECT project_id, id, latest_version FROM artifacts WHERE project_id = ?
|
|
8763
|
+
) AND EXISTS (
|
|
8764
|
+
SELECT 1 FROM json_each(v.refs) r
|
|
8765
|
+
WHERE substr(json_extract(r.value, '$.address'), 1, ?) = ?
|
|
8766
|
+
AND substr(json_extract(r.value, '$.address'), ? + 1, 1) IN ('', '@', '/', '#')
|
|
8767
|
+
)
|
|
8768
|
+
ORDER BY artifact_id`).all(input.projectId, prefix.length, prefix, prefix.length);
|
|
8769
|
+
return rows.map(parseArtifactVersionRow);
|
|
8770
|
+
}
|
|
8771
|
+
async createFrameGrant(record) {
|
|
8772
|
+
runImmediateTransaction(this.db, () => {
|
|
8773
|
+
const exists = this.db.query("SELECT 1 FROM artifacts WHERE project_id = ? AND id = ?").get(record.projectId, record.artifactId);
|
|
8774
|
+
if (!exists)
|
|
8775
|
+
throw artifactStorageErrors.notFound(record.artifactId);
|
|
8776
|
+
this.db.query(`INSERT INTO artifact_frame_grants
|
|
8777
|
+
(project_id, token_hash, artifact_id, version, targets, created_at, expires_at)
|
|
8778
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(record.projectId, record.tokenHash, record.artifactId, record.version, JSON.stringify(record.targets), record.createdAt.toISOString(), record.expiresAt.toISOString());
|
|
8779
|
+
});
|
|
8780
|
+
}
|
|
8781
|
+
async findFrameGrant(input) {
|
|
8782
|
+
const row = this.db.query(`SELECT ${FRAME_GRANT_COLUMNS} FROM artifact_frame_grants
|
|
8783
|
+
WHERE project_id = ? AND token_hash = ?`).get(input.projectId, input.tokenHash);
|
|
8784
|
+
return row ? parseArtifactFrameGrantRow(row) : null;
|
|
8785
|
+
}
|
|
8786
|
+
async deleteExpiredFrameGrants(input) {
|
|
8787
|
+
return this.db.query(`DELETE FROM artifact_frame_grants WHERE rowid IN (
|
|
8788
|
+
SELECT rowid FROM artifact_frame_grants
|
|
8789
|
+
WHERE project_id = ? AND expires_at < ?
|
|
8790
|
+
ORDER BY expires_at LIMIT ?
|
|
8791
|
+
)`).run(input.projectId, input.expiredBefore.toISOString(), input.limit).changes;
|
|
8792
|
+
}
|
|
8793
|
+
async delete(input) {
|
|
8794
|
+
return this.db.query("DELETE FROM artifacts WHERE project_id = ? AND id = ?").run(input.projectId, input.id).changes > 0;
|
|
8795
|
+
}
|
|
8796
|
+
async deleteVersionsByOrigin(input) {
|
|
8797
|
+
return runImmediateTransaction(this.db, () => {
|
|
8798
|
+
const affected = this.db.query(`SELECT DISTINCT artifact_id AS artifactId FROM artifact_versions
|
|
8799
|
+
WHERE project_id = ? AND origin_run_id = ?`).all(input.projectId, input.runId);
|
|
8800
|
+
const deleted = this.db.query("DELETE FROM artifact_versions WHERE project_id = ? AND origin_run_id = ?").run(input.projectId, input.runId).changes;
|
|
8801
|
+
for (const { artifactId } of affected) {
|
|
8802
|
+
this.repointLatest(input.projectId, artifactId);
|
|
8803
|
+
}
|
|
8804
|
+
return deleted;
|
|
8805
|
+
});
|
|
8806
|
+
}
|
|
8807
|
+
repointLatest(projectId, artifactId) {
|
|
8808
|
+
const latest = this.db.query(`SELECT version, title, created_at AS createdAt FROM artifact_versions
|
|
8809
|
+
WHERE project_id = ? AND artifact_id = ? ORDER BY version DESC LIMIT 1`).get(projectId, artifactId);
|
|
8810
|
+
if (!latest) {
|
|
8811
|
+
this.db.query("DELETE FROM artifacts WHERE project_id = ? AND id = ?").run(projectId, artifactId);
|
|
8812
|
+
return;
|
|
8813
|
+
}
|
|
8814
|
+
this.db.query(`UPDATE artifacts SET title = ?, latest_version = ?, updated_at = ?
|
|
8815
|
+
WHERE project_id = ? AND id = ?`).run(latest.title, latest.version, latest.createdAt, projectId, artifactId);
|
|
8816
|
+
}
|
|
8817
|
+
insertVersion(projectId, artifactId, version, input) {
|
|
8818
|
+
const row = this.db.query(`INSERT INTO artifact_versions (
|
|
8819
|
+
project_id, artifact_id, version, title, entry, files, outline, refs,
|
|
8820
|
+
author_principal_type, author_principal_id, origin_thread_id, origin_run_id, created_at
|
|
8821
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8822
|
+
RETURNING ${VERSION_COLUMNS}`).get(projectId, artifactId, version, input.title, input.entry, JSON.stringify(input.files), JSON.stringify(input.outline), JSON.stringify(input.references), input.author.type, input.author.id, input.origin?.threadId ?? null, input.origin?.runId ?? null, input.createdAt.toISOString());
|
|
8823
|
+
return parseArtifactVersionRow(row);
|
|
8824
|
+
}
|
|
8825
|
+
}
|
|
8826
|
+
|
|
8048
8827
|
// src/auth-storage/auth-storage.ts
|
|
8049
8828
|
import { AuthStorageError as AuthStorageError11 } from "@sixb/core/storage";
|
|
8050
8829
|
|
|
@@ -8106,7 +8885,6 @@ function rowToSessionRecord(row) {
|
|
|
8106
8885
|
projectId: row.project_id,
|
|
8107
8886
|
userId: row.user_id,
|
|
8108
8887
|
strategyId: row.strategy_id,
|
|
8109
|
-
audience: row.audience,
|
|
8110
8888
|
tokenHash: row.token_hash,
|
|
8111
8889
|
createdAt: new Date(row.created_at),
|
|
8112
8890
|
expiresAt: new Date(row.expires_at),
|
|
@@ -8179,7 +8957,6 @@ function rowToMagicLinkRecord(row) {
|
|
|
8179
8957
|
id: row.id,
|
|
8180
8958
|
projectId: row.project_id,
|
|
8181
8959
|
strategyId: row.strategy_id,
|
|
8182
|
-
audience: row.audience,
|
|
8183
8960
|
email: row.email,
|
|
8184
8961
|
tokenHash: row.token_hash,
|
|
8185
8962
|
returnTo: row.return_to ?? undefined,
|
|
@@ -8194,7 +8971,6 @@ function rowToOidcAuthorizationAttemptRecord(row) {
|
|
|
8194
8971
|
id: row.id,
|
|
8195
8972
|
projectId: row.project_id,
|
|
8196
8973
|
strategyId: row.strategy_id,
|
|
8197
|
-
audience: row.audience,
|
|
8198
8974
|
stateHash: row.state_hash,
|
|
8199
8975
|
nonceHash: row.nonce_hash,
|
|
8200
8976
|
codeVerifier: row.code_verifier,
|
|
@@ -8412,7 +9188,6 @@ function consumeOidcAttempt(db, params) {
|
|
|
8412
9188
|
}
|
|
8413
9189
|
function validateCompleteSessionInput(db, projectId, session) {
|
|
8414
9190
|
const sessionId = assertNonEmpty(session.id, "Session id");
|
|
8415
|
-
assertNonEmpty(session.audience, "Session audience");
|
|
8416
9191
|
assertNonEmpty(session.tokenHash, "Session token hash");
|
|
8417
9192
|
assertSessionIdAvailable(db, projectId, sessionId);
|
|
8418
9193
|
}
|
|
@@ -8426,7 +9201,6 @@ function createSession(db, input) {
|
|
|
8426
9201
|
const projectId = assertNonEmpty(input.projectId, "Project id");
|
|
8427
9202
|
const userId = assertNonEmpty(input.userId, "User id");
|
|
8428
9203
|
const strategyId = assertNonEmpty(input.strategyId, "Strategy id");
|
|
8429
|
-
const audience = assertNonEmpty(input.audience, "Session audience");
|
|
8430
9204
|
const tokenHash = assertNonEmpty(input.tokenHash, "Session token hash");
|
|
8431
9205
|
assertSessionIdAvailable(db, projectId, id);
|
|
8432
9206
|
db.query(`
|
|
@@ -8435,7 +9209,6 @@ function createSession(db, input) {
|
|
|
8435
9209
|
id,
|
|
8436
9210
|
user_id,
|
|
8437
9211
|
strategy_id,
|
|
8438
|
-
audience,
|
|
8439
9212
|
token_hash,
|
|
8440
9213
|
created_at,
|
|
8441
9214
|
expires_at,
|
|
@@ -8445,8 +9218,8 @@ function createSession(db, input) {
|
|
|
8445
9218
|
client_name,
|
|
8446
9219
|
access_expires_at,
|
|
8447
9220
|
refresh_token_hash
|
|
8448
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
8449
|
-
`).run(projectId, id, userId, strategyId,
|
|
9221
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9222
|
+
`).run(projectId, id, userId, strategyId, tokenHash, toIso(input.createdAt), toIso(input.expiresAt), input.absoluteExpiresAt ? toIso(input.absoluteExpiresAt) : null, input.userAgent ?? null, input.ipAddress ?? null, input.bearer ? assertNonEmpty(input.bearer.clientName, "Session client name") : null, input.bearer ? toIso(input.bearer.accessExpiresAt) : null, input.bearer ? assertNonEmpty(input.bearer.refreshTokenHash, "Refresh token hash") : null);
|
|
8450
9223
|
const session = getSessionById(db, { projectId, id });
|
|
8451
9224
|
if (!session) {
|
|
8452
9225
|
throw new AuthStorageError("missing_session", `[Sixb] Failed to load session '${id}' for project '${projectId}'.`);
|
|
@@ -8455,30 +9228,25 @@ function createSession(db, input) {
|
|
|
8455
9228
|
}
|
|
8456
9229
|
function revokeActiveSessionsForUser(db, params) {
|
|
8457
9230
|
const revokedAt = toIso(params.revokedAt);
|
|
8458
|
-
const audienceCondition = params.audience === undefined ? "" : "AND audience = ?";
|
|
8459
|
-
const selectArgs = params.audience === undefined ? [params.projectId, params.userId, revokedAt, revokedAt] : [params.projectId, params.userId, params.audience, revokedAt, revokedAt];
|
|
8460
|
-
const updateArgs = params.audience === undefined ? [revokedAt, params.projectId, params.userId, revokedAt, revokedAt] : [revokedAt, params.projectId, params.userId, params.audience, revokedAt, revokedAt];
|
|
8461
9231
|
const rows = db.query(`
|
|
8462
9232
|
SELECT *
|
|
8463
9233
|
FROM auth_sessions
|
|
8464
9234
|
WHERE project_id = ?
|
|
8465
9235
|
AND user_id = ?
|
|
8466
|
-
${audienceCondition}
|
|
8467
9236
|
AND revoked_at IS NULL
|
|
8468
9237
|
AND expires_at > ?
|
|
8469
9238
|
AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
|
|
8470
9239
|
ORDER BY created_at ASC, id ASC
|
|
8471
|
-
`).all(
|
|
9240
|
+
`).all(params.projectId, params.userId, revokedAt, revokedAt);
|
|
8472
9241
|
db.query(`
|
|
8473
9242
|
UPDATE auth_sessions
|
|
8474
9243
|
SET revoked_at = ?
|
|
8475
9244
|
WHERE project_id = ?
|
|
8476
9245
|
AND user_id = ?
|
|
8477
|
-
${audienceCondition}
|
|
8478
9246
|
AND revoked_at IS NULL
|
|
8479
9247
|
AND expires_at > ?
|
|
8480
9248
|
AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
|
|
8481
|
-
`).run(
|
|
9249
|
+
`).run(revokedAt, params.projectId, params.userId, revokedAt, revokedAt);
|
|
8482
9250
|
return rows.map((row) => rowToSessionRecord({
|
|
8483
9251
|
...row,
|
|
8484
9252
|
revoked_at: revokedAt
|
|
@@ -9056,7 +9824,6 @@ class SqliteAuthInvitationStore {
|
|
|
9056
9824
|
}
|
|
9057
9825
|
|
|
9058
9826
|
// src/auth-storage/magic-links.ts
|
|
9059
|
-
import { resolveAuthSessionAudience } from "@sixb/core";
|
|
9060
9827
|
import { AuthStorageError as AuthStorageError5 } from "@sixb/core/storage";
|
|
9061
9828
|
class SqliteAuthMagicLinkStore {
|
|
9062
9829
|
db;
|
|
@@ -9068,7 +9835,6 @@ class SqliteAuthMagicLinkStore {
|
|
|
9068
9835
|
const id = assertNonEmpty(input.id, "Magic link id");
|
|
9069
9836
|
const projectId = assertNonEmpty(input.projectId, "Project id");
|
|
9070
9837
|
const strategyId = assertNonEmpty(input.strategyId, "Strategy id");
|
|
9071
|
-
const audience = resolveAuthSessionAudience(input.audience);
|
|
9072
9838
|
const email = normalizeEmail(input.email);
|
|
9073
9839
|
const tokenHash = assertNonEmpty(input.tokenHash, "Magic link token hash");
|
|
9074
9840
|
if (getMagicLinkRowById(this.db, { projectId, id })) {
|
|
@@ -9085,14 +9851,13 @@ class SqliteAuthMagicLinkStore {
|
|
|
9085
9851
|
project_id,
|
|
9086
9852
|
id,
|
|
9087
9853
|
strategy_id,
|
|
9088
|
-
audience,
|
|
9089
9854
|
email,
|
|
9090
9855
|
token_hash,
|
|
9091
9856
|
return_to,
|
|
9092
9857
|
created_at,
|
|
9093
9858
|
expires_at
|
|
9094
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?,
|
|
9095
|
-
`).run(projectId, id, strategyId,
|
|
9859
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
9860
|
+
`).run(projectId, id, strategyId, email, tokenHash, input.returnTo ?? null, toIso(input.createdAt), toIso(input.expiresAt));
|
|
9096
9861
|
} catch (error) {
|
|
9097
9862
|
mapUniqueConstraintError(error, "duplicate_magic_link", `[Sixb] Magic link '${id}' already exists for project '${projectId}'.`);
|
|
9098
9863
|
}
|
|
@@ -9100,7 +9865,6 @@ class SqliteAuthMagicLinkStore {
|
|
|
9100
9865
|
id,
|
|
9101
9866
|
projectId,
|
|
9102
9867
|
strategyId,
|
|
9103
|
-
audience,
|
|
9104
9868
|
email,
|
|
9105
9869
|
tokenHash,
|
|
9106
9870
|
returnTo: input.returnTo,
|
|
@@ -9135,7 +9899,6 @@ class SqliteAuthMagicLinkStore {
|
|
|
9135
9899
|
}
|
|
9136
9900
|
|
|
9137
9901
|
// src/auth-storage/oidc-attempts.ts
|
|
9138
|
-
import { resolveAuthSessionAudience as resolveAuthSessionAudience2 } from "@sixb/core";
|
|
9139
9902
|
import { AuthStorageError as AuthStorageError6 } from "@sixb/core/storage";
|
|
9140
9903
|
class SqliteAuthOidcAuthorizationAttemptStore {
|
|
9141
9904
|
db;
|
|
@@ -9147,7 +9910,6 @@ class SqliteAuthOidcAuthorizationAttemptStore {
|
|
|
9147
9910
|
const id = assertNonEmpty(input.id, "OIDC authorization attempt id");
|
|
9148
9911
|
const projectId = assertNonEmpty(input.projectId, "Project id");
|
|
9149
9912
|
const strategyId = assertNonEmpty(input.strategyId, "Strategy id");
|
|
9150
|
-
const audience = resolveAuthSessionAudience2(input.audience);
|
|
9151
9913
|
const stateHash = assertNonEmpty(input.stateHash, "OIDC state hash");
|
|
9152
9914
|
const nonceHash = assertNonEmpty(input.nonceHash, "OIDC nonce hash");
|
|
9153
9915
|
const codeVerifier = assertNonEmpty(input.codeVerifier, "OIDC code verifier");
|
|
@@ -9160,15 +9922,14 @@ class SqliteAuthOidcAuthorizationAttemptStore {
|
|
|
9160
9922
|
project_id,
|
|
9161
9923
|
id,
|
|
9162
9924
|
strategy_id,
|
|
9163
|
-
audience,
|
|
9164
9925
|
state_hash,
|
|
9165
9926
|
nonce_hash,
|
|
9166
9927
|
code_verifier,
|
|
9167
9928
|
return_to,
|
|
9168
9929
|
created_at,
|
|
9169
9930
|
expires_at
|
|
9170
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?,
|
|
9171
|
-
`).run(projectId, id, strategyId,
|
|
9931
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9932
|
+
`).run(projectId, id, strategyId, stateHash, nonceHash, codeVerifier, input.returnTo ?? null, toIso(input.createdAt), toIso(input.expiresAt));
|
|
9172
9933
|
} catch (error) {
|
|
9173
9934
|
mapUniqueConstraintError(error, "duplicate_oidc_attempt", `[Sixb] OIDC authorization attempt '${id}' already exists for project '${projectId}'.`);
|
|
9174
9935
|
}
|
|
@@ -9176,7 +9937,6 @@ class SqliteAuthOidcAuthorizationAttemptStore {
|
|
|
9176
9937
|
id,
|
|
9177
9938
|
projectId,
|
|
9178
9939
|
strategyId,
|
|
9179
|
-
audience,
|
|
9180
9940
|
stateHash,
|
|
9181
9941
|
nonceHash,
|
|
9182
9942
|
codeVerifier,
|
|
@@ -9395,21 +10155,6 @@ class SqliteAuthSessionStore {
|
|
|
9395
10155
|
async getById(params) {
|
|
9396
10156
|
return getSessionById(this.db, params);
|
|
9397
10157
|
}
|
|
9398
|
-
async getActiveByUserId(params) {
|
|
9399
|
-
const row = this.db.query(`
|
|
9400
|
-
SELECT *
|
|
9401
|
-
FROM auth_sessions
|
|
9402
|
-
WHERE project_id = ?
|
|
9403
|
-
AND user_id = ?
|
|
9404
|
-
AND audience = ?
|
|
9405
|
-
AND revoked_at IS NULL
|
|
9406
|
-
AND expires_at > ?
|
|
9407
|
-
AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
|
|
9408
|
-
ORDER BY created_at DESC, id DESC
|
|
9409
|
-
LIMIT 1
|
|
9410
|
-
`).get(params.projectId, params.userId, params.audience, toIso(params.now), toIso(params.now));
|
|
9411
|
-
return row ? rowToSessionRecord(row) : null;
|
|
9412
|
-
}
|
|
9413
10158
|
async listActiveByUserId(params) {
|
|
9414
10159
|
const rows = this.db.query(`
|
|
9415
10160
|
SELECT *
|
|
@@ -9423,9 +10168,22 @@ class SqliteAuthSessionStore {
|
|
|
9423
10168
|
`).all(params.projectId, params.userId, toIso(params.now), toIso(params.now));
|
|
9424
10169
|
return rows.map(rowToSessionRecord);
|
|
9425
10170
|
}
|
|
10171
|
+
async listByUserId(params) {
|
|
10172
|
+
const rows = this.db.query(`
|
|
10173
|
+
SELECT *
|
|
10174
|
+
FROM auth_sessions
|
|
10175
|
+
WHERE project_id = ? AND user_id = ? AND created_at <= ?
|
|
10176
|
+
ORDER BY created_at DESC, id DESC
|
|
10177
|
+
LIMIT ? OFFSET ?
|
|
10178
|
+
`).all(params.projectId, params.userId, params.before ? toIso(params.before.createdAt) : "9999-12-31T23:59:59.999Z", params.limit + 1, params.before?.skip ?? 0);
|
|
10179
|
+
return {
|
|
10180
|
+
sessions: rows.slice(0, params.limit).map(rowToSessionRecord),
|
|
10181
|
+
hasMore: rows.length > params.limit
|
|
10182
|
+
};
|
|
10183
|
+
}
|
|
9426
10184
|
async findValidByTokenHash(params) {
|
|
9427
10185
|
const row = getSessionRowById(this.db, params);
|
|
9428
|
-
if (!row || row.refresh_token_hash !== null || row.
|
|
10186
|
+
if (!row || row.refresh_token_hash !== null || row.token_hash !== params.tokenHash || !isLiveSessionRow(row, params.now)) {
|
|
9429
10187
|
return null;
|
|
9430
10188
|
}
|
|
9431
10189
|
return rowToSessionRecord(row);
|
|
@@ -9480,13 +10238,12 @@ class SqliteAuthSessionStore {
|
|
|
9480
10238
|
last_seen_at = ?
|
|
9481
10239
|
WHERE project_id = ?
|
|
9482
10240
|
AND id = ?
|
|
9483
|
-
AND audience = ?
|
|
9484
10241
|
AND token_hash = ?
|
|
9485
10242
|
AND refresh_token_hash IS NULL
|
|
9486
10243
|
AND revoked_at IS NULL
|
|
9487
10244
|
AND expires_at > ?
|
|
9488
10245
|
AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
|
|
9489
|
-
`).run(expiresAt, expiresAt, now, params.projectId, params.id, params.
|
|
10246
|
+
`).run(expiresAt, expiresAt, now, params.projectId, params.id, params.tokenHash, now, now);
|
|
9490
10247
|
return result.changes === 0 ? null : getSessionById(this.db, params);
|
|
9491
10248
|
});
|
|
9492
10249
|
}
|
|
@@ -9679,9 +10436,6 @@ class SqliteAuthStorage {
|
|
|
9679
10436
|
constructor(options = {}) {
|
|
9680
10437
|
this.connection = openSqliteStoreConnection(options);
|
|
9681
10438
|
this.db = this.connection.db;
|
|
9682
|
-
if (this.connection.installFreshSchema) {
|
|
9683
|
-
installFreshSqliteSchema(this.db);
|
|
9684
|
-
}
|
|
9685
10439
|
this.users = new SqliteAuthUserStore(this.db);
|
|
9686
10440
|
this.identities = new SqliteAuthUserIdentityStore(this.db);
|
|
9687
10441
|
this.serviceAccounts = new SqliteAuthServiceAccountStore(this.db);
|
|
@@ -9760,7 +10514,6 @@ class SqliteAuthStorage {
|
|
|
9760
10514
|
};
|
|
9761
10515
|
}
|
|
9762
10516
|
validateCompleteSessionInput(this.db, projectId, input.session);
|
|
9763
|
-
assertSignInSessionAudience(projectId, input.session.audience, magicLink.audience);
|
|
9764
10517
|
let newUserId;
|
|
9765
10518
|
if (shouldCreateUser) {
|
|
9766
10519
|
newUserId = assertNonEmpty(input.newUserId, "User id");
|
|
@@ -9857,7 +10610,6 @@ class SqliteAuthStorage {
|
|
|
9857
10610
|
};
|
|
9858
10611
|
}
|
|
9859
10612
|
validateCompleteSessionInput(this.db, projectId, input.session);
|
|
9860
|
-
assertSignInSessionAudience(projectId, input.session.audience, attempt.audience);
|
|
9861
10613
|
let newUserId;
|
|
9862
10614
|
if (shouldCreateUser) {
|
|
9863
10615
|
newUserId = assertNonEmpty(input.newUserId, "User id");
|
|
@@ -10076,7 +10828,6 @@ class SqliteAuthStorage {
|
|
|
10076
10828
|
projectId: input.projectId,
|
|
10077
10829
|
userId: input.userId,
|
|
10078
10830
|
strategyId: input.strategyId,
|
|
10079
|
-
audience: input.session.audience,
|
|
10080
10831
|
tokenHash: input.session.tokenHash,
|
|
10081
10832
|
createdAt: input.session.createdAt,
|
|
10082
10833
|
expiresAt: input.session.expiresAt,
|
|
@@ -10121,12 +10872,6 @@ class SqliteAuthStorage {
|
|
|
10121
10872
|
});
|
|
10122
10873
|
}
|
|
10123
10874
|
}
|
|
10124
|
-
function assertSignInSessionAudience(projectId, sessionAudience, storedAudience) {
|
|
10125
|
-
if (sessionAudience === storedAudience) {
|
|
10126
|
-
return;
|
|
10127
|
-
}
|
|
10128
|
-
throw new AuthStorageError11("invalid_input", `[Sixb] Sign-in session audience '${sessionAudience}' does not match stored auth audience '${storedAudience}' for project '${projectId}'.`);
|
|
10129
|
-
}
|
|
10130
10875
|
function assertCompletableDeviceAuthorization(authorization, input) {
|
|
10131
10876
|
if (authorization.deviceCodeHash !== input.deviceCodeHash) {
|
|
10132
10877
|
throw new AuthStorageError11("invalid_device_authorization", "[Sixb] Invalid device authorization.");
|
|
@@ -10194,7 +10939,7 @@ class SqliteConnectorConnectionPersistence {
|
|
|
10194
10939
|
callback_binding_hash, reauthorization_id, reauthorization_revision,
|
|
10195
10940
|
reauthorization_connection_ids, created_at, expires_at
|
|
10196
10941
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10197
|
-
`).run(record.projectId, record.connectorId, record.id, record.slot, record.initiatedByExecutionId, record.stateHash, record.codeVerifier === undefined ? null : json(record.codeVerifier), record.redirectUri, record.connectionRunId ?? null, record.returnTo ?? null, record.callbackBindingHash ?? null, record.reauthorizationId ?? null, record.reauthorizationRevision ?? null, record.reauthorizationConnectionIds ? json(record.reauthorizationConnectionIds) : null,
|
|
10942
|
+
`).run(record.projectId, record.connectorId, record.id, record.slot, record.initiatedByExecutionId, record.stateHash, record.codeVerifier === undefined ? null : json(record.codeVerifier), record.redirectUri, record.connectionRunId ?? null, record.returnTo ?? null, record.callbackBindingHash ?? null, record.reauthorizationId ?? null, record.reauthorizationRevision ?? null, record.reauthorizationConnectionIds ? json(record.reauthorizationConnectionIds) : null, iso2(record.createdAt), iso2(record.expiresAt));
|
|
10198
10943
|
return true;
|
|
10199
10944
|
} catch (error) {
|
|
10200
10945
|
if (isUniqueConstraintError(error))
|
|
@@ -10285,7 +11030,7 @@ class SqliteConnectorConnectionPersistence {
|
|
|
10285
11030
|
project_id, connector_id, id, authorization_id, slot, account,
|
|
10286
11031
|
account_id, status, disconnected_at, created_at, updated_at
|
|
10287
11032
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10288
|
-
`).run(record.projectId, record.connectorId, record.id, record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt),
|
|
11033
|
+
`).run(record.projectId, record.connectorId, record.id, record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt), iso2(record.createdAt), iso2(record.updatedAt));
|
|
10289
11034
|
return true;
|
|
10290
11035
|
} catch (error) {
|
|
10291
11036
|
if (isUniqueConstraintError(error))
|
|
@@ -10337,7 +11082,7 @@ class SqliteConnectorConnectionPersistence {
|
|
|
10337
11082
|
SET authorization_id = ?, slot = ?, account = ?, account_id = ?,
|
|
10338
11083
|
status = ?, disconnected_at = ?, updated_at = ?
|
|
10339
11084
|
WHERE project_id = ? AND connector_id = ? AND id = ?
|
|
10340
|
-
`).run(record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt),
|
|
11085
|
+
`).run(record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt), iso2(record.updatedAt), record.projectId, record.connectorId, record.id);
|
|
10341
11086
|
assertChanged(result.changes, "connector connection", record.id);
|
|
10342
11087
|
}
|
|
10343
11088
|
insertRun(record) {
|
|
@@ -10349,7 +11094,7 @@ class SqliteConnectorConnectionPersistence {
|
|
|
10349
11094
|
expires_at, authorization_id, cleanup_authorization_id, connections, error,
|
|
10350
11095
|
created_at, updated_at, finished_at
|
|
10351
11096
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10352
|
-
`).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -2),
|
|
11097
|
+
`).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -2), iso2(record.createdAt), ...values.slice(-2));
|
|
10353
11098
|
}
|
|
10354
11099
|
insertAuthorizationRow(record) {
|
|
10355
11100
|
const values = authorizationValues(record);
|
|
@@ -10361,7 +11106,7 @@ class SqliteConnectorConnectionPersistence {
|
|
|
10361
11106
|
mutation_expected_connection_ids, staged_credentials, staged_credential_expires_at,
|
|
10362
11107
|
staged_scopes, created_at, updated_at
|
|
10363
11108
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10364
|
-
`).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -1),
|
|
11109
|
+
`).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -1), iso2(record.createdAt), ...values.slice(-1));
|
|
10365
11110
|
}
|
|
10366
11111
|
getById(table, id) {
|
|
10367
11112
|
if (this.scope.connectorId) {
|
|
@@ -10398,13 +11143,13 @@ function authorizationAttemptFromRow(row) {
|
|
|
10398
11143
|
function runValues(record) {
|
|
10399
11144
|
const waitingFor = record.status === "waiting" ? record.waitingFor : null;
|
|
10400
11145
|
const processingId = record.status === "running" ? record.processingId : null;
|
|
10401
|
-
const callbackStartedAt = record.status === "running" ?
|
|
10402
|
-
const expiresAt = record.status === "waiting" || record.status === "running" ?
|
|
11146
|
+
const callbackStartedAt = record.status === "running" ? iso2(record.callbackStartedAt) : null;
|
|
11147
|
+
const expiresAt = record.status === "waiting" || record.status === "running" ? iso2(record.expiresAt) : null;
|
|
10403
11148
|
const authorizationId = "authorizationId" in record ? record.authorizationId ?? null : null;
|
|
10404
11149
|
const cleanupAuthorizationId = record.status === "succeeded" ? record.cleanupAuthorizationId ?? null : null;
|
|
10405
11150
|
const connections = record.status === "succeeded" ? json(record.connections) : null;
|
|
10406
11151
|
const error = record.status === "failed" ? json(record.error) : null;
|
|
10407
|
-
const finishedAt = record.status === "succeeded" || record.status === "failed" || record.status === "cancelled" || record.status === "expired" ?
|
|
11152
|
+
const finishedAt = record.status === "succeeded" || record.status === "failed" || record.status === "cancelled" || record.status === "expired" ? iso2(record.finishedAt) : null;
|
|
10408
11153
|
return [
|
|
10409
11154
|
record.kind,
|
|
10410
11155
|
record.slot,
|
|
@@ -10418,7 +11163,7 @@ function runValues(record) {
|
|
|
10418
11163
|
cleanupAuthorizationId,
|
|
10419
11164
|
connections,
|
|
10420
11165
|
error,
|
|
10421
|
-
|
|
11166
|
+
iso2(record.updatedAt),
|
|
10422
11167
|
finishedAt
|
|
10423
11168
|
];
|
|
10424
11169
|
}
|
|
@@ -10521,7 +11266,7 @@ function authorizationValues(record) {
|
|
|
10521
11266
|
staged ? json(staged.credentials) : null,
|
|
10522
11267
|
optionalIso(staged?.credentialExpiresAt),
|
|
10523
11268
|
staged ? json(staged.scopes) : null,
|
|
10524
|
-
|
|
11269
|
+
iso2(record.updatedAt)
|
|
10525
11270
|
];
|
|
10526
11271
|
}
|
|
10527
11272
|
function authorizationFromRow(row) {
|
|
@@ -10596,7 +11341,7 @@ function json(value) {
|
|
|
10596
11341
|
function parseJson(value) {
|
|
10597
11342
|
return JSON.parse(value);
|
|
10598
11343
|
}
|
|
10599
|
-
function
|
|
11344
|
+
function iso2(value) {
|
|
10600
11345
|
return value.toISOString();
|
|
10601
11346
|
}
|
|
10602
11347
|
function optionalIso(value) {
|
|
@@ -10618,6 +11363,7 @@ function assertChanged(changes, label, id) {
|
|
|
10618
11363
|
|
|
10619
11364
|
// src/execution-storage.ts
|
|
10620
11365
|
import {
|
|
11366
|
+
compareExecutionCreation,
|
|
10621
11367
|
executionRecordFromStorageRow,
|
|
10622
11368
|
executionRecordToStorageRow,
|
|
10623
11369
|
prepareExecutionRecord
|
|
@@ -10684,6 +11430,39 @@ class SqliteExecutionStorage {
|
|
|
10684
11430
|
`).get(params.projectId, params.id);
|
|
10685
11431
|
return row ? sqliteExecutionRecord(row) : null;
|
|
10686
11432
|
}
|
|
11433
|
+
async listBySession(input) {
|
|
11434
|
+
const sessionColumn = input.session.type === "user" ? "authority_session_id" : "authority_delegation_session_id";
|
|
11435
|
+
const rows = this.db.query(`
|
|
11436
|
+
WITH RECURSIVE tree AS (
|
|
11437
|
+
SELECT roots.*, 0 AS depth
|
|
11438
|
+
FROM (
|
|
11439
|
+
SELECT *
|
|
11440
|
+
FROM executions
|
|
11441
|
+
WHERE project_id = ?
|
|
11442
|
+
AND ${sessionColumn} = ?
|
|
11443
|
+
AND parent_execution_id IS NULL
|
|
11444
|
+
AND created_at >= ?
|
|
11445
|
+
AND created_at < ?
|
|
11446
|
+
ORDER BY created_at, id
|
|
11447
|
+
LIMIT ?
|
|
11448
|
+
) roots
|
|
11449
|
+
UNION ALL
|
|
11450
|
+
SELECT child.*, tree.depth + 1
|
|
11451
|
+
FROM tree
|
|
11452
|
+
CROSS JOIN executions child
|
|
11453
|
+
ON child.project_id = tree.project_id
|
|
11454
|
+
AND child.parent_execution_id = tree.id
|
|
11455
|
+
ORDER BY depth, created_at, id
|
|
11456
|
+
LIMIT ?
|
|
11457
|
+
)
|
|
11458
|
+
SELECT * FROM tree
|
|
11459
|
+
`).all(input.projectId, input.session.id, input.from.toISOString(), input.to.toISOString(), input.limit + 1, input.limit + 1);
|
|
11460
|
+
const kept = rows.slice(0, input.limit).map(sqliteExecutionRecord);
|
|
11461
|
+
return {
|
|
11462
|
+
executions: kept.sort(compareExecutionCreation),
|
|
11463
|
+
truncated: rows.length > input.limit
|
|
11464
|
+
};
|
|
11465
|
+
}
|
|
10687
11466
|
}
|
|
10688
11467
|
function sqliteExecutionRecord(row) {
|
|
10689
11468
|
return executionRecordFromStorageRow(toStorageRow(row));
|
|
@@ -12785,9 +13564,6 @@ class SqliteObjectStorage {
|
|
|
12785
13564
|
this.connection = openSqliteStoreConnection(options);
|
|
12786
13565
|
this.db = this.connection.db;
|
|
12787
13566
|
this.reader = new SqliteObjectReader(this.db, DEFAULT_OBJECT_QUERY_SOURCE);
|
|
12788
|
-
if (this.connection.installFreshSchema) {
|
|
12789
|
-
installFreshSqliteSchema(this.db);
|
|
12790
|
-
}
|
|
12791
13567
|
}
|
|
12792
13568
|
queryCapabilities() {
|
|
12793
13569
|
return SQLITE_OBJECT_QUERY_CAPABILITIES;
|
|
@@ -15822,6 +16598,26 @@ class SqliteOntologySourceStorage {
|
|
|
15822
16598
|
return row ? sourceRecord(row) : null;
|
|
15823
16599
|
});
|
|
15824
16600
|
}
|
|
16601
|
+
async listActive(input) {
|
|
16602
|
+
return this.runRootOperation(() => {
|
|
16603
|
+
assertNonblank(input.projectId, "Source project id");
|
|
16604
|
+
const rows = this.db.query(`
|
|
16605
|
+
SELECT source_id, dataset_id, dataset_version_id, dataset_version_created_at,
|
|
16606
|
+
projection_revision
|
|
16607
|
+
FROM ontology_sources
|
|
16608
|
+
WHERE project_id = ? AND status = 'active'
|
|
16609
|
+
`).all(input.projectId);
|
|
16610
|
+
return rows.map((row) => ({
|
|
16611
|
+
source: { projectionId: row.source_id },
|
|
16612
|
+
datasetVersion: {
|
|
16613
|
+
datasetId: row.dataset_id,
|
|
16614
|
+
versionId: row.dataset_version_id,
|
|
16615
|
+
createdAt: row.dataset_version_created_at
|
|
16616
|
+
},
|
|
16617
|
+
projectionRevision: row.projection_revision
|
|
16618
|
+
}));
|
|
16619
|
+
});
|
|
16620
|
+
}
|
|
15825
16621
|
async adopt(input) {
|
|
15826
16622
|
return this.runRootOperation(() => {
|
|
15827
16623
|
assertProjectAndSource(input);
|
|
@@ -16330,9 +17126,6 @@ class SqlitePipelineRunStorage {
|
|
|
16330
17126
|
this.connection = openSqliteStoreConnection(options);
|
|
16331
17127
|
this.db = this.connection.db;
|
|
16332
17128
|
this.executions = options.executions;
|
|
16333
|
-
if (this.connection.installFreshSchema) {
|
|
16334
|
-
installFreshSqliteSchema(this.db);
|
|
16335
|
-
}
|
|
16336
17129
|
}
|
|
16337
17130
|
async queue(input) {
|
|
16338
17131
|
const queuedAt = input.queuedAt ?? new Date;
|
|
@@ -16678,9 +17471,6 @@ class SqliteProjectionRunStorage {
|
|
|
16678
17471
|
this.connection = openSqliteStoreConnection(options);
|
|
16679
17472
|
this.db = this.connection.db;
|
|
16680
17473
|
this.executions = options.executions;
|
|
16681
|
-
if (this.connection.installFreshSchema) {
|
|
16682
|
-
installFreshSqliteSchema(this.db);
|
|
16683
|
-
}
|
|
16684
17474
|
}
|
|
16685
17475
|
async queue(input) {
|
|
16686
17476
|
assertProjectionRunQueueInput(input);
|
|
@@ -17124,9 +17914,6 @@ class SqliteRulesStorage {
|
|
|
17124
17914
|
constructor(options = {}) {
|
|
17125
17915
|
this.connection = openSqliteStoreConnection(options);
|
|
17126
17916
|
this.db = this.connection.db;
|
|
17127
|
-
if (this.connection.installFreshSchema) {
|
|
17128
|
-
installFreshSqliteSchema(this.db);
|
|
17129
|
-
}
|
|
17130
17917
|
}
|
|
17131
17918
|
async getActive(params) {
|
|
17132
17919
|
const row = this.db.query(`
|
|
@@ -17372,8 +18159,6 @@ class SqliteShareGrantStorage {
|
|
|
17372
18159
|
constructor(options = {}) {
|
|
17373
18160
|
this.connection = openSqliteStoreConnection(options);
|
|
17374
18161
|
this.db = this.connection.db;
|
|
17375
|
-
if (this.connection.installFreshSchema)
|
|
17376
|
-
installFreshSqliteSchema(this.db);
|
|
17377
18162
|
}
|
|
17378
18163
|
async create(input) {
|
|
17379
18164
|
const record = normalizeShareGrantCreate(input);
|
|
@@ -17397,6 +18182,23 @@ class SqliteShareGrantStorage {
|
|
|
17397
18182
|
const normalized = normalizeGetShareGrantByIdInput(input);
|
|
17398
18183
|
return this.getRow(normalized.projectId, normalized.id);
|
|
17399
18184
|
}
|
|
18185
|
+
async getByIds(input) {
|
|
18186
|
+
const ids = [...new Set(input.ids)];
|
|
18187
|
+
if (ids.length === 0) {
|
|
18188
|
+
return [];
|
|
18189
|
+
}
|
|
18190
|
+
const rows = this.db.query(`
|
|
18191
|
+
SELECT *
|
|
18192
|
+
FROM share_grants
|
|
18193
|
+
WHERE project_id = ?
|
|
18194
|
+
AND id IN (SELECT value FROM json_each(?))
|
|
18195
|
+
`).all(input.projectId, JSON.stringify(ids));
|
|
18196
|
+
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
18197
|
+
return ids.flatMap((id) => {
|
|
18198
|
+
const row = byId.get(id);
|
|
18199
|
+
return row ? [rowToRecord(row)] : [];
|
|
18200
|
+
});
|
|
18201
|
+
}
|
|
17400
18202
|
async list(input) {
|
|
17401
18203
|
const normalized = normalizeListShareGrantsInput(input);
|
|
17402
18204
|
const where = ["project_id = ?"];
|
|
@@ -17518,8 +18320,6 @@ class SqliteShareSessionStorage {
|
|
|
17518
18320
|
constructor(options = {}) {
|
|
17519
18321
|
this.connection = openSqliteStoreConnection(options);
|
|
17520
18322
|
this.db = this.connection.db;
|
|
17521
|
-
if (this.connection.installFreshSchema)
|
|
17522
|
-
installFreshSqliteSchema(this.db);
|
|
17523
18323
|
}
|
|
17524
18324
|
async create(input) {
|
|
17525
18325
|
const record = normalizeShareSessionCreate(input);
|
|
@@ -17622,9 +18422,6 @@ class SqliteSyncRunStorage {
|
|
|
17622
18422
|
this.connection = openSqliteStoreConnection(options);
|
|
17623
18423
|
this.db = this.connection.db;
|
|
17624
18424
|
this.executions = options.executions;
|
|
17625
|
-
if (this.connection.installFreshSchema) {
|
|
17626
|
-
installFreshSqliteSchema(this.db);
|
|
17627
|
-
}
|
|
17628
18425
|
}
|
|
17629
18426
|
async queue(input) {
|
|
17630
18427
|
const queuedAt = input.queuedAt ?? new Date;
|
|
@@ -17834,9 +18631,6 @@ class SqliteTimeseriesStorage {
|
|
|
17834
18631
|
constructor(options = {}) {
|
|
17835
18632
|
this.connection = openSqliteStoreConnection(options);
|
|
17836
18633
|
this.db = this.connection.db;
|
|
17837
|
-
if (this.connection.installFreshSchema) {
|
|
17838
|
-
installFreshSqliteSchema(this.db);
|
|
17839
|
-
}
|
|
17840
18634
|
}
|
|
17841
18635
|
async getHistory(params) {
|
|
17842
18636
|
let query = `
|
|
@@ -17963,8 +18757,6 @@ class SqliteWebhookRunStorage {
|
|
|
17963
18757
|
this.connection = openSqliteStoreConnection(options);
|
|
17964
18758
|
this.db = this.connection.db;
|
|
17965
18759
|
this.executions = options.executions;
|
|
17966
|
-
if (this.connection.installFreshSchema)
|
|
17967
|
-
installFreshSqliteSchema(this.db);
|
|
17968
18760
|
}
|
|
17969
18761
|
async start(input) {
|
|
17970
18762
|
const startedAt = input.startedAt ?? new Date;
|
|
@@ -18110,9 +18902,6 @@ class SqliteWorkflowInterventionStorage {
|
|
|
18110
18902
|
constructor(options = {}) {
|
|
18111
18903
|
this.connection = openSqliteStoreConnection(options);
|
|
18112
18904
|
this.db = this.connection.db;
|
|
18113
|
-
if (this.connection.installFreshSchema) {
|
|
18114
|
-
installFreshSqliteSchema(this.db);
|
|
18115
|
-
}
|
|
18116
18905
|
}
|
|
18117
18906
|
async create(input) {
|
|
18118
18907
|
assertNonNegativeInteger(input.nodeIndex, "nodeIndex");
|
|
@@ -18347,9 +19136,6 @@ class SqliteWorkflowRunStorage {
|
|
|
18347
19136
|
this.connection = openSqliteStoreConnection(options);
|
|
18348
19137
|
this.db = this.connection.db;
|
|
18349
19138
|
this.executions = options.executions;
|
|
18350
|
-
if (this.connection.installFreshSchema) {
|
|
18351
|
-
installFreshSqliteSchema(this.db);
|
|
18352
|
-
}
|
|
18353
19139
|
this.nodes = new SqliteWorkflowNodeRunStorage(this.db);
|
|
18354
19140
|
this.agentNodes = new SqliteWorkflowAgentNodeRunStorage(this.db, this.executions);
|
|
18355
19141
|
}
|
|
@@ -18499,6 +19285,23 @@ class SqliteWorkflowRunStorage {
|
|
|
18499
19285
|
const row = this.db.query("SELECT * FROM workflow_runs WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
|
|
18500
19286
|
return row ? rowToWorkflowRunRecord(row) : null;
|
|
18501
19287
|
}
|
|
19288
|
+
async getByExecutionIds(params) {
|
|
19289
|
+
const executionIds = [...new Set(params.executionIds)];
|
|
19290
|
+
if (executionIds.length === 0) {
|
|
19291
|
+
return [];
|
|
19292
|
+
}
|
|
19293
|
+
const rows = this.db.query(`
|
|
19294
|
+
SELECT *
|
|
19295
|
+
FROM workflow_runs
|
|
19296
|
+
WHERE project_id = ?
|
|
19297
|
+
AND execution_id IN (SELECT value FROM json_each(?))
|
|
19298
|
+
`).all(params.projectId, JSON.stringify(executionIds));
|
|
19299
|
+
const byExecution = new Map(rows.map((row) => [row.execution_id, row]));
|
|
19300
|
+
return executionIds.flatMap((executionId) => {
|
|
19301
|
+
const row = byExecution.get(executionId);
|
|
19302
|
+
return row ? [rowToWorkflowRunRecord(row)] : [];
|
|
19303
|
+
});
|
|
19304
|
+
}
|
|
18502
19305
|
async list(input) {
|
|
18503
19306
|
if (hasEmptyStatuses(input) || input.workflowIds?.length === 0) {
|
|
18504
19307
|
return {
|
|
@@ -18964,6 +19767,7 @@ class SqliteStorage {
|
|
|
18964
19767
|
auth;
|
|
18965
19768
|
executions;
|
|
18966
19769
|
agents;
|
|
19770
|
+
artifacts;
|
|
18967
19771
|
aiUsage;
|
|
18968
19772
|
aiCosts;
|
|
18969
19773
|
aiLimits;
|
|
@@ -18981,6 +19785,7 @@ class SqliteStorage {
|
|
|
18981
19785
|
connectorConnections;
|
|
18982
19786
|
fileUploadSessions;
|
|
18983
19787
|
fileDownloadGrants;
|
|
19788
|
+
activity;
|
|
18984
19789
|
migrators;
|
|
18985
19790
|
connection;
|
|
18986
19791
|
readConnection;
|
|
@@ -18995,9 +19800,6 @@ class SqliteStorage {
|
|
|
18995
19800
|
} else {
|
|
18996
19801
|
this.readConnection = this.connection;
|
|
18997
19802
|
}
|
|
18998
|
-
if (this.connection.installFreshSchema) {
|
|
18999
|
-
installFreshSqliteSchema(this.connection.db);
|
|
19000
|
-
}
|
|
19001
19803
|
const stores = createSqliteStores(this.connection, {
|
|
19002
19804
|
runOntologyOperation: async (run) => run(),
|
|
19003
19805
|
transactionContext: null
|
|
@@ -19016,6 +19818,7 @@ class SqliteStorage {
|
|
|
19016
19818
|
this.auth = createAuthOperationScope(stores.auth, scope);
|
|
19017
19819
|
this.executions = createOperationScopedFacade(stores.executions, scope);
|
|
19018
19820
|
this.agents = createAgentOperationScope(stores.agents, scope);
|
|
19821
|
+
this.artifacts = createOperationScopedFacade(stores.artifacts, scope);
|
|
19019
19822
|
this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope);
|
|
19020
19823
|
this.aiCosts = createOperationScopedFacade(stores.aiCosts, scope);
|
|
19021
19824
|
this.aiLimits = createOperationScopedFacade(stores.aiLimits, scope);
|
|
@@ -19033,6 +19836,7 @@ class SqliteStorage {
|
|
|
19033
19836
|
this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope);
|
|
19034
19837
|
this.fileUploadSessions = createOperationScopedFacade(stores.fileUploadSessions, scope);
|
|
19035
19838
|
this.fileDownloadGrants = createOperationScopedFacade(stores.fileDownloadGrants, scope);
|
|
19839
|
+
this.activity = createOperationScopedFacade(stores.activity, scope);
|
|
19036
19840
|
this.migrators = options.path ? createSqliteStorageMigrators(options.path) : [];
|
|
19037
19841
|
registerSqliteStorageTestingAdapter(this, {
|
|
19038
19842
|
advanceConnectorConnectionTime: (durationMs) => stores.connectorConnections.advanceTimeForTesting(durationMs),
|
|
@@ -19090,8 +19894,7 @@ class SqliteStorage {
|
|
|
19090
19894
|
return {
|
|
19091
19895
|
...createSqliteStores({
|
|
19092
19896
|
db: this.connection.db,
|
|
19093
|
-
ownsConnection: false
|
|
19094
|
-
installFreshSchema: false
|
|
19897
|
+
ownsConnection: false
|
|
19095
19898
|
}, {
|
|
19096
19899
|
runOntologyOperation: async (run) => run(),
|
|
19097
19900
|
transactionContext
|
|
@@ -19146,6 +19949,7 @@ function createSqliteStores(connection, options) {
|
|
|
19146
19949
|
auth,
|
|
19147
19950
|
executions,
|
|
19148
19951
|
agents: new SqliteAgentStorage({ connection, executions }),
|
|
19952
|
+
artifacts: new SqliteArtifactStorage(connection),
|
|
19149
19953
|
aiUsage: new SqliteAiUsageStorage({ connection }),
|
|
19150
19954
|
aiCosts: new SqliteAiCostStorage(connection),
|
|
19151
19955
|
aiLimits: new SqliteAiLimitStorage({ connection }),
|
|
@@ -19162,7 +19966,8 @@ function createSqliteStores(connection, options) {
|
|
|
19162
19966
|
shareSessions,
|
|
19163
19967
|
connectorConnections: new SqliteConnectorConnectionStorage(connection),
|
|
19164
19968
|
fileUploadSessions: new SqliteFileUploadSessionStorage(connection),
|
|
19165
|
-
fileDownloadGrants: new SqliteFileDownloadGrantStorage(connection)
|
|
19969
|
+
fileDownloadGrants: new SqliteFileDownloadGrantStorage(connection),
|
|
19970
|
+
activity: new SqliteActivityStorage(connection)
|
|
19166
19971
|
};
|
|
19167
19972
|
}
|
|
19168
19973
|
export {
|
|
@@ -19170,4 +19975,4 @@ export {
|
|
|
19170
19975
|
migrateSqliteStorage
|
|
19171
19976
|
};
|
|
19172
19977
|
|
|
19173
|
-
//# debugId=
|
|
19978
|
+
//# debugId=B9F8223963B8B3F664756E2164756E21
|