@powersync/service-sync-rules 0.41.0 → 0.42.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.
@@ -0,0 +1,22 @@
1
+ import { SyncConfig } from '../SyncConfig.js';
2
+ import { SourceSchema } from '../types.js';
3
+ import { GenerateSchemaOptions, SchemaGenerator } from './SchemaGenerator.js';
4
+ /**
5
+ * Generates the client-side schema for the PowerSync Rust SDK, in the shape its reference docs use:
6
+ * an `app_schema()` function that pushes one `Table::create(...)` per synced table.
7
+ *
8
+ * When the sync configuration has optional streams, a `TypedSyncStreams` helper is generated with
9
+ * one method per stream.
10
+ */
11
+ export declare class RustSchemaGenerator extends SchemaGenerator {
12
+ readonly key = "rust";
13
+ readonly label = "Rust";
14
+ readonly mediaType = "text/rust";
15
+ readonly fileName = "schema.rs";
16
+ generate(source: SyncConfig, schema: SourceSchema, options?: GenerateSchemaOptions): string;
17
+ private generateTable;
18
+ private generateColumn;
19
+ private generateStreamHelper;
20
+ private generateStreamMethod;
21
+ private rustType;
22
+ }
@@ -0,0 +1,120 @@
1
+ import { TYPE_INTEGER, TYPE_REAL, TYPE_TEXT } from '../ExpressionType.js';
2
+ import { SchemaGenerator, toCamelCase } from './SchemaGenerator.js';
3
+ /**
4
+ * Generates the client-side schema for the PowerSync Rust SDK, in the shape its reference docs use:
5
+ * an `app_schema()` function that pushes one `Table::create(...)` per synced table.
6
+ *
7
+ * When the sync configuration has optional streams, a `TypedSyncStreams` helper is generated with
8
+ * one method per stream.
9
+ */
10
+ export class RustSchemaGenerator extends SchemaGenerator {
11
+ key = 'rust';
12
+ label = 'Rust';
13
+ mediaType = 'text/rust';
14
+ fileName = 'schema.rs';
15
+ generate(source, schema, options) {
16
+ const tables = super.getAllTables(source, schema);
17
+ const streams = this.getOptionalStreams(source, schema);
18
+ const pushes = tables.map((table) => this.generateTable(table.name, table.columns, options)).join('\n\n');
19
+ // Import order matches what rustfmt produces.
20
+ let imports = 'use powersync::schema::{Column, Schema, Table};';
21
+ if (streams.length) {
22
+ imports += '\nuse powersync::{PowerSyncDatabase, SyncStream};';
23
+ if (streams.some((stream) => Object.keys(stream.parameters).length > 0)) {
24
+ imports += '\nuse serde_json::json;';
25
+ }
26
+ }
27
+ const streamHelper = streams.length ? `\n${this.generateStreamHelper(streams)}` : '';
28
+ return `${imports}
29
+
30
+ pub fn app_schema() -> Schema {
31
+ let mut schema = Schema::default();
32
+
33
+ ${pushes}
34
+
35
+ schema
36
+ }
37
+ ${streamHelper}`;
38
+ }
39
+ generateTable(name, columns, options) {
40
+ const lines = columns.map((column) => {
41
+ const line = ` ${this.generateColumn(column)},`;
42
+ if (options?.includeTypeComments && column.originalType != null) {
43
+ return `${line} // ${column.originalType}`;
44
+ }
45
+ return line;
46
+ });
47
+ const columnsVec = lines.length > 0 ? `vec![\n${lines.join('\n')}\n ]` : 'vec![]';
48
+ return ` schema.tables.push(Table::create(
49
+ ${rustString(name)},
50
+ ${columnsVec},
51
+ |_| {},
52
+ ));`;
53
+ }
54
+ generateColumn(column) {
55
+ return `Column::${this.columnType(column)}(${rustString(column.name)})`;
56
+ }
57
+ generateStreamHelper(streams) {
58
+ const methods = streams.map((stream) => this.generateStreamMethod(stream)).join('\n\n');
59
+ // The field must be pub so that the helper can be constructed from outside the generated module.
60
+ return `pub struct TypedSyncStreams<'a>(pub &'a PowerSyncDatabase);
61
+
62
+ impl<'a> TypedSyncStreams<'a> {
63
+ ${methods}
64
+ }
65
+ `;
66
+ }
67
+ generateStreamMethod(stream) {
68
+ const entries = Object.entries(stream.parameters);
69
+ const name = rustString(stream.name);
70
+ if (entries.length == 0) {
71
+ return ` pub fn ${toSnakeCase(stream.name)}(&self) -> SyncStream<'a> {
72
+ self.0.sync_stream(${name}, None)
73
+ }`;
74
+ }
75
+ const args = entries.map(([parameter, type]) => `${toSnakeCase(parameter)}: ${this.rustType(type)}`).join(', ');
76
+ const jsonEntries = entries.map(([parameter]) => `${rustString(parameter)}: ${toSnakeCase(parameter)}`).join(', ');
77
+ return ` pub fn ${toSnakeCase(stream.name)}(&self, ${args}) -> SyncStream<'a> {
78
+ let encoded_params = json!({${jsonEntries}});
79
+
80
+ self.0.sync_stream(${name}, Some(&encoded_params))
81
+ }`;
82
+ }
83
+ rustType({ type }) {
84
+ if (type.typeFlags & TYPE_TEXT) {
85
+ // The SDK clones parameters internally, so &str is more convenient for callers than String.
86
+ return '&str';
87
+ }
88
+ else if (type.typeFlags & TYPE_REAL) {
89
+ return 'f64';
90
+ }
91
+ else if (type.typeFlags & TYPE_INTEGER) {
92
+ return 'i64';
93
+ }
94
+ else {
95
+ return '&str';
96
+ }
97
+ }
98
+ }
99
+ const RUST_STRING_ESCAPES = {
100
+ '\\': '\\\\',
101
+ '"': '\\"',
102
+ '\n': '\\n',
103
+ '\r': '\\r',
104
+ '\t': '\\t'
105
+ };
106
+ /**
107
+ * Wraps a name in a Rust string literal, escaping characters that would break or distort it.
108
+ */
109
+ function rustString(value) {
110
+ return `"${value.replace(/[\\"\n\r\t]/g, (c) => RUST_STRING_ESCAPES[c])}"`;
111
+ }
112
+ /**
113
+ * Converts a name to a snake_case Rust identifier, e.g. `myStream` and `my-stream` become `my_stream`.
114
+ */
115
+ function toSnakeCase(source) {
116
+ return toCamelCase(source)
117
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
118
+ .toLowerCase();
119
+ }
120
+ //# sourceMappingURL=RustSchemaGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RustSchemaGenerator.js","sourceRoot":"","sources":["../../src/schema-generators/RustSchemaGenerator.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { DartSchemaGenerator } from './DartSchemaGenerator.js';
2
2
  import { DotNetClassSchemaGenerator, DotNetSchemaGenerator } from './DotNetSchemaGenerator.js';
3
3
  import { JsLegacySchemaGenerator } from './JsLegacySchemaGenerator.js';
4
4
  import { KotlinSchemaGenerator } from './KotlinSchemaGenerator.js';
5
+ import { RustSchemaGenerator } from './RustSchemaGenerator.js';
5
6
  import { SwiftSchemaGenerator } from './SwiftSchemaGenerator.js';
6
7
  import { TsSchemaGenerator } from './TsSchemaGenerator.js';
7
8
  export declare const schemaGenerators: {
@@ -11,6 +12,7 @@ export declare const schemaGenerators: {
11
12
  js: TsSchemaGenerator;
12
13
  jsLegacy: JsLegacySchemaGenerator;
13
14
  kotlin: KotlinSchemaGenerator;
15
+ rust: RustSchemaGenerator;
14
16
  swift: SwiftSchemaGenerator;
15
17
  ts: TsSchemaGenerator;
16
18
  };
@@ -2,6 +2,7 @@ import { DartSchemaGenerator } from './DartSchemaGenerator.js';
2
2
  import { DotNetClassSchemaGenerator, DotNetSchemaGenerator } from './DotNetSchemaGenerator.js';
3
3
  import { JsLegacySchemaGenerator } from './JsLegacySchemaGenerator.js';
4
4
  import { KotlinSchemaGenerator } from './KotlinSchemaGenerator.js';
5
+ import { RustSchemaGenerator } from './RustSchemaGenerator.js';
5
6
  import { SwiftSchemaGenerator } from './SwiftSchemaGenerator.js';
6
7
  import { TsSchemaGenerator, TsSchemaLanguage } from './TsSchemaGenerator.js';
7
8
  export const schemaGenerators = {
@@ -11,6 +12,7 @@ export const schemaGenerators = {
11
12
  js: new TsSchemaGenerator({ language: TsSchemaLanguage.js }),
12
13
  jsLegacy: new JsLegacySchemaGenerator(),
13
14
  kotlin: new KotlinSchemaGenerator(),
15
+ rust: new RustSchemaGenerator(),
14
16
  swift: new SwiftSchemaGenerator(),
15
17
  ts: new TsSchemaGenerator()
16
18
  };
@@ -1 +1 @@
1
- {"version":3,"file":"generators.js","sourceRoot":"","sources":["../../src/schema-generators/generators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE7E,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,IAAI,mBAAmB,EAAE;IAC/B,MAAM,EAAE,IAAI,qBAAqB,EAAE;IACnC,WAAW,EAAE,IAAI,0BAA0B,EAAE;IAC7C,EAAE,EAAE,IAAI,iBAAiB,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE,CAAC;IAC5D,QAAQ,EAAE,IAAI,uBAAuB,EAAE;IACvC,MAAM,EAAE,IAAI,qBAAqB,EAAE;IACnC,KAAK,EAAE,IAAI,oBAAoB,EAAE;IACjC,EAAE,EAAE,IAAI,iBAAiB,EAAE;CAC5B,CAAC"}
1
+ {"version":3,"file":"generators.js","sourceRoot":"","sources":["../../src/schema-generators/generators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE7E,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,IAAI,mBAAmB,EAAE;IAC/B,MAAM,EAAE,IAAI,qBAAqB,EAAE;IACnC,WAAW,EAAE,IAAI,0BAA0B,EAAE;IAC7C,EAAE,EAAE,IAAI,iBAAiB,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE,CAAC;IAC5D,QAAQ,EAAE,IAAI,uBAAuB,EAAE;IACvC,MAAM,EAAE,IAAI,qBAAqB,EAAE;IACnC,IAAI,EAAE,IAAI,mBAAmB,EAAE;IAC/B,KAAK,EAAE,IAAI,oBAAoB,EAAE;IACjC,EAAE,EAAE,IAAI,iBAAiB,EAAE;CAC5B,CAAC"}
@@ -5,6 +5,7 @@ export * from './generators.js';
5
5
  export * from './JsLegacySchemaGenerator.js';
6
6
  export * from './KotlinSchemaGenerator.js';
7
7
  export * from './RoomSchemaGenerator.js';
8
+ export * from './RustSchemaGenerator.js';
8
9
  export * from './SchemaGenerator.js';
9
10
  export * from './SwiftSchemaGenerator.js';
10
11
  export * from './TsSchemaGenerator.js';
@@ -5,6 +5,7 @@ export * from './generators.js';
5
5
  export * from './JsLegacySchemaGenerator.js';
6
6
  export * from './KotlinSchemaGenerator.js';
7
7
  export * from './RoomSchemaGenerator.js';
8
+ export * from './RustSchemaGenerator.js';
8
9
  export * from './SchemaGenerator.js';
9
10
  export * from './SwiftSchemaGenerator.js';
10
11
  export * from './TsSchemaGenerator.js';
@@ -1 +1 @@
1
- {"version":3,"file":"schema-generators.js","sourceRoot":"","sources":["../../src/schema-generators/schema-generators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,kBAAkB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AACpF,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,kBAAkB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC"}
1
+ {"version":3,"file":"schema-generators.js","sourceRoot":"","sources":["../../src/schema-generators/schema-generators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,kBAAkB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AACpF,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,kBAAkB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powersync/service-sync-rules",
3
3
  "repository": "https://github.com/powersync-ja/powersync-service",
4
- "version": "0.41.0",
4
+ "version": "0.42.0",
5
5
  "exports": "./dist/index.js",
6
6
  "license": "FSL-1.1-ALv2",
7
7
  "publishConfig": {