@powersync/service-types 0.17.0 → 0.19.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/routes.d.ts CHANGED
@@ -28,7 +28,7 @@ export declare const ExecuteSqlRequest: t.ObjectCodec<{
28
28
  connection_id: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
29
29
  sql: t.ObjectCodec<{
30
30
  query: t.IdentityCodec<t.CodecType.String>;
31
- args: t.ArrayCodec<t.Union<t.Codec<string | number, string | number, string, t.CodecProps>, t.IdentityCodec<t.CodecType.Boolean>>>;
31
+ args: t.ArrayCodec<t.Codec<string | number | boolean, string | number | boolean>>;
32
32
  }>;
33
33
  }>;
34
34
  export type ExecuteSqlRequest = t.Encoded<typeof ExecuteSqlRequest>;
@@ -36,7 +36,7 @@ export declare const ExecuteSqlResponse: t.ObjectCodec<{
36
36
  success: t.IdentityCodec<t.CodecType.Boolean>;
37
37
  results: t.ObjectCodec<{
38
38
  columns: t.ArrayCodec<t.IdentityCodec<t.CodecType.String>>;
39
- rows: t.ArrayCodec<t.ArrayCodec<t.Union<t.Codec<string | number | boolean, string | number | boolean, string, t.CodecProps>, t.Codec<null, null, t.CodecType.Null, t.CodecProps>>>>;
39
+ rows: t.ArrayCodec<t.ArrayCodec<t.Codec<string | number | boolean | null, string | number | boolean | null>>>;
40
40
  }>;
41
41
  /** Set if success = false */
42
42
  error: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
@@ -55,7 +55,7 @@ export declare const DiagnosticsResponse: t.ObjectCodec<{
55
55
  postgres_uri: t.IdentityCodec<t.CodecType.String>;
56
56
  connected: t.IdentityCodec<t.CodecType.Boolean>;
57
57
  errors: t.ArrayCodec<t.ObjectCodec<{
58
- level: t.Union<t.Codec<"warning", "warning", string, t.CodecProps>, t.LiteralCodec<"fatal">>;
58
+ level: t.Codec<"warning" | "fatal", "warning" | "fatal">;
59
59
  message: t.IdentityCodec<t.CodecType.String>;
60
60
  location: t.OptionalCodec<t.Codec<{
61
61
  start_offset: number;
@@ -112,6 +112,7 @@ export declare const DiagnosticsResponse: t.ObjectCodec<{
112
112
  safe_wal_size?: number | undefined;
113
113
  max_slot_wal_keep_size?: number | undefined;
114
114
  }[];
115
+ version_label?: string | null | undefined;
115
116
  content?: string | undefined;
116
117
  }, {
117
118
  errors: {
@@ -153,6 +154,7 @@ export declare const DiagnosticsResponse: t.ObjectCodec<{
153
154
  safe_wal_size?: number | undefined;
154
155
  max_slot_wal_keep_size?: number | undefined;
155
156
  }[];
157
+ version_label?: string | null | undefined;
156
158
  content?: string | undefined;
157
159
  }, string, t.CodecProps>>;
158
160
  /**
@@ -202,6 +204,7 @@ export declare const DiagnosticsResponse: t.ObjectCodec<{
202
204
  safe_wal_size?: number | undefined;
203
205
  max_slot_wal_keep_size?: number | undefined;
204
206
  }[];
207
+ version_label?: string | null | undefined;
205
208
  content?: string | undefined;
206
209
  }, {
207
210
  errors: {
@@ -243,6 +246,7 @@ export declare const DiagnosticsResponse: t.ObjectCodec<{
243
246
  safe_wal_size?: number | undefined;
244
247
  max_slot_wal_keep_size?: number | undefined;
245
248
  }[];
249
+ version_label?: string | null | undefined;
246
250
  content?: string | undefined;
247
251
  }, string, t.CodecProps>>;
248
252
  }>;
@@ -262,6 +266,7 @@ export declare const ValidateRequest: t.ObjectCodec<{
262
266
  }>;
263
267
  export type ValidateRequest = t.Encoded<typeof ValidateRequest>;
264
268
  export declare const ValidateResponse: t.ObjectCodec<{
269
+ version_label: t.OptionalCodec<t.Codec<string | null, string | null, string, t.CodecProps>>;
265
270
  content: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
266
271
  connections: t.ArrayCodec<t.ObjectCodec<{
267
272
  id: t.IdentityCodec<t.CodecType.String>;
@@ -283,7 +288,7 @@ export declare const ValidateResponse: t.ObjectCodec<{
283
288
  data_queries: t.IdentityCodec<t.CodecType.Boolean>;
284
289
  parameter_queries: t.IdentityCodec<t.CodecType.Boolean>;
285
290
  errors: t.ArrayCodec<t.ObjectCodec<{
286
- level: t.Union<t.Codec<"warning", "warning", string, t.CodecProps>, t.LiteralCodec<"fatal">>;
291
+ level: t.Codec<"warning" | "fatal", "warning" | "fatal">;
287
292
  message: t.IdentityCodec<t.CodecType.String>;
288
293
  location: t.OptionalCodec<t.Codec<{
289
294
  start_offset: number;
@@ -297,7 +302,7 @@ export declare const ValidateResponse: t.ObjectCodec<{
297
302
  }>>;
298
303
  }>>;
299
304
  errors: t.ArrayCodec<t.ObjectCodec<{
300
- level: t.Union<t.Codec<"warning", "warning", string, t.CodecProps>, t.LiteralCodec<"fatal">>;
305
+ level: t.Codec<"warning" | "fatal", "warning" | "fatal">;
301
306
  message: t.IdentityCodec<t.CodecType.String>;
302
307
  location: t.OptionalCodec<t.Codec<{
303
308
  start_offset: number;
package/dist/routes.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as t from 'ts-codec';
2
+ import { anyPrimitive } from './codecs.js';
2
3
  import { ConnectionStatus, InstanceSchema, SyncRulesStatus } from './definitions.js';
3
4
  export const GetSchemaRequest = t.object({});
4
5
  export const GetSchemaResponse = InstanceSchema;
@@ -6,14 +7,14 @@ export const ExecuteSqlRequest = t.object({
6
7
  connection_id: t.string.optional(),
7
8
  sql: t.object({
8
9
  query: t.string,
9
- args: t.array(t.string.or(t.number).or(t.boolean))
10
+ args: t.array(anyPrimitive({ string: true, number: true, boolean: true }))
10
11
  })
11
12
  });
12
13
  export const ExecuteSqlResponse = t.object({
13
14
  success: t.boolean,
14
15
  results: t.object({
15
16
  columns: t.array(t.string),
16
- rows: t.array(t.array(t.string.or(t.number).or(t.boolean).or(t.Null)))
17
+ rows: t.array(t.array(anyPrimitive({ string: true, number: true, boolean: true, null: true })))
17
18
  }),
18
19
  /** Set if success = false */
19
20
  error: t.string.optional()
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAErF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAG7C,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAGhD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;IAClC,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC;QACZ,KAAK,EAAE,CAAC,CAAC,MAAM;QACf,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;KACnD,CAAC;CACH,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,CAAC,CAAC,OAAO;IAClB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;KACvE,CAAC;IACF,6BAA6B;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C;;OAEG;IACH,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;IAEtC;;;;OAIG;IACH,iBAAiB,EAAE,eAAe,CAAC,QAAQ,EAAE;IAE7C;;;;;;OAMG;IACH,oBAAoB,EAAE,eAAe,CAAC,QAAQ,EAAE;CACjD,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAG7C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,CAAC,CAAC,KAAK,CAClB,CAAC,CAAC,MAAM,CAAC;QACP,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;QACvB,GAAG,EAAE,CAAC,CAAC,MAAM;QACb,SAAS,EAAE,CAAC,CAAC,MAAM;KACpB,CAAC,CACH;CACF,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM;CACrB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAErF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAG7C,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAGhD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;IAClC,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC;QACZ,KAAK,EAAE,CAAC,CAAC,MAAM;QACf,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;KAC3E,CAAC;CACH,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,CAAC,CAAC,OAAO;IAClB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;KAChG,CAAC;IACF,6BAA6B;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C;;OAEG;IACH,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;IAEtC;;;;OAIG;IACH,iBAAiB,EAAE,eAAe,CAAC,QAAQ,EAAE;IAE7C;;;;;;OAMG;IACH,oBAAoB,EAAE,eAAe,CAAC,QAAQ,EAAE;CACjD,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAG7C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,CAAC,CAAC,KAAK,CAClB,CAAC,CAAC,MAAM,CAAC;QACP,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;QACvB,GAAG,EAAE,CAAC,CAAC,MAAM;QACb,SAAS,EAAE,CAAC,CAAC,MAAM;KACpB,CAAC,CACH;CACF,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM;CACrB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powersync/service-types",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "exports": "./dist/index.js",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",
@@ -13,8 +13,12 @@
13
13
  "ts-codec": "^1.3.0",
14
14
  "uri-js": "^4.4.1"
15
15
  },
16
+ "devDependencies": {
17
+ "vitest": "^4.1.5"
18
+ },
16
19
  "scripts": {
17
20
  "clean": "rm -r ./dist && tsc -b --clean",
18
- "build": "tsc -b"
21
+ "build": "tsc -b",
22
+ "test": "vitest"
19
23
  }
20
24
  }
package/src/codecs.ts ADDED
@@ -0,0 +1,112 @@
1
+ import type { AnyCodec, Codec, Ix, Ox, Union } from 'ts-codec';
2
+ import * as t from 'ts-codec';
3
+
4
+ /**
5
+ * An optimized codec equivalent to `type.or(Null)`.
6
+ */
7
+ export function orNull<T extends AnyCodec>(type: T): Codec<Ix<T> | null, Ox<T> | null> {
8
+ return t.codec(
9
+ t.CodecType.Union,
10
+ (data) => {
11
+ if (data === null) {
12
+ return null;
13
+ }
14
+ return type.encode(data);
15
+ },
16
+ (data) => {
17
+ if (data === null) {
18
+ return null;
19
+ }
20
+ return type.decode(data);
21
+ },
22
+ {
23
+ codecs: [type, t.Null]
24
+ } satisfies Union<T, typeof t.Null>['props']
25
+ );
26
+ }
27
+
28
+ /**
29
+ * A codec parsing a static enumeration of literals.
30
+ *
31
+ * `enumLiteral('foo', 'bar')` is an equivalent but more efficient implementation of
32
+ * `literal('foo').or(literal('bar'))`
33
+ */
34
+ export function enumLiteral<T extends string>(...values: T[]): Codec<T, T> {
35
+ function validate(source: unknown): T {
36
+ function invalid(): never {
37
+ const allowed = values.join(', ');
38
+ throw new t.TransformError(`Expected one of ${allowed}, but got ${source}`);
39
+ }
40
+
41
+ if (typeof source !== 'string') invalid();
42
+ if (!values.includes(source as T)) invalid();
43
+
44
+ return source as T;
45
+ }
46
+
47
+ return t.codec(t.CodecType.Enum, validate, validate, {
48
+ enum: values
49
+ });
50
+ }
51
+
52
+ type NamedPrimitiveTypes = {
53
+ string: string;
54
+ number: number;
55
+ boolean: boolean;
56
+ null: null;
57
+ };
58
+
59
+ const primitiveCodecs: Record<keyof NamedPrimitiveTypes, AnyCodec> = {
60
+ string: t.string,
61
+ number: t.number,
62
+ boolean: t.boolean,
63
+ null: t.Null
64
+ };
65
+
66
+ type EnabledKeys<T> = {
67
+ [K in keyof T]: T[K] extends true ? K : never;
68
+ }[keyof T];
69
+
70
+ type PrimitiveUnion<T extends Partial<Record<keyof NamedPrimitiveTypes, boolean>>> = NamedPrimitiveTypes[Extract<
71
+ EnabledKeys<T>,
72
+ keyof NamedPrimitiveTypes
73
+ >];
74
+
75
+ /**
76
+ * Parses multiple primitive types.
77
+ *
78
+ * `anyPrimitive({ boolean: true, string: true })` behaves identical to `boolean.or(string)`, but is more efficient.
79
+ */
80
+ export function anyPrimitive<const T extends Partial<Record<keyof NamedPrimitiveTypes, boolean>>>(
81
+ allowedPrimitives: T
82
+ ): Codec<PrimitiveUnion<T>, PrimitiveUnion<T>> {
83
+ function validate(source: unknown): PrimitiveUnion<T> {
84
+ switch (typeof source) {
85
+ case 'string':
86
+ if (allowedPrimitives.string) return source as PrimitiveUnion<T>;
87
+ break;
88
+ case 'number':
89
+ if (allowedPrimitives.number) return source as PrimitiveUnion<T>;
90
+ break;
91
+ case 'boolean':
92
+ if (allowedPrimitives.boolean) return source as PrimitiveUnion<T>;
93
+ break;
94
+ case 'object':
95
+ if (allowedPrimitives.null && source === null) return source as PrimitiveUnion<T>;
96
+ break;
97
+ }
98
+
99
+ throw new t.TransformError(`Primitive value ${source} is not allowed.`);
100
+ }
101
+
102
+ const codecsForSchema: AnyCodec[] = [];
103
+ for (const [key, value] of Object.entries(allowedPrimitives)) {
104
+ if (value) {
105
+ codecsForSchema.push(primitiveCodecs[key as keyof NamedPrimitiveTypes]);
106
+ }
107
+ }
108
+
109
+ return t.codec(t.CodecType.Union, validate, validate, {
110
+ codecs: codecsForSchema
111
+ } satisfies Union<AnyCodec, AnyCodec>['props']);
112
+ }
@@ -1,5 +1,6 @@
1
1
  import dedent from 'dedent';
2
2
  import * as t from 'ts-codec';
3
+ import { anyPrimitive, enumLiteral } from '../codecs.js';
3
4
 
4
5
  /**
5
6
  * The meta tags here are used in the generated JSON schema.
@@ -96,10 +97,7 @@ export const jwkRSA = t
96
97
  e: t.string.meta({
97
98
  description: 'RSA exponent, Base64 URL encoded.'
98
99
  }),
99
- alg: t
100
- .literal('RS256')
101
- .or(t.literal('RS384'))
102
- .or(t.literal('RS512'))
100
+ alg: enumLiteral('RS256', 'RS384', 'RS512')
103
101
  .meta({
104
102
  description: 'The algorithm intended for use with this key (RS256, RS384, or RS512).'
105
103
  })
@@ -128,7 +126,7 @@ export const jwkHmac = t
128
126
  k: t.string.meta({
129
127
  description: 'The HMAC key value, Base64 URL encoded.'
130
128
  }),
131
- alg: t.literal('HS256').or(t.literal('HS384')).or(t.literal('HS512')).meta({
129
+ alg: enumLiteral('HS256', 'HS384', 'HS512').meta({
132
130
  description: 'The algorithm intended for use with this key (HS256, HS384, or HS512).'
133
131
  }),
134
132
  use: t.string
@@ -152,7 +150,7 @@ export const jwkOKP = t
152
150
  })
153
151
  .optional(),
154
152
  /** Other curves have security issues so only these two are supported. */
155
- crv: t.literal('Ed25519').or(t.literal('Ed448')).meta({
153
+ crv: enumLiteral('Ed25519', 'Ed448').meta({
156
154
  description: 'The cryptographic curve used with this key. Only Ed25519 and Ed448 are supported.'
157
155
  }),
158
156
  x: t.string.meta({
@@ -182,7 +180,7 @@ export const jwkEC = t
182
180
  description: 'Key ID, a unique identifier for the key.'
183
181
  })
184
182
  .optional(),
185
- crv: t.literal('P-256').or(t.literal('P-384')).or(t.literal('P-521')).meta({
183
+ crv: enumLiteral('P-256', 'P-384', 'P-521').meta({
186
184
  description: 'The cryptographic curve used with this key (P-256, P-384, or P-521).'
187
185
  }),
188
186
  x: t.string.meta({
@@ -191,7 +189,7 @@ export const jwkEC = t
191
189
  y: t.string.meta({
192
190
  description: 'The y coordinate for the Elliptic Curve point, Base64 URL encoded.'
193
191
  }),
194
- alg: t.literal('ES256').or(t.literal('ES384')).or(t.literal('ES512')).meta({
192
+ alg: enumLiteral('ES256', 'ES384', 'ES512').meta({
195
193
  description: 'The algorithm intended for use with this key (ES256, ES384, or ES512).'
196
194
  }),
197
195
  use: t.string
@@ -204,7 +202,7 @@ export const jwkEC = t
204
202
  description: 'JSON Web Key (JWK) representation of an Elliptic Curve key.'
205
203
  });
206
204
 
207
- const jwk = t.union(t.union(t.union(jwkRSA, jwkHmac), jwkOKP), jwkEC).meta({
205
+ const jwk = jwkRSA.or(jwkHmac).or(jwkOKP).or(jwkEC).meta({
208
206
  description: 'A JSON Web Key (JWK) representing a cryptographic key. Can be RSA, HMAC, OKP, or EC key types.'
209
207
  });
210
208
 
@@ -222,21 +220,12 @@ export type StrictJwk = t.Decoded<typeof jwk>;
222
220
 
223
221
  export const LoggingConfig = t
224
222
  .object({
225
- level: t
226
- .literal('silly')
227
- .or(t.literal('debug'))
228
- .or(t.literal('verbose'))
229
- .or(t.literal('http'))
230
- .or(t.literal('info'))
231
- .or(t.literal('warn'))
232
- .or(t.literal('error'))
223
+ level: enumLiteral('silly', 'debug', 'verbose', 'http', 'info', 'warn', 'error')
233
224
  .meta({
234
225
  description: 'Log level for the service logs.'
235
226
  })
236
227
  .optional(),
237
- format: t
238
- .literal('json')
239
- .or(t.literal('text'))
228
+ format: enumLiteral('json', 'text')
240
229
  .meta({
241
230
  description: 'Log output format.'
242
231
  })
@@ -257,6 +246,11 @@ export const BaseStorageConfig = t
257
246
  .meta({
258
247
  description: 'Maximum number of connections to the storage database, per process. Defaults to 8.'
259
248
  })
249
+ .optional(),
250
+ default_storage_version: t.number
251
+ .meta({
252
+ description: 'Storage version to use when deploying a sync config that does not specify storage_version.'
253
+ })
260
254
  .optional()
261
255
  })
262
256
  .meta({
@@ -276,6 +270,11 @@ export type GenericStorageConfig = t.Encoded<typeof GenericStorageConfig>;
276
270
 
277
271
  export const SyncConfig = t
278
272
  .object({
273
+ version_label: t.string
274
+ .meta({
275
+ description: 'Optional label identifying this deployed sync config version.'
276
+ })
277
+ .optional(),
279
278
  path: t.string
280
279
  .meta({
281
280
  description: 'Path to the sync config YAML file.'
@@ -538,7 +537,7 @@ export const powerSyncConfig = t
538
537
  .optional(),
539
538
 
540
539
  parameters: t
541
- .record(t.number.or(t.string).or(t.boolean).or(t.Null))
540
+ .record(anyPrimitive({ null: true, number: true, string: true, boolean: true }))
542
541
  .meta({
543
542
  description: 'Global parameters that can be referenced in sync config and other configurations.'
544
543
  })
@@ -1,4 +1,5 @@
1
1
  import * as t from 'ts-codec';
2
+ import { enumLiteral, orNull } from './codecs.js';
2
3
 
3
4
  export const SourceSpan = t.object({
4
5
  start_offset: t.number,
@@ -8,7 +9,7 @@ export type SourceSpan = t.Encoded<typeof SourceSpan>;
8
9
 
9
10
  export const ReplicationError = t.object({
10
11
  /** Warning: Could indicate an issue. Fatal: Prevents replicating. */
11
- level: t.literal('warning').or(t.literal('fatal')),
12
+ level: enumLiteral('warning', 'fatal'),
12
13
  message: t.string,
13
14
  location: SourceSpan.optional(),
14
15
  ts: t.string.optional()
@@ -35,6 +36,10 @@ export const TableInfo = t.object({
35
36
  export type TableInfo = t.Encoded<typeof TableInfo>;
36
37
 
37
38
  export const SyncRulesStatus = t.object({
39
+ /**
40
+ * Optional label identifying this deployed sync config version.
41
+ */
42
+ version_label: orNull(t.string).optional(),
38
43
  content: t.string.optional(),
39
44
  connections: t.array(
40
45
  t.object({
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * as configFile from './config/PowerSyncConfig.js';
2
2
 
3
+ export * from './codecs.js';
3
4
  export * from './definitions.js';
4
5
  export * from './metrics.js';
5
6
  export * as metric_types from './metrics.js';
package/src/metrics.ts CHANGED
@@ -26,8 +26,14 @@ export enum ReplicationMetric {
26
26
  export enum StorageMetric {
27
27
  // Size of current replication data stored in PowerSync
28
28
  REPLICATION_SIZE_BYTES = 'powersync_replication_storage_size_bytes',
29
+ ATTRIBUTED_SOURCE_RECORDS_BYTES = 'powersync_attributed_source_records_bytes',
29
30
  // Size of operations data stored in PowerSync
30
31
  OPERATION_SIZE_BYTES = 'powersync_operation_storage_size_bytes',
32
+ ATTRIBUTED_BUCKET_DATA_BYTES = 'powersync_attributed_bucket_data_bytes',
31
33
  // Size of parameter data stored in PowerSync
32
- PARAMETER_SIZE_BYTES = 'powersync_parameter_storage_size_bytes'
34
+ PARAMETER_SIZE_BYTES = 'powersync_parameter_storage_size_bytes',
35
+ ATTRIBUTED_PARAMETER_INDEXES_BYTES = 'powersync_attributed_parameter_indexes_bytes',
36
+ // Size of active S3 object-storage references stored in PowerSync
37
+ OBJECT_STORAGE_SIZE_BYTES = 'powersync_object_storage_size_bytes',
38
+ ATTRIBUTED_OBJECT_STORAGE_BYTES = 'powersync_attributed_object_storage_bytes'
33
39
  }
package/src/routes.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as t from 'ts-codec';
2
+ import { anyPrimitive } from './codecs.js';
2
3
  import { ConnectionStatus, InstanceSchema, SyncRulesStatus } from './definitions.js';
3
4
 
4
5
  export const GetSchemaRequest = t.object({});
@@ -11,7 +12,7 @@ export const ExecuteSqlRequest = t.object({
11
12
  connection_id: t.string.optional(),
12
13
  sql: t.object({
13
14
  query: t.string,
14
- args: t.array(t.string.or(t.number).or(t.boolean))
15
+ args: t.array(anyPrimitive({ string: true, number: true, boolean: true }))
15
16
  })
16
17
  });
17
18
  export type ExecuteSqlRequest = t.Encoded<typeof ExecuteSqlRequest>;
@@ -20,7 +21,7 @@ export const ExecuteSqlResponse = t.object({
20
21
  success: t.boolean,
21
22
  results: t.object({
22
23
  columns: t.array(t.string),
23
- rows: t.array(t.array(t.string.or(t.number).or(t.boolean).or(t.Null)))
24
+ rows: t.array(t.array(anyPrimitive({ string: true, number: true, boolean: true, null: true })))
24
25
  }),
25
26
  /** Set if success = false */
26
27
  error: t.string.optional()
@@ -0,0 +1,77 @@
1
+ import * as t from 'ts-codec';
2
+ import { describe, expect, test } from 'vitest';
3
+
4
+ import { anyPrimitive, enumLiteral, orNull } from '../src/codecs.js';
5
+
6
+ test('orNull', () => {
7
+ const codec = orNull(t.literal('allowed'));
8
+
9
+ expect(codec.encode('allowed')).toStrictEqual('allowed');
10
+ expect(codec.decode('allowed')).toStrictEqual('allowed');
11
+ expect(codec.encode(null)).toStrictEqual(null);
12
+ expect(codec.decode(null)).toStrictEqual(null);
13
+
14
+ expect(() => codec.encode('invalid' as any)).throws();
15
+ expect(() => codec.decode('invalid' as any)).throws();
16
+ });
17
+
18
+ test('enumLiteral', () => {
19
+ const codec = enumLiteral('foo', 'bar', 'baz');
20
+
21
+ for (const value of ['foo', 'bar', 'baz'] as const) {
22
+ expect(codec.encode(value)).toStrictEqual(value);
23
+ expect(codec.decode(value)).toStrictEqual(value);
24
+ }
25
+
26
+ expect(() => codec.encode('invalid' as any)).throws();
27
+ expect(() => codec.decode('invalid' as any)).throws();
28
+ });
29
+
30
+ describe('anyPrimitive', () => {
31
+ test('allows only the configured types', () => {
32
+ const codec = anyPrimitive({ string: true, number: true }) as t.AnyCodec;
33
+
34
+ expect(codec.decode('foo')).toStrictEqual('foo');
35
+ expect(codec.decode(42)).toStrictEqual(42);
36
+ expect(codec.encode('foo')).toStrictEqual('foo');
37
+ expect(codec.encode(42)).toStrictEqual(42);
38
+
39
+ expect(() => codec.decode(true)).throws();
40
+ expect(() => codec.decode(null)).throws();
41
+ expect(() => codec.decode(undefined)).throws();
42
+ expect(() => codec.decode({})).throws();
43
+ });
44
+
45
+ test('allows null', () => {
46
+ const codec = anyPrimitive({ null: true }) as t.AnyCodec;
47
+
48
+ expect(codec.decode(null)).toStrictEqual(null);
49
+ expect(codec.encode(null)).toStrictEqual(null);
50
+
51
+ expect(() => codec.decode('foo')).throws();
52
+ expect(() => codec.decode(undefined)).throws();
53
+ expect(() => codec.decode({})).throws();
54
+ });
55
+
56
+ test('rejects types not in the configuration, even when other types are allowed', () => {
57
+ const codec = anyPrimitive({ boolean: true }) as t.AnyCodec;
58
+
59
+ expect(codec.decode(true)).toStrictEqual(true);
60
+ expect(codec.decode(false)).toStrictEqual(false);
61
+
62
+ expect(() => codec.decode('foo')).throws();
63
+ expect(() => codec.decode(1)).throws();
64
+ expect(() => codec.decode(null)).throws();
65
+ expect(() => codec.decode(undefined)).throws();
66
+ });
67
+
68
+ test('with no allowed types rejects everything', () => {
69
+ const codec = anyPrimitive({}) as any as t.AnyCodec;
70
+
71
+ expect(() => codec.decode('foo')).throws();
72
+ expect(() => codec.decode(1)).throws();
73
+ expect(() => codec.decode(true)).throws();
74
+ expect(() => codec.decode(null)).throws();
75
+ expect(() => codec.decode(undefined)).throws();
76
+ });
77
+ });