@syncular/server 0.15.8 → 0.15.10
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/seed.js +23 -6
- package/package.json +2 -2
- package/src/seed.ts +27 -6
package/dist/seed.js
CHANGED
|
@@ -12,8 +12,26 @@
|
|
|
12
12
|
import { decodeMessage, encodeMessage, encodeRow, PROTOCOL_WIRE_VERSION, } from '@syncular/core';
|
|
13
13
|
import { SyncError } from './errors.js';
|
|
14
14
|
import { handleSyncRequest } from './handler.js';
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const MAPPABLE_RE = /^_*[A-Za-z][A-Za-z0-9_]*$/;
|
|
16
|
+
/** Pinned §12 schema alias used by generated row types and every client host. */
|
|
17
|
+
function snakeToCamel(name) {
|
|
18
|
+
if (!MAPPABLE_RE.test(name))
|
|
19
|
+
return name;
|
|
20
|
+
const lead = /^_*/.exec(name)?.[0] ?? '';
|
|
21
|
+
const bare = name.slice(lead.length);
|
|
22
|
+
const trail = /_*$/.exec(bare)?.[0] ?? '';
|
|
23
|
+
const middle = bare.slice(0, bare.length - trail.length);
|
|
24
|
+
const segments = middle.split('_').filter((segment) => segment.length > 0);
|
|
25
|
+
if (segments.length === 0)
|
|
26
|
+
return name;
|
|
27
|
+
const first = segments[0];
|
|
28
|
+
return (lead +
|
|
29
|
+
first +
|
|
30
|
+
segments
|
|
31
|
+
.slice(1)
|
|
32
|
+
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
33
|
+
.join('') +
|
|
34
|
+
trail);
|
|
17
35
|
}
|
|
18
36
|
/**
|
|
19
37
|
* Seed `mutations` into a partition through the real push path. Throws a
|
|
@@ -35,12 +53,11 @@ export async function seedMutations(config, target, mutations) {
|
|
|
35
53
|
// the two casings the client tier accepts).
|
|
36
54
|
const byColumn = new Map();
|
|
37
55
|
for (const [key, value] of Object.entries(mutation.values)) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
: camelToSnake(key);
|
|
41
|
-
if (!table.columns.some((c) => c.name === name)) {
|
|
56
|
+
const column = table.columns.find((candidate) => candidate.name === key || snakeToCamel(candidate.name) === key);
|
|
57
|
+
if (column === undefined) {
|
|
42
58
|
throw new SyncError('sync.invalid_request', `seedMutations: table ${table.name}: unknown column ${JSON.stringify(key)}`);
|
|
43
59
|
}
|
|
60
|
+
const name = column.name;
|
|
44
61
|
if (byColumn.has(name)) {
|
|
45
62
|
throw new SyncError('sync.invalid_request', `seedMutations: table ${table.name}: column ${JSON.stringify(name)} appears twice (snake_case and camelCase) — pass it once`);
|
|
46
63
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/server",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.10",
|
|
4
4
|
"description": "Syncular server: handleSyncRequest + storage/auth interfaces for the sync protocol",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Benjamin Kniffler",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"!dist/**/*.test.d.ts"
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@syncular/core": "0.15.
|
|
56
|
+
"@syncular/core": "0.15.10"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@electric-sql/pglite": "^0.5.4"
|
package/src/seed.ts
CHANGED
|
@@ -54,8 +54,27 @@ export interface SeedTarget {
|
|
|
54
54
|
readonly commitId?: string;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const MAPPABLE_RE = /^_*[A-Za-z][A-Za-z0-9_]*$/;
|
|
58
|
+
|
|
59
|
+
/** Pinned §12 schema alias used by generated row types and every client host. */
|
|
60
|
+
function snakeToCamel(name: string): string {
|
|
61
|
+
if (!MAPPABLE_RE.test(name)) return name;
|
|
62
|
+
const lead = /^_*/.exec(name)?.[0] ?? '';
|
|
63
|
+
const bare = name.slice(lead.length);
|
|
64
|
+
const trail = /_*$/.exec(bare)?.[0] ?? '';
|
|
65
|
+
const middle = bare.slice(0, bare.length - trail.length);
|
|
66
|
+
const segments = middle.split('_').filter((segment) => segment.length > 0);
|
|
67
|
+
if (segments.length === 0) return name;
|
|
68
|
+
const first = segments[0] as string;
|
|
69
|
+
return (
|
|
70
|
+
lead +
|
|
71
|
+
first +
|
|
72
|
+
segments
|
|
73
|
+
.slice(1)
|
|
74
|
+
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
75
|
+
.join('') +
|
|
76
|
+
trail
|
|
77
|
+
);
|
|
59
78
|
}
|
|
60
79
|
|
|
61
80
|
/**
|
|
@@ -85,15 +104,17 @@ export async function seedMutations(
|
|
|
85
104
|
// the two casings the client tier accepts).
|
|
86
105
|
const byColumn = new Map<string, unknown>();
|
|
87
106
|
for (const [key, value] of Object.entries(mutation.values)) {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
107
|
+
const column = table.columns.find(
|
|
108
|
+
(candidate) =>
|
|
109
|
+
candidate.name === key || snakeToCamel(candidate.name) === key,
|
|
110
|
+
);
|
|
111
|
+
if (column === undefined) {
|
|
92
112
|
throw new SyncError(
|
|
93
113
|
'sync.invalid_request',
|
|
94
114
|
`seedMutations: table ${table.name}: unknown column ${JSON.stringify(key)}`,
|
|
95
115
|
);
|
|
96
116
|
}
|
|
117
|
+
const name = column.name;
|
|
97
118
|
if (byColumn.has(name)) {
|
|
98
119
|
throw new SyncError(
|
|
99
120
|
'sync.invalid_request',
|