@paradigma-inc/flywheel 0.2.50 → 0.2.61
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/package.json +1 -1
- package/skills/flywheel-repo/SKILL.md +1 -1
- package/skills/flywheel-repo/references/repository-commands.md +5 -4
- package/src/runtime/required-runtime-commands.json +1 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/hosted.js +2 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/hosted.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/repository.js +87 -19
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry/repository.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry.js +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/registry.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-operation.js +7 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-operation.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-output.js +20 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-output.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-projection.js +5 -2
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-projection.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-refusal-hints.js +20 -17
- package/src/runtime/vendor/flywheel-cli-dist/commands/repository-refusal-hints.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/commands/tombstone-recovery.js +17 -0
- package/src/runtime/vendor/flywheel-cli-dist/commands/tombstone-recovery.js.map +1 -0
- package/src/runtime/vendor/flywheel-cli-dist/errors.js +16 -0
- package/src/runtime/vendor/flywheel-cli-dist/errors.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/adapter.js +77 -25
- package/src/runtime/vendor/flywheel-cli-dist/local/adapter.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/errors.js +9 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/errors.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/rebase/replay.js +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/rebase/replay.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/service/revert.js +16 -16
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/service/revert.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/service/staging.js +9 -2
- package/src/runtime/vendor/flywheel-cli-dist/local/domain/service/staging.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/errors.js +21 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/errors.js.map +1 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/fs/state.js +9 -11
- package/src/runtime/vendor/flywheel-cli-dist/local/fs/state.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/fs/writer-lock.js +5 -10
- package/src/runtime/vendor/flywheel-cli-dist/local/fs/writer-lock.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/bootstrap.js +2 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/bootstrap.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/dispatch.js +2 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/dispatch.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/operations/core.js +3 -3
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/operations/core.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/operations/maintenance.js +7 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/operations/maintenance.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/operations/projections.js +1 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/operations/projections.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/projection.js +44 -8
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/projection.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/recover-departed.js +8 -2
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/recover-departed.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/remote/purge.js +100 -5
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/remote/purge.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/remote/push.js +8 -4
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/remote/push.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/snapshot-diff.js +2 -5
- package/src/runtime/vendor/flywheel-cli-dist/local/runtime/snapshot-diff.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/apply.js +6 -2
- package/src/runtime/vendor/flywheel-cli-dist/local/store/apply.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/archive.js +1 -8
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/archive.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/backup.js +2 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/backup.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/restore.js +2 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/restore.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/retained-directory.js +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/backup/retained-directory.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migrate.js +4 -4
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migrate.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migration/steps.js +8 -3
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migration/steps.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migration/versions.js +2 -2
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migration/versions.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migrations/009_repository_backup_state.sql +8 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migrations.js +1 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/store/migrations.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/pending.js +6 -2
- package/src/runtime/vendor/flywheel-cli-dist/local/store/pending.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/projection/nodes.js +33 -16
- package/src/runtime/vendor/flywheel-cli-dist/local/store/projection/nodes.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/projection/rules.js +87 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/projection/rules.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/snapshot/replace.js +51 -12
- package/src/runtime/vendor/flywheel-cli-dist/local/store/snapshot/replace.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sqlite/errors.js +24 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sqlite/errors.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sqlite/schema.js +1 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sqlite/schema.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sqlite/store.js +38 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sqlite/store.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sync.js +4 -0
- package/src/runtime/vendor/flywheel-cli-dist/local/store/sync.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/workspace.js +7 -1
- package/src/runtime/vendor/flywheel-cli-dist/local/store/workspace.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/main.js +83 -82
- package/src/runtime/vendor/flywheel-cli-dist/main.js.map +1 -1
- package/src/runtime/vendor/flywheel-cli-dist/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/src/runtime/vendor/flywheel-cli-dist/prebuilds/darwin-x64/node.napi.node +0 -0
- package/src/runtime/vendor/manifest.json +6 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { migrationNames } from "../migrations.js";
|
|
2
|
-
export const CURRENT_REPOSITORY_USER_VERSION =
|
|
3
|
-
export const PREDECESSOR_REPOSITORY_USER_VERSIONS = [1, 2, 3, 4, 5, 6, 7];
|
|
2
|
+
export const CURRENT_REPOSITORY_USER_VERSION = 9;
|
|
3
|
+
export const PREDECESSOR_REPOSITORY_USER_VERSIONS = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
4
4
|
export function isPredecessorRepositoryUserVersion(value) {
|
|
5
5
|
return PREDECESSOR_REPOSITORY_USER_VERSIONS.includes(value);
|
|
6
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versions.js","sourceRoot":"","sources":["../../../../src/local/store/migration/versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AACjD,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"versions.js","sourceRoot":"","sources":["../../../../src/local/store/migration/versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AACjD,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAU,CAAC;AAItF,MAAM,UAAU,kCAAkC,CAAC,KAAa;IAC9D,OAAO,oCAAoC,CAAC,QAAQ,CAAC,KAAyC,CAAC,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAyC;IAC/E,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
|
package/src/runtime/vendor/flywheel-cli-dist/local/store/migrations/009_repository_backup_state.sql
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
CREATE TABLE repository_backup_state (
|
|
2
|
+
repository_id TEXT PRIMARY KEY NOT NULL REFERENCES repository_metadata(repository_id),
|
|
3
|
+
completed_at TEXT NOT NULL CHECK (
|
|
4
|
+
completed_at GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9][0-9][0-9][0-9]Z'
|
|
5
|
+
),
|
|
6
|
+
archive_digest TEXT NOT NULL CHECK (length(archive_digest) = 71 AND substr(archive_digest, 1, 7) = 'sha256:' AND substr(archive_digest, 8) NOT GLOB '*[^0-9a-f]*')
|
|
7
|
+
);
|
|
8
|
+
PRAGMA user_version = 9;
|
|
@@ -8,6 +8,7 @@ export const migrationNames = [
|
|
|
8
8
|
"006_sync_journal.sql",
|
|
9
9
|
"007_repository_node_source_timestamps.sql",
|
|
10
10
|
"008_boundaries_departures_and_author.sql",
|
|
11
|
+
"009_repository_backup_state.sql",
|
|
11
12
|
];
|
|
12
13
|
export function loadMigration(name) {
|
|
13
14
|
return readFileSync(new URL(`./migrations/${name}`, import.meta.url), "utf8");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/local/store/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,iBAAiB;IACjB,8BAA8B;IAC9B,kCAAkC;IAClC,4BAA4B;IAC5B,6BAA6B;IAC7B,sBAAsB;IACtB,2CAA2C;IAC3C,0CAA0C;
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/local/store/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,iBAAiB;IACjB,8BAA8B;IAC9B,kCAAkC;IAClC,4BAA4B;IAC5B,6BAA6B;IAC7B,sBAAsB;IACtB,2CAA2C;IAC3C,0CAA0C;IAC1C,iCAAiC;CACzB,CAAC;AAIX,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,OAAO,YAAY,CAAC,IAAI,GAAG,CAAC,gBAAgB,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;AAChF,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DatabaseSync } from "node:sqlite";
|
|
2
2
|
import { iterateOperationLatestValues } from "./latest-values.js";
|
|
3
3
|
import { applyOperations } from "./projection/apply.js";
|
|
4
|
+
import { assertNoDuplicateSemanticEdges, assertTombstonesReleaseLineage } from "./projection/rules.js";
|
|
4
5
|
import { assertProjectionInvariants } from "./snapshot/replace.js";
|
|
5
6
|
import { snapshotTablesFromConnection } from "./snapshot/read.js";
|
|
6
7
|
import { SNAPSHOT_TABLES } from "./snapshot/schema.js";
|
|
@@ -60,6 +61,7 @@ function withPendingGraph(repositoryId, identity, baseHeadCommitId, baseTables,
|
|
|
60
61
|
runSql(markLocal, repositoryId, objectDigest);
|
|
61
62
|
}
|
|
62
63
|
assertProjectionInvariants(database, repositoryId);
|
|
64
|
+
assertTombstonesReleaseLineage(database, repositoryId, staging.operations);
|
|
63
65
|
return render(database);
|
|
64
66
|
}
|
|
65
67
|
finally {
|
|
@@ -79,12 +81,14 @@ function applyPendingSnapshot(repositoryId, identity, baseHeadCommitId, baseTabl
|
|
|
79
81
|
/**
|
|
80
82
|
* Reject an invalid batch against its committed base without exposing the
|
|
81
83
|
* rendered pending graph; the append path only needs pass/fail plus the
|
|
82
|
-
* first-failing-operation attribution `applyOperations` already raises.
|
|
84
|
+
* first-failing-operation attribution `applyOperations` already raises. Only
|
|
85
|
+
* this new-write path refuses a duplicated semantic edge relation; rendering a
|
|
86
|
+
* batch saved earlier still shows it.
|
|
83
87
|
*/
|
|
84
88
|
export function validatePendingBatch(repositoryId, identity, baseHeadCommitId, baseTables, staging, committedOperationIds) {
|
|
85
89
|
if (staging.operations.length === 0 && staging.latestValues.length === 0)
|
|
86
90
|
return;
|
|
87
|
-
withPendingGraph(repositoryId, identity, baseHeadCommitId, validateSnapshotTables(repositoryId, baseTables), staging, committedOperationIds,
|
|
91
|
+
withPendingGraph(repositoryId, identity, baseHeadCommitId, validateSnapshotTables(repositoryId, baseTables), staging, committedOperationIds, database => assertNoDuplicateSemanticEdges(database, repositoryId, staging.operations));
|
|
88
92
|
}
|
|
89
93
|
export function pendingRepositorySnapshot(repositoryId, identity, baseHeadCommitId, baseTables, staging, committedOperationIds) {
|
|
90
94
|
const tables = validateSnapshotTables(repositoryId, baseTables);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending.js","sourceRoot":"","sources":["../../../src/local/store/pending.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"pending.js","sourceRoot":"","sources":["../../../src/local/store/pending.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RepositoryStoreError } from "../sqlite/errors.js";
|
|
2
|
-
import { getRow, runSql } from "../sqlite/rows.js";
|
|
2
|
+
import { allRows, getRow, runSql } from "../sqlite/rows.js";
|
|
3
3
|
import { RepositoryCommit } from "../../domain/models/commits.js";
|
|
4
4
|
const NODE_FIELDS = ["title", "slug_name", "summary", "content", "origin_host", "external_transcript_ref"];
|
|
5
5
|
function stringValue(value) {
|
|
@@ -41,20 +41,37 @@ export function applyNodeFieldSet(database, commit, operation, latestValue) {
|
|
|
41
41
|
SET ${field} = ?, ${field}_digest = ?, updated_by_commit_id = ?
|
|
42
42
|
WHERE repository_id = ? AND node_id = ?`), latestValue.value, replacement, commit.commitId, commit.repositoryId, nodeId);
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Every relationship in this repository that references the node, in a stable
|
|
46
|
+
* order. Purge requires none; relations held by other repositories are not
|
|
47
|
+
* visible here and are settled by the hosted purge.
|
|
48
|
+
*/
|
|
49
|
+
export function structuralDependencies(database, repositoryId, nodeId) {
|
|
50
|
+
const rows = allRows(database.prepare(`SELECT * FROM (
|
|
51
|
+
SELECT 1 AS rank, parent_repository_id AS a, parent_node_id AS b, child_node_id AS c, NULL AS d FROM repository_lineage
|
|
47
52
|
WHERE repository_id = ? AND ((parent_repository_id = ? AND parent_node_id = ?) OR child_node_id = ?)
|
|
48
|
-
UNION ALL SELECT containment_id
|
|
49
|
-
|
|
50
|
-
UNION ALL SELECT
|
|
51
|
-
|
|
53
|
+
UNION ALL SELECT 2, containment_id, container_node_id, child_node_id, NULL FROM repository_containments
|
|
54
|
+
WHERE repository_id = ? AND (container_node_id = ? OR child_node_id = ?)
|
|
55
|
+
UNION ALL SELECT 3, edge_id, graph_node_id, source_node_id, target_node_id FROM repository_semantic_edges
|
|
56
|
+
WHERE repository_id = ? AND (graph_node_id = ? OR source_node_id = ? OR target_node_id = ?)
|
|
57
|
+
UNION ALL SELECT 4, tag_id, scope_node_id, NULL, NULL FROM repository_tag_definitions
|
|
58
|
+
WHERE repository_id = ? AND scope_node_id = ? AND state = 'active'
|
|
59
|
+
UNION ALL SELECT 5, tag_id, scope_repository_id, scope_node_id, node_id FROM repository_tag_assignments
|
|
52
60
|
WHERE repository_id = ? AND ((scope_repository_id = ? AND scope_node_id = ?) OR node_id = ?)
|
|
53
|
-
UNION ALL SELECT artifact_id FROM repository_artifact_references WHERE repository_id = ? AND node_owner_id = ?
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
UNION ALL SELECT 6, artifact_id, NULL, NULL, NULL FROM repository_artifact_references WHERE repository_id = ? AND node_owner_id = ?
|
|
62
|
+
) ORDER BY rank, a, b, c, d`), repositoryId, repositoryId, nodeId, nodeId, repositoryId, nodeId, nodeId, repositoryId, nodeId, nodeId, nodeId, repositoryId, nodeId, repositoryId, repositoryId, nodeId, nodeId, repositoryId, nodeId);
|
|
63
|
+
return rows.map((row) => {
|
|
64
|
+
const [a, b, c, d] = [row.a, row.b, row.c, row.d].map(String);
|
|
65
|
+
switch (row.rank) {
|
|
66
|
+
case 1: return { kind: "lineage", parent_repository_id: a, parent_node_id: b, child_node_id: c };
|
|
67
|
+
case 2: return { kind: "containment", containment_id: a, container_node_id: b, child_node_id: c };
|
|
68
|
+
case 3: return { kind: "semantic_edge", edge_id: a, graph_node_id: b, source_node_id: c, target_node_id: d };
|
|
69
|
+
case 4: return { kind: "tag_definition", tag_id: a, scope_node_id: b };
|
|
70
|
+
case 5: return { kind: "tag_assignment", tag_id: a, scope_repository_id: b, scope_node_id: c, node_id: d };
|
|
71
|
+
case 6: return { kind: "artifact", artifact_id: a };
|
|
72
|
+
default: throw new RepositoryStoreError("structural dependency row is invalid");
|
|
73
|
+
}
|
|
74
|
+
});
|
|
58
75
|
}
|
|
59
76
|
function assertRestoreStructure(database, repositoryId, nodeId) {
|
|
60
77
|
// Retained semantic edges do not block restoring one endpoint while the
|
|
@@ -98,7 +115,7 @@ export function applyNodeLifecycle(database, commit, operation) {
|
|
|
98
115
|
throw new RepositoryStoreError("invalid node lifecycle operation");
|
|
99
116
|
if (operation.type === "node.restore.v1")
|
|
100
117
|
assertRestoreStructure(database, commit.repositoryId, nodeId);
|
|
101
|
-
else if (revertsNodeCreate(database, commit, nodeId) &&
|
|
118
|
+
else if (revertsNodeCreate(database, commit, nodeId) && structuralDependencies(database, commit.repositoryId, nodeId).length !== 0) {
|
|
102
119
|
throw new RepositoryStoreError("node create revert has new live structural dependencies");
|
|
103
120
|
}
|
|
104
121
|
const next = operation.type === "node.tombstone.v1" ? "tombstoned" : "live";
|
|
@@ -117,9 +134,9 @@ export function applyNodePurge(database, commit, operation) {
|
|
|
117
134
|
if (nodeId !== confirmation || expectedHead !== commit.expectedHead) {
|
|
118
135
|
throw new RepositoryStoreError("node purge confirmation or head is invalid");
|
|
119
136
|
}
|
|
120
|
-
|
|
121
|
-
if (dependencies !== 0n)
|
|
137
|
+
if (structuralDependencies(database, commit.repositoryId, nodeId).length !== 0) {
|
|
122
138
|
throw new RepositoryStoreError("node purge has live structural dependencies");
|
|
139
|
+
}
|
|
123
140
|
if (runSql(database.prepare("UPDATE repository_nodes SET lifecycle_state = 'purged', lifecycle_commit_id = ? WHERE repository_id = ? AND node_id = ? AND lifecycle_state = 'tombstoned'"), commit.commitId, commit.repositoryId, nodeId).changes !== 1)
|
|
124
141
|
throw new RepositoryStoreError("node purge projection precondition failed");
|
|
125
142
|
runSql(database.prepare("DELETE FROM repository_node_latest_values WHERE repository_id = ? AND node_id = ?"), commit.repositoryId, nodeId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.js","sourceRoot":"","sources":["../../../../src/local/store/projection/nodes.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"nodes.js","sourceRoot":"","sources":["../../../../src/local/store/projection/nodes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { RepositoryServiceError } from "../../domain/errors.js";
|
|
1
2
|
import { validateAcyclicRelation, validateSemanticEdgeScale } from "../../domain/invariants.js";
|
|
2
|
-
import { RepositoryStoreError } from "../sqlite/errors.js";
|
|
3
|
+
import { RepositoryNodeTombstoneBlockedError, RepositoryStoreError } from "../sqlite/errors.js";
|
|
3
4
|
import { allRows, getRow } from "../sqlite/rows.js";
|
|
4
5
|
function asString(value) {
|
|
5
6
|
return typeof value === "string" ? value : undefined;
|
|
@@ -13,6 +14,45 @@ export function assertLiveNodes(database, repositoryId, nodeIds) {
|
|
|
13
14
|
throw new RepositoryStoreError("structural relation requires live repository nodes");
|
|
14
15
|
}
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* New work may not leave lineage on a node it tombstones. Only the finished
|
|
19
|
+
* batch counts: a link removed later in the batch, or a node restored before
|
|
20
|
+
* the batch ends, does not block, and history already accepted before the
|
|
21
|
+
* batch is never re-checked. A node's links are its incoming relations and the
|
|
22
|
+
* outgoing relations it parents in this repository; a same-ID parent held by
|
|
23
|
+
* another repository is a different node.
|
|
24
|
+
*/
|
|
25
|
+
export function assertTombstonesReleaseLineage(database, repositoryId, operations) {
|
|
26
|
+
// A node tombstoned, restored and tombstoned again is left tombstoned by its last tombstone.
|
|
27
|
+
const lastTombstones = new Map();
|
|
28
|
+
for (const operation of operations) {
|
|
29
|
+
if (operation.type === "node.tombstone.v1")
|
|
30
|
+
lastTombstones.set(String(operation.payload.node_id), operation);
|
|
31
|
+
}
|
|
32
|
+
if (lastTombstones.size === 0)
|
|
33
|
+
return;
|
|
34
|
+
const lifecycle = database.prepare("SELECT lifecycle_state FROM repository_nodes WHERE repository_id = ? AND node_id = ?");
|
|
35
|
+
const links = database.prepare(`SELECT parent_repository_id, parent_node_id, child_node_id FROM repository_lineage
|
|
36
|
+
WHERE repository_id = ? AND (child_node_id = ? OR (parent_repository_id = repository_id AND parent_node_id = ?))
|
|
37
|
+
ORDER BY parent_repository_id, parent_node_id, child_node_id`);
|
|
38
|
+
for (const operation of operations) {
|
|
39
|
+
if (operation.type !== "node.tombstone.v1")
|
|
40
|
+
continue;
|
|
41
|
+
const nodeId = String(operation.payload.node_id);
|
|
42
|
+
if (lastTombstones.get(nodeId) !== operation)
|
|
43
|
+
continue;
|
|
44
|
+
if (getRow(lifecycle, repositoryId, nodeId)?.lifecycle_state !== "tombstoned")
|
|
45
|
+
continue;
|
|
46
|
+
const rows = allRows(links, repositoryId, nodeId, nodeId);
|
|
47
|
+
if (rows.length === 0)
|
|
48
|
+
continue;
|
|
49
|
+
throw new RepositoryNodeTombstoneBlockedError(repositoryId, nodeId, operation.operationId, rows.map(row => ({
|
|
50
|
+
parentRepositoryId: String(row.parent_repository_id),
|
|
51
|
+
parentNodeId: String(row.parent_node_id),
|
|
52
|
+
childNodeId: String(row.child_node_id),
|
|
53
|
+
})));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
16
56
|
export function assertAcyclicRelation(database, repositoryId, parentId, childId, table) {
|
|
17
57
|
if (parentId === childId) {
|
|
18
58
|
throw new RepositoryStoreError(`${table === "repository_lineage" ? "lineage" : "containment"} cycle is invalid`);
|
|
@@ -86,4 +126,50 @@ export function assertAllSemanticEdgesValid(database, repositoryId) {
|
|
|
86
126
|
assertSemanticEdgeValid(database, repositoryId, graphId, sourceId, targetId);
|
|
87
127
|
}
|
|
88
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Refuses a batch whose resulting graph gives one relation (graph, source,
|
|
131
|
+
* target and relation digest) a second semantic edge. Only edges this batch
|
|
132
|
+
* added are checked, after the whole batch applied, so an edge removed in the
|
|
133
|
+
* same batch can be replaced and history accepted before this rule keeps its
|
|
134
|
+
* parallel edges. One query covers the batch and reads each graph the batch
|
|
135
|
+
* added edges to once.
|
|
136
|
+
*/
|
|
137
|
+
export function assertNoDuplicateSemanticEdges(database, repositoryId, operations) {
|
|
138
|
+
const added = operations.flatMap(operation => operation.type === "edge.add.v1" ? [operation.payload.edge_id] : []);
|
|
139
|
+
if (added.length === 0)
|
|
140
|
+
return;
|
|
141
|
+
// The materialized sets let SQLite build temporary indexes to match them,
|
|
142
|
+
// instead of scanning a whole graph once per added edge. Of two edges for one
|
|
143
|
+
// relation, the one added later in the batch is the proposed duplicate; an
|
|
144
|
+
// edge from before the batch is always the existing one.
|
|
145
|
+
const row = getRow(database.prepare(`WITH added(edge_id, position) AS MATERIALIZED (SELECT value, key FROM json_each(?1)),
|
|
146
|
+
proposed AS MATERIALIZED (
|
|
147
|
+
SELECT edge.edge_id, edge.graph_node_id, edge.source_node_id, edge.target_node_id, edge.relation_digest, added.position
|
|
148
|
+
FROM added CROSS JOIN repository_semantic_edges AS edge
|
|
149
|
+
ON edge.repository_id = ?2 AND edge.edge_id = added.edge_id),
|
|
150
|
+
existing AS MATERIALIZED (
|
|
151
|
+
SELECT edge.edge_id, edge.graph_node_id, edge.source_node_id, edge.target_node_id, edge.relation_digest, added.position
|
|
152
|
+
FROM repository_semantic_edges AS edge LEFT JOIN added ON added.edge_id = edge.edge_id
|
|
153
|
+
WHERE edge.repository_id = ?2 AND edge.graph_node_id IN (SELECT graph_node_id FROM proposed))
|
|
154
|
+
SELECT proposed.edge_id, existing.edge_id AS existing_edge_id,
|
|
155
|
+
proposed.graph_node_id, proposed.source_node_id, proposed.target_node_id
|
|
156
|
+
FROM proposed JOIN existing
|
|
157
|
+
ON existing.graph_node_id = proposed.graph_node_id AND existing.source_node_id = proposed.source_node_id
|
|
158
|
+
AND existing.target_node_id = proposed.target_node_id AND existing.relation_digest = proposed.relation_digest
|
|
159
|
+
AND existing.edge_id <> proposed.edge_id
|
|
160
|
+
WHERE existing.position IS NULL OR existing.position < proposed.position
|
|
161
|
+
ORDER BY proposed.position, existing.position IS NOT NULL, existing.position, existing.edge_id
|
|
162
|
+
LIMIT 1`), JSON.stringify(added), repositoryId);
|
|
163
|
+
if (row === undefined)
|
|
164
|
+
return;
|
|
165
|
+
const details = {
|
|
166
|
+
edge_id: asString(row.edge_id), existing_edge_id: asString(row.existing_edge_id),
|
|
167
|
+
graph_node_id: asString(row.graph_node_id), source_node_id: asString(row.source_node_id), target_node_id: asString(row.target_node_id),
|
|
168
|
+
};
|
|
169
|
+
throw new RepositoryServiceError(`Semantic edge ${details.edge_id} duplicates edge ${details.existing_edge_id}: graph ${details.graph_node_id} already has an edge from ${details.source_node_id} to ${details.target_node_id} for this relation. The repository head and pending batch are unchanged.`, {
|
|
170
|
+
code: "repository_semantic_edge_duplicate",
|
|
171
|
+
details,
|
|
172
|
+
nextAction: `Keep using edge ${details.existing_edge_id}, or remove it with repo:edge:remove ${details.existing_edge_id} before adding a replacement edge for the same relation.`,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
89
175
|
//# sourceMappingURL=rules.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../../src/local/store/projection/rules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../../src/local/store/projection/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,21 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assertAllSemanticEdgesValid, } from "../projection/rules.js";
|
|
2
2
|
import { RepositoryStoreError } from "../sqlite/errors.js";
|
|
3
|
-
import { allRows, runSql } from "../sqlite/rows.js";
|
|
3
|
+
import { allRows, iterateRows, runSql } from "../sqlite/rows.js";
|
|
4
4
|
import { SNAPSHOT_DELETE_ORDER, SNAPSHOT_TABLES } from "./schema.js";
|
|
5
5
|
import { validateSnapshotTables } from "./validate.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Validate one whole relation in O(V + E) time, retaining O(V + E) graph state
|
|
8
|
+
* until this check returns. This trades memory for avoiding repeated descendant
|
|
9
|
+
* walks for every edge; V and E are the local relation's vertices and edges.
|
|
10
|
+
*/
|
|
11
|
+
function assertAcyclicGraph(database, repositoryId, relation) {
|
|
12
|
+
const rows = iterateRows(database.prepare(relation === "lineage"
|
|
13
|
+
? "SELECT parent_node_id AS parent, child_node_id AS child FROM repository_lineage WHERE repository_id = ? AND parent_repository_id = repository_id"
|
|
14
|
+
: "SELECT container_node_id AS parent, child_node_id AS child FROM repository_containments WHERE repository_id = ?"), repositoryId);
|
|
15
|
+
const children = new Map();
|
|
16
|
+
const incoming = new Map();
|
|
17
|
+
for (const row of rows) {
|
|
18
|
+
const parent = row.parent;
|
|
19
|
+
const child = row.child;
|
|
20
|
+
const siblings = children.get(parent);
|
|
21
|
+
if (siblings === undefined)
|
|
22
|
+
children.set(parent, [child]);
|
|
23
|
+
else
|
|
24
|
+
siblings.push(child);
|
|
25
|
+
if (!incoming.has(parent))
|
|
26
|
+
incoming.set(parent, 0);
|
|
27
|
+
incoming.set(child, (incoming.get(child) ?? 0) + 1);
|
|
28
|
+
}
|
|
29
|
+
const ready = [];
|
|
30
|
+
for (const [node, count] of incoming) {
|
|
31
|
+
if (count === 0)
|
|
32
|
+
ready.push(node);
|
|
33
|
+
}
|
|
34
|
+
for (let index = 0; index < ready.length; index++) {
|
|
35
|
+
const descendants = children.get(ready[index]);
|
|
36
|
+
if (descendants === undefined)
|
|
37
|
+
continue;
|
|
38
|
+
for (const child of descendants) {
|
|
39
|
+
const remaining = incoming.get(child) - 1;
|
|
40
|
+
incoming.set(child, remaining);
|
|
41
|
+
if (remaining === 0)
|
|
42
|
+
ready.push(child);
|
|
14
43
|
}
|
|
15
44
|
}
|
|
16
|
-
|
|
17
|
-
|
|
45
|
+
if (ready.length !== incoming.size) {
|
|
46
|
+
throw new RepositoryStoreError(`${relation} cycle is invalid`);
|
|
18
47
|
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Invariants every stored or received projection satisfies. Lineage to a
|
|
51
|
+
* tombstoned node is not one of them: hosted history accepted such links, and
|
|
52
|
+
* clone, pull and restore must keep carrying it. New work is held to that rule
|
|
53
|
+
* separately by `assertTombstonesReleaseLineage`.
|
|
54
|
+
*/
|
|
55
|
+
export function assertProjectionInvariants(database, repositoryId) {
|
|
56
|
+
assertAcyclicGraph(database, repositoryId, "lineage");
|
|
57
|
+
assertAcyclicGraph(database, repositoryId, "containment");
|
|
19
58
|
// Migration 008 dropped the assignments→definitions foreign key to permit
|
|
20
59
|
// boundary scopes; local-scoped assignments must still resolve locally.
|
|
21
60
|
if (allRows(database.prepare("SELECT 1 FROM repository_tag_assignments"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace.js","sourceRoot":"","sources":["../../../../src/local/store/snapshot/replace.ts"],"names":[],"mappings":"AACA,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"replace.js","sourceRoot":"","sources":["../../../../src/local/store/snapshot/replace.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
export class RepositoryStoreError extends Error {
|
|
2
2
|
name = "RepositoryStoreError";
|
|
3
|
+
code;
|
|
4
|
+
constructor(message, options) {
|
|
5
|
+
super(message, options);
|
|
6
|
+
this.code = options?.code;
|
|
7
|
+
}
|
|
3
8
|
}
|
|
4
9
|
/** The workspace moved between capture and the atomic write; nothing was changed. */
|
|
5
10
|
export class WorkspacePreconditionError extends RepositoryStoreError {
|
|
6
11
|
name = "WorkspacePreconditionError";
|
|
7
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* A batch tombstones a node that still has lineage when the batch ends. Names
|
|
15
|
+
* the first such tombstone in operation order and every link still attached
|
|
16
|
+
* to its node; the refused batch changed nothing.
|
|
17
|
+
*/
|
|
18
|
+
export class RepositoryNodeTombstoneBlockedError extends RepositoryStoreError {
|
|
19
|
+
name = "RepositoryNodeTombstoneBlockedError";
|
|
20
|
+
repositoryId;
|
|
21
|
+
nodeId;
|
|
22
|
+
operationId;
|
|
23
|
+
links;
|
|
24
|
+
constructor(repositoryId, nodeId, operationId, links) {
|
|
25
|
+
super("repository_node_tombstone_blocked", { code: "repository_node_tombstone_blocked" });
|
|
26
|
+
this.repositoryId = repositoryId;
|
|
27
|
+
this.nodeId = nodeId;
|
|
28
|
+
this.operationId = operationId;
|
|
29
|
+
this.links = links;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
8
32
|
/** SQLite's primary result code for "database is locked"; masks off extended detail bits. */
|
|
9
33
|
const SQLITE_BUSY = 5;
|
|
10
34
|
const SQLITE_LOCKED = 6;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/local/store/sqlite/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACpC,IAAI,GAAG,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/local/store/sqlite/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACpC,IAAI,GAAG,sBAAsB,CAAC;IAC9B,IAAI,CAAU;IAEvB,YAAY,OAAe,EAAE,OAA0C;QACrE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC;IAC5B,CAAC;CACF;AAED,qFAAqF;AACrF,MAAM,OAAO,0BAA2B,SAAQ,oBAAoB;IACzD,IAAI,GAAG,4BAA4B,CAAC;CAC9C;AAQD;;;;GAIG;AACH,MAAM,OAAO,mCAAoC,SAAQ,oBAAoB;IAClE,IAAI,GAAG,qCAAqC,CAAC;IAC7C,YAAY,CAAS;IACrB,MAAM,CAAS;IACf,WAAW,CAAS;IACpB,KAAK,CAAyB;IAEvC,YAAY,YAAoB,EAAE,MAAc,EAAE,WAAmB,EAAE,KAA6B;QAClG,KAAK,CAAC,mCAAmC,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC,CAAC;QAC1F,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,6FAA6F;AAC7F,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAOtC;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAwB,CAAC;IACnD,IAAI,IAAI,KAAK,kBAAkB,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACjF,MAAM,OAAO,GAAG,OAAO,GAAG,wBAAwB,CAAC;IACnD,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,oBAAoB;IAClD,IAAI,GAAG,qBAAqB,CAAC;IAC7B,UAAU,CAAS;IAE5B,YAAY,UAAkB,EAAE,KAAc;QAC5C,KAAK,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,sIAAsI;AACtI,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,QAAQ,KAAK,SAAS;QAAE,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -28,6 +28,7 @@ export function initializeRepository(state, repositoryId, clientId) {
|
|
|
28
28
|
database.exec(loadMigration("001_initial.sql"));
|
|
29
29
|
database.exec(loadMigration("007_repository_node_source_timestamps.sql"));
|
|
30
30
|
database.exec(loadMigration("008_boundaries_departures_and_author.sql"));
|
|
31
|
+
database.exec(loadMigration("009_repository_backup_state.sql"));
|
|
31
32
|
const createdAt = now();
|
|
32
33
|
database.prepare("INSERT INTO repository_metadata VALUES (?, ?, 'flywheel.repository.v1', ?)").run(repositoryId, clientId, createdAt);
|
|
33
34
|
database.prepare("INSERT INTO repository_heads VALUES (?, NULL, NULL, ?)").run(repositoryId, now());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../src/local/store/sqlite/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEjG,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACnG,CAAC;AAED,SAAS,cAAc,CAAC,KAA8B;IACpD,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,SAAS,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACnC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,GAAG;IACV,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAA8B,EAC9B,YAAoB,EACpB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxC,mBAAmB,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;QACtC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,2CAA2C,CAAC,CAAC,CAAC;YAC1E,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,0CAA0C,CAAC,CAAC,CAAC;YACzE,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC;YACxB,QAAQ,CAAC,OAAO,CACd,4EAA4E,CAC7E,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;YACzC,QAAQ,CAAC,OAAO,CACd,wDAAwD,CACzD,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3B,QAAQ,CAAC,OAAO,CACd,+DAA+D,CAChE,CAAC,GAAG,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3F,QAAQ,CAAC,OAAO,CACd;;;qDAG6C,CAC9C,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACvC,CAAC,EAAE,IAAI,CAAC,CAAC;AACX,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../src/local/store/sqlite/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEjG,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACnG,CAAC;AAED,SAAS,cAAc,CAAC,KAA8B;IACpD,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,SAAS,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACnC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,GAAG;IACV,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAA8B,EAC9B,YAAoB,EACpB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxC,mBAAmB,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;QACtC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,2CAA2C,CAAC,CAAC,CAAC;YAC1E,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,0CAA0C,CAAC,CAAC,CAAC;YACzE,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,iCAAiC,CAAC,CAAC,CAAC;YAChE,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC;YACxB,QAAQ,CAAC,OAAO,CACd,4EAA4E,CAC7E,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;YACzC,QAAQ,CAAC,OAAO,CACd,wDAAwD,CACzD,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3B,QAAQ,CAAC,OAAO,CACd,+DAA+D,CAChE,CAAC,GAAG,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3F,QAAQ,CAAC,OAAO,CACd;;;qDAG6C,CAC9C,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACvC,CAAC,EAAE,IAAI,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { applyOrdinaryAtomic } from "../apply.js";
|
|
3
|
-
import { isUuid } from "../../domain/models/constants.js";
|
|
3
|
+
import { isDigest, isUuid, validateTimestamp } from "../../domain/models/constants.js";
|
|
4
4
|
import { capturedRepositoryIdentityIn, commitLatestValues, committedOperationIds, listCommitsIn, loadCommitByOperationId, workspaceHeadIn } from "../commits.js";
|
|
5
5
|
import { rootedObjectDigestsIn, reachableObjectDigestsIn, assertObjectBytesAvailable, objectBytesVerified } from "../gc.js";
|
|
6
6
|
import { snapshotBytesFromConnection, snapshotTablesFromConnection } from "../snapshot/read.js";
|
|
7
7
|
import { replaceSnapshotTables } from "../snapshot/replace.js";
|
|
8
8
|
import { canonicalSnapshotBytes, snapshotLocalObjectDigests, snapshotTables } from "../snapshot/validate.js";
|
|
9
|
+
import { structuralDependencies } from "../projection/nodes.js";
|
|
9
10
|
import { pendingRepositorySnapshot } from "../pending.js";
|
|
10
11
|
import { completeSyncJournalEntry, latestPushJournalEntry, listInterruptedSyncEntries, recordSyncJournalPhase, timestamp, } from "../sync-journal.js";
|
|
11
12
|
import { applySyncAtomic } from "../sync.js";
|
|
@@ -85,6 +86,31 @@ export class SQLiteRepositoryStore {
|
|
|
85
86
|
return requireRepositoryRecord(repositoryId, row?.client_id);
|
|
86
87
|
});
|
|
87
88
|
}
|
|
89
|
+
async loadLastBackup(repositoryId) {
|
|
90
|
+
return withReadConnection(this.#state, database => {
|
|
91
|
+
verifyFormat(database, repositoryId);
|
|
92
|
+
const row = getRow(database.prepare("SELECT completed_at, archive_digest FROM repository_backup_state WHERE repository_id = ?"), repositoryId);
|
|
93
|
+
if (row === undefined)
|
|
94
|
+
return null;
|
|
95
|
+
if (!isDigest(row.archive_digest))
|
|
96
|
+
throw new RepositoryStoreError("repository backup record is invalid");
|
|
97
|
+
try {
|
|
98
|
+
validateTimestamp(row.completed_at, "repository backup completion time is invalid");
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
throw new RepositoryStoreError("repository backup record is invalid", { cause: error });
|
|
102
|
+
}
|
|
103
|
+
return { completed_at: row.completed_at, archive_digest: row.archive_digest };
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async recordLastBackup(repositoryId, archiveDigest) {
|
|
107
|
+
if (!isDigest(archiveDigest))
|
|
108
|
+
throw new RepositoryStoreError("repository backup digest is invalid");
|
|
109
|
+
withWriteConnection(this.#state, database => {
|
|
110
|
+
verifyFormat(database, repositoryId);
|
|
111
|
+
runSql(database.prepare("INSERT INTO repository_backup_state (repository_id, completed_at, archive_digest) VALUES (?, ?, ?) ON CONFLICT(repository_id) DO UPDATE SET completed_at = excluded.completed_at, archive_digest = excluded.archive_digest"), repositoryId, timestamp(), archiveDigest);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
88
114
|
async loadAuthorPrincipalId(repositoryId) {
|
|
89
115
|
return withReadConnection(this.#state, database => {
|
|
90
116
|
verifyFormat(database, repositoryId);
|
|
@@ -152,6 +178,17 @@ export class SQLiteRepositoryStore {
|
|
|
152
178
|
return loadCommitByOperationId(database, repositoryId, operationId);
|
|
153
179
|
});
|
|
154
180
|
}
|
|
181
|
+
/** The node's lifecycle at the local head and every local relationship that still references it. */
|
|
182
|
+
async loadNodePurgeState(repositoryId, nodeId) {
|
|
183
|
+
return withReadConnection(this.#state, database => {
|
|
184
|
+
verifyFormat(database, repositoryId);
|
|
185
|
+
const node = getRow(database.prepare("SELECT lifecycle_state FROM repository_nodes WHERE repository_id = ? AND node_id = ?"), repositoryId, nodeId);
|
|
186
|
+
return {
|
|
187
|
+
lifecycleState: typeof node?.lifecycle_state === "string" ? node.lifecycle_state : null,
|
|
188
|
+
dependencies: structuralDependencies(database, repositoryId, nodeId),
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
}
|
|
155
192
|
async listCommits(repositoryId, atHead, afterCommitId, limit) {
|
|
156
193
|
return withReadConnection(this.#state, database => {
|
|
157
194
|
verifyFormat(database, repositoryId);
|