@abloatai/transaction 0.47.0 → 0.48.0
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/branches.d.ts +61 -12
- package/dist/branches.d.ts.map +1 -1
- package/dist/branches.js +25 -3
- package/dist/branches.js.map +1 -1
- package/dist/errorCodes.d.ts +16 -2
- package/dist/errorCodes.d.ts.map +1 -1
- package/dist/errorCodes.js +26 -12
- package/dist/errorCodes.js.map +1 -1
- package/dist/footprint.d.ts +10 -22
- package/dist/footprint.d.ts.map +1 -1
- package/dist/footprint.js +12 -24
- package/dist/footprint.js.map +1 -1
- package/dist/schema/openapi.js +4 -4
- package/dist/schema/openapi.js.map +1 -1
- package/dist/source/index.d.ts +1 -1
- package/dist/source/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/branches.ts +27 -3
- package/src/errorCodes.ts +101 -17
- package/src/footprint.ts +16 -27
- package/src/schema/openapi.ts +4 -4
- package/src/source/index.ts +1 -1
package/src/footprint.ts
CHANGED
|
@@ -51,25 +51,14 @@ export interface FootprintArtifact {
|
|
|
51
51
|
|
|
52
52
|
// ── The names, for the code that creates and reads these objects ────────────
|
|
53
53
|
|
|
54
|
-
/**
|
|
55
|
-
*
|
|
56
|
-
* publication declares what streams, and several readers are meant to share one
|
|
57
|
-
* (`publication_names` is a per-stream option, not slot state).
|
|
58
|
-
*/
|
|
54
|
+
/** Prefix used to derive a branch-scoped publication name. The exact unsuffixed
|
|
55
|
+
* name is retained only so `ablo connect scan` can identify an old installation. */
|
|
59
56
|
export const ABLO_PUBLICATION = 'ablo_publication';
|
|
60
|
-
/**
|
|
61
|
-
* The replication slot that holds Ablo's position in the write-ahead log.
|
|
62
|
-
*
|
|
63
|
-
* Constant, and it should not be — a slot stores ONE position, so two
|
|
64
|
-
* connections sharing this name compete for the same marker. It is why a
|
|
65
|
-
* database can be connected to one plane at a time. ADR 0020 derives it per
|
|
66
|
-
* connection; {@link isValidReplicationSlotName} is the check a derived name
|
|
67
|
-
* has to pass.
|
|
68
|
-
*/
|
|
57
|
+
/** Prefix used to derive the branch-scoped replication slot. */
|
|
69
58
|
export const ABLO_REPLICATION_SLOT = 'ablo_slot';
|
|
70
|
-
/**
|
|
59
|
+
/** Prefix used to derive the branch-scoped least-privilege replication login. */
|
|
71
60
|
export const ABLO_REPLICATION_ROLE = 'ablo_replicator';
|
|
72
|
-
/**
|
|
61
|
+
/** Prefix used to derive the branch-scoped login used only for row writes. */
|
|
73
62
|
export const ABLO_WRITE_ROLE = 'ablo_writer';
|
|
74
63
|
/** The bookkeeping table that makes a retried write land once. */
|
|
75
64
|
export const ABLO_IDEMPOTENCY_TABLE = 'ablo_idempotency';
|
|
@@ -90,15 +79,15 @@ export function isValidReplicationSlotName(name: string): boolean {
|
|
|
90
79
|
|
|
91
80
|
// ── Per-connection names (ADR 0020) ─────────────────────────────────────────
|
|
92
81
|
|
|
93
|
-
/** The immutable branch coordinates
|
|
94
|
-
export interface
|
|
82
|
+
/** The immutable branch coordinates that identify one customer Data Source. */
|
|
83
|
+
export interface DataSourceIdentity {
|
|
95
84
|
readonly organizationId: string;
|
|
96
85
|
readonly branchId: string;
|
|
97
86
|
/** Omitted for the organization-default project. */
|
|
98
87
|
readonly projectId?: string;
|
|
99
88
|
}
|
|
100
89
|
|
|
101
|
-
/** The names one connection owns. Nothing here is shared with another
|
|
90
|
+
/** The names one connection owns. Nothing here is shared with another Data Source. */
|
|
102
91
|
export interface FootprintNames {
|
|
103
92
|
readonly slot: string;
|
|
104
93
|
readonly publication: string;
|
|
@@ -114,7 +103,7 @@ export interface FootprintNames {
|
|
|
114
103
|
const SUFFIX_LENGTH = 16;
|
|
115
104
|
|
|
116
105
|
/**
|
|
117
|
-
* FNV-1a over the
|
|
106
|
+
* FNV-1a over the Data Source identity, in hex.
|
|
118
107
|
*
|
|
119
108
|
* Deliberately not a cryptographic hash: this module is imported by the CLI,
|
|
120
109
|
* which runs in environments without `node:crypto` guaranteed, and the property
|
|
@@ -123,13 +112,14 @@ const SUFFIX_LENGTH = 16;
|
|
|
123
112
|
* over different seeds give the sixteen hex characters, which is ample for the
|
|
124
113
|
* handful of planes any one database is ever connected to.
|
|
125
114
|
*/
|
|
126
|
-
function
|
|
115
|
+
function dataSourceDigest(identity: DataSourceIdentity): string {
|
|
127
116
|
// The organization-default project may be omitted or repeated as the
|
|
128
117
|
// organization id; both spell the same branch coordinates.
|
|
129
|
-
const project =
|
|
118
|
+
const project =
|
|
119
|
+
identity.projectId === identity.organizationId ? '' : (identity.projectId ?? '');
|
|
130
120
|
// Delimit the coordinates. Concatenation alone makes structurally different
|
|
131
121
|
// planes such as ("ab", "c") and ("a", "bc") hash the same input.
|
|
132
|
-
const key = [
|
|
122
|
+
const key = [identity.organizationId, project, identity.branchId].join('\0');
|
|
133
123
|
|
|
134
124
|
const round = (seed: number): string => {
|
|
135
125
|
let hash = seed;
|
|
@@ -148,16 +138,15 @@ function planeDigest(plane: FootprintPlane): string {
|
|
|
148
138
|
* The objects this connection owns, named so no other connection can claim them.
|
|
149
139
|
*
|
|
150
140
|
* A slot stores ONE position, so two connections sharing a name compete for the
|
|
151
|
-
* same marker and Postgres reports nothing
|
|
152
|
-
* replace made a database connectable to one plane at a time. The publication
|
|
141
|
+
* same marker and Postgres reports nothing. The publication
|
|
153
142
|
* and roles are derived from the same digest so a database's footprint reads as
|
|
154
143
|
* one set per connection rather than a mix of shared and private objects.
|
|
155
144
|
*
|
|
156
145
|
* Stable: the same plane always derives the same names, so re-running setup is a
|
|
157
146
|
* no-op rather than a second installation.
|
|
158
147
|
*/
|
|
159
|
-
export function footprintNamesFor(
|
|
160
|
-
const suffix =
|
|
148
|
+
export function footprintNamesFor(identity: DataSourceIdentity): FootprintNames {
|
|
149
|
+
const suffix = dataSourceDigest(identity);
|
|
161
150
|
const names: FootprintNames = {
|
|
162
151
|
slot: `${ABLO_REPLICATION_SLOT}_${suffix}`,
|
|
163
152
|
publication: `${ABLO_PUBLICATION}_${suffix}`,
|
package/src/schema/openapi.ts
CHANGED
|
@@ -644,7 +644,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
644
644
|
'/v1/branches/{id}/status': {
|
|
645
645
|
get: {
|
|
646
646
|
tags: ['branches'],
|
|
647
|
-
summary: 'Diagnose one branch
|
|
647
|
+
summary: 'Diagnose one branch',
|
|
648
648
|
description:
|
|
649
649
|
'Returns branch lifecycle, active schema, compatibility with the parent schema, safe datasource coordinates, and readiness blockers.',
|
|
650
650
|
parameters: [idParam()],
|
|
@@ -699,7 +699,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
699
699
|
description:
|
|
700
700
|
'One round trip per cadence for a worker holding many rows, instead of ' +
|
|
701
701
|
'one per row. Takes only `ttl`; the leases are whichever ones your ' +
|
|
702
|
-
'credential holds on this
|
|
702
|
+
'credential holds on this branch.',
|
|
703
703
|
requestBody: optionalJsonBody(derive(claimHeartbeatRequestSchema, 'input')),
|
|
704
704
|
responses: {
|
|
705
705
|
'200': namedResp(
|
|
@@ -812,7 +812,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
812
812
|
tags: ['schema'],
|
|
813
813
|
summary: 'What the models look like',
|
|
814
814
|
description:
|
|
815
|
-
"The schema deployed on your credential's
|
|
815
|
+
"The schema deployed on your credential's branch: every model with its " +
|
|
816
816
|
'fields, their types, and its relations. Read this when you have no ' +
|
|
817
817
|
'local schema declaration to read types from — it is what makes a ' +
|
|
818
818
|
'field typo a local check rather than a rejected write. Each model ' +
|
|
@@ -835,7 +835,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
835
835
|
'How a caller without a socket learns what its peers did. Omit ' +
|
|
836
836
|
'`after` for the most recent entries, then copy each page\'s ' +
|
|
837
837
|
'`next_cursor` back as `after` to walk forward. Scope is taken from ' +
|
|
838
|
-
'your key — organization,
|
|
838
|
+
'your key — organization, branch and sync groups — so a caller cannot ' +
|
|
839
839
|
'widen what it sees by asking.',
|
|
840
840
|
parameters: queryParams(logQuerySchema),
|
|
841
841
|
responses: {
|
package/src/source/index.ts
CHANGED