@rebasepro/server-postgresql 0.4.0 → 0.6.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/README.md +69 -89
- package/dist/{server-postgresql/src/PostgresAdapter.d.ts → PostgresAdapter.d.ts} +1 -1
- package/dist/{server-postgresql/src/PostgresBackendDriver.d.ts → PostgresBackendDriver.d.ts} +2 -2
- package/dist/{server-postgresql/src/PostgresBootstrapper.d.ts → PostgresBootstrapper.d.ts} +11 -1
- package/dist/{server-postgresql/src/auth → auth}/services.d.ts +11 -11
- package/dist/{server-postgresql/src/collections → collections}/PostgresCollectionRegistry.d.ts +4 -0
- package/dist/{server-postgresql/src/data-transformer.d.ts → data-transformer.d.ts} +0 -3
- package/dist/{server-postgresql/src/databasePoolManager.d.ts → databasePoolManager.d.ts} +1 -1
- package/dist/index.es.js +10174 -11184
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +10735 -11462
- package/dist/index.umd.js.map +1 -1
- package/dist/{server-postgresql/src/services → services}/EntityPersistService.d.ts +0 -14
- package/dist/types.d.ts +3 -0
- package/dist/utils/pg-error-utils.d.ts +55 -0
- package/dist/{server-postgresql/src/websocket.d.ts → websocket.d.ts} +8 -3
- package/package.json +24 -21
- package/src/PostgresAdapter.ts +9 -10
- package/src/PostgresBackendDriver.ts +135 -122
- package/src/PostgresBootstrapper.ts +90 -16
- package/src/auth/ensure-tables.ts +28 -5
- package/src/auth/services.ts +56 -45
- package/src/cli.ts +140 -110
- package/src/collections/PostgresCollectionRegistry.ts +7 -0
- package/src/connection.ts +11 -6
- package/src/data-transformer.ts +73 -109
- package/src/databasePoolManager.ts +5 -3
- package/src/history/HistoryService.ts +3 -2
- package/src/history/ensure-history-table.ts +5 -4
- package/src/schema/auth-schema.ts +1 -2
- package/src/schema/doctor-cli.ts +2 -1
- package/src/schema/doctor.ts +40 -37
- package/src/schema/generate-drizzle-schema-logic.ts +56 -18
- package/src/schema/generate-drizzle-schema.ts +11 -11
- package/src/schema/introspect-db-inference.ts +25 -25
- package/src/schema/introspect-db-logic.ts +38 -38
- package/src/schema/introspect-db.ts +28 -27
- package/src/services/BranchService.ts +14 -0
- package/src/services/EntityFetchService.ts +28 -25
- package/src/services/EntityPersistService.ts +11 -124
- package/src/services/RelationService.ts +57 -37
- package/src/services/entity-helpers.ts +6 -2
- package/src/services/realtimeService.ts +45 -32
- package/src/types.ts +4 -0
- package/src/utils/drizzle-conditions.ts +31 -15
- package/src/utils/pg-error-utils.ts +211 -0
- package/src/websocket.ts +51 -33
- package/test/auth-services.test.ts +36 -19
- package/test/batch-many-to-many-regression.test.ts +119 -39
- package/test/data-transformer-hardening.test.ts +67 -33
- package/test/data-transformer.test.ts +4 -2
- package/test/doctor.test.ts +10 -5
- package/test/drizzle-conditions.test.ts +59 -6
- package/test/generate-drizzle-schema.test.ts +65 -40
- package/test/introspect-db-generation.test.ts +179 -81
- package/test/introspect-db-utils.test.ts +92 -37
- package/test/mocks/chalk.cjs +7 -0
- package/test/pg-error-utils.test.ts +221 -0
- package/test/postgresDataDriver.test.ts +14 -5
- package/test/property-ordering.test.ts +126 -79
- package/test/realtimeService.test.ts +6 -2
- package/test/relation-pipeline-gaps.test.ts +84 -36
- package/test/relations.test.ts +247 -0
- package/test/unmapped-tables-safety.test.ts +14 -6
- package/test/websocket.test.ts +1 -1
- package/tsconfig.json +5 -0
- package/tsconfig.prod.json +3 -0
- package/vite.config.ts +5 -5
- package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
- package/dist/common/src/collections/default-collections.d.ts +0 -9
- package/dist/common/src/collections/index.d.ts +0 -2
- package/dist/common/src/data/buildRebaseData.d.ts +0 -14
- package/dist/common/src/data/query_builder.d.ts +0 -55
- package/dist/common/src/index.d.ts +0 -4
- package/dist/common/src/util/builders.d.ts +0 -57
- package/dist/common/src/util/callbacks.d.ts +0 -6
- package/dist/common/src/util/collections.d.ts +0 -11
- package/dist/common/src/util/common.d.ts +0 -2
- package/dist/common/src/util/conditions.d.ts +0 -26
- package/dist/common/src/util/entities.d.ts +0 -58
- package/dist/common/src/util/enums.d.ts +0 -3
- package/dist/common/src/util/index.d.ts +0 -16
- package/dist/common/src/util/navigation_from_path.d.ts +0 -34
- package/dist/common/src/util/navigation_utils.d.ts +0 -20
- package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
- package/dist/common/src/util/paths.d.ts +0 -14
- package/dist/common/src/util/permissions.d.ts +0 -6
- package/dist/common/src/util/references.d.ts +0 -2
- package/dist/common/src/util/relations.d.ts +0 -22
- package/dist/common/src/util/resolutions.d.ts +0 -72
- package/dist/common/src/util/storage.d.ts +0 -24
- package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
- package/dist/types/src/controllers/auth.d.ts +0 -104
- package/dist/types/src/controllers/client.d.ts +0 -168
- package/dist/types/src/controllers/collection_registry.d.ts +0 -46
- package/dist/types/src/controllers/customization_controller.d.ts +0 -60
- package/dist/types/src/controllers/data.d.ts +0 -207
- package/dist/types/src/controllers/data_driver.d.ts +0 -218
- package/dist/types/src/controllers/database_admin.d.ts +0 -11
- package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
- package/dist/types/src/controllers/effective_role.d.ts +0 -4
- package/dist/types/src/controllers/email.d.ts +0 -36
- package/dist/types/src/controllers/index.d.ts +0 -18
- package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
- package/dist/types/src/controllers/navigation.d.ts +0 -225
- package/dist/types/src/controllers/registry.d.ts +0 -63
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
- package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
- package/dist/types/src/controllers/snackbar.d.ts +0 -24
- package/dist/types/src/controllers/storage.d.ts +0 -171
- package/dist/types/src/index.d.ts +0 -4
- package/dist/types/src/rebase_context.d.ts +0 -122
- package/dist/types/src/types/auth_adapter.d.ts +0 -301
- package/dist/types/src/types/backend.d.ts +0 -536
- package/dist/types/src/types/backend_hooks.d.ts +0 -172
- package/dist/types/src/types/builders.d.ts +0 -15
- package/dist/types/src/types/chips.d.ts +0 -5
- package/dist/types/src/types/collections.d.ts +0 -941
- package/dist/types/src/types/component_ref.d.ts +0 -47
- package/dist/types/src/types/cron.d.ts +0 -102
- package/dist/types/src/types/data_source.d.ts +0 -64
- package/dist/types/src/types/database_adapter.d.ts +0 -94
- package/dist/types/src/types/entities.d.ts +0 -145
- package/dist/types/src/types/entity_actions.d.ts +0 -104
- package/dist/types/src/types/entity_callbacks.d.ts +0 -173
- package/dist/types/src/types/entity_link_builder.d.ts +0 -7
- package/dist/types/src/types/entity_overrides.d.ts +0 -10
- package/dist/types/src/types/entity_views.d.ts +0 -87
- package/dist/types/src/types/export_import.d.ts +0 -21
- package/dist/types/src/types/formex.d.ts +0 -40
- package/dist/types/src/types/index.d.ts +0 -28
- package/dist/types/src/types/locales.d.ts +0 -4
- package/dist/types/src/types/modify_collections.d.ts +0 -5
- package/dist/types/src/types/plugins.d.ts +0 -282
- package/dist/types/src/types/properties.d.ts +0 -1181
- package/dist/types/src/types/property_config.d.ts +0 -74
- package/dist/types/src/types/relations.d.ts +0 -336
- package/dist/types/src/types/slots.d.ts +0 -262
- package/dist/types/src/types/translations.d.ts +0 -900
- package/dist/types/src/types/user_management_delegate.d.ts +0 -86
- package/dist/types/src/types/websockets.d.ts +0 -78
- package/dist/types/src/users/index.d.ts +0 -1
- package/dist/types/src/users/user.d.ts +0 -50
- package/drizzle.test.config.ts +0 -10
- /package/dist/{server-postgresql/src/auth → auth}/ensure-tables.d.ts +0 -0
- /package/dist/{server-postgresql/src/cli.d.ts → cli.d.ts} +0 -0
- /package/dist/{server-postgresql/src/connection.d.ts → connection.d.ts} +0 -0
- /package/dist/{server-postgresql/src/history → history}/HistoryService.d.ts +0 -0
- /package/dist/{server-postgresql/src/history → history}/ensure-history-table.d.ts +0 -0
- /package/dist/{server-postgresql/src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{server-postgresql/src/interfaces.d.ts → interfaces.d.ts} +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/auth-schema.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/doctor-cli.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/doctor.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema-logic.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/introspect-db-inference.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/introspect-db-logic.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/introspect-db.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/test-schema.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/BranchService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/EntityFetchService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/RelationService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/entity-helpers.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/entityService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/index.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/realtimeService.d.ts +0 -0
- /package/dist/{server-postgresql/src/utils → utils}/drizzle-conditions.d.ts +0 -0
package/test/relations.test.ts
CHANGED
|
@@ -1113,3 +1113,250 @@ relationName: "recipient" }
|
|
|
1113
1113
|
expect(namesInMessages[0]).not.toBe(namesInMessages[1]);
|
|
1114
1114
|
});
|
|
1115
1115
|
});
|
|
1116
|
+
|
|
1117
|
+
describe("columnName vs property key deduplication regression", () => {
|
|
1118
|
+
const extractRelationNames = (schema: string): string[] => {
|
|
1119
|
+
const matches = schema.match(/relationName:\s*"([^"]+)"/g) ?? [];
|
|
1120
|
+
return matches.map(m => m.replace(/relationName:\s*"/, "").replace(/"$/, ""));
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
it("should not emit a synthetic duplicate when property uses columnName different from property key", async () => {
|
|
1124
|
+
// Scenario: engagements.clientId has columnName: "client_id"
|
|
1125
|
+
// The explicit owning relation uses localKey: "clientId" (property key).
|
|
1126
|
+
// The inverse relation on clients uses foreignKeyOnTarget: "client_id" (raw column).
|
|
1127
|
+
// Without the fix, the synthetic loop would emit a broken second relation
|
|
1128
|
+
// using engagements.client_id (which doesn't exist as a Drizzle property).
|
|
1129
|
+
const clientsCollection: EntityCollection = {
|
|
1130
|
+
slug: "clients",
|
|
1131
|
+
table: "clients",
|
|
1132
|
+
name: "Clients",
|
|
1133
|
+
properties: {
|
|
1134
|
+
id: { type: "string" },
|
|
1135
|
+
name: { type: "string" }
|
|
1136
|
+
},
|
|
1137
|
+
relations: [
|
|
1138
|
+
{
|
|
1139
|
+
relationName: "engagements",
|
|
1140
|
+
target: () => engagementsCollection,
|
|
1141
|
+
cardinality: "many",
|
|
1142
|
+
direction: "inverse",
|
|
1143
|
+
foreignKeyOnTarget: "client_id"
|
|
1144
|
+
}
|
|
1145
|
+
]
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
const engagementsCollection: EntityCollection = {
|
|
1149
|
+
slug: "engagements",
|
|
1150
|
+
table: "engagements",
|
|
1151
|
+
name: "Engagements",
|
|
1152
|
+
properties: {
|
|
1153
|
+
id: { type: "string" },
|
|
1154
|
+
clientId: {
|
|
1155
|
+
type: "relation",
|
|
1156
|
+
columnName: "client_id",
|
|
1157
|
+
target: () => clientsCollection,
|
|
1158
|
+
cardinality: "one",
|
|
1159
|
+
direction: "owning",
|
|
1160
|
+
localKey: "clientId",
|
|
1161
|
+
relationName: "client"
|
|
1162
|
+
} as any,
|
|
1163
|
+
title: { type: "string" }
|
|
1164
|
+
},
|
|
1165
|
+
relations: [
|
|
1166
|
+
{
|
|
1167
|
+
relationName: "client",
|
|
1168
|
+
target: () => clientsCollection,
|
|
1169
|
+
cardinality: "one",
|
|
1170
|
+
direction: "owning",
|
|
1171
|
+
localKey: "clientId"
|
|
1172
|
+
}
|
|
1173
|
+
]
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
const result = await generateSchema([clientsCollection, engagementsCollection]);
|
|
1177
|
+
|
|
1178
|
+
// engagements should have exactly ONE one() entry for clientId
|
|
1179
|
+
const engagementsRelBlock = result.match(/export const engagementsRelations[\s\S]*?\}\)\);/)?.[0] ?? "";
|
|
1180
|
+
const oneEntries = (engagementsRelBlock.match(/:\s*one\(/g) ?? []).length;
|
|
1181
|
+
expect(oneEntries).toBe(1);
|
|
1182
|
+
|
|
1183
|
+
// The one() entry must reference engagements.clientId (property key), NOT engagements.client_id
|
|
1184
|
+
expect(engagementsRelBlock).toContain("engagements.clientId");
|
|
1185
|
+
expect(engagementsRelBlock).not.toContain("engagements.client_id");
|
|
1186
|
+
|
|
1187
|
+
// No _synth_ entry should appear
|
|
1188
|
+
expect(engagementsRelBlock).not.toContain("_synth_");
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
it("should produce matching relation names on both sides when columnName differs from property key", async () => {
|
|
1192
|
+
const clientsCollection: EntityCollection = {
|
|
1193
|
+
slug: "clients",
|
|
1194
|
+
table: "clients",
|
|
1195
|
+
name: "Clients",
|
|
1196
|
+
properties: {
|
|
1197
|
+
id: { type: "string" },
|
|
1198
|
+
name: { type: "string" }
|
|
1199
|
+
},
|
|
1200
|
+
relations: [
|
|
1201
|
+
{
|
|
1202
|
+
relationName: "engagements",
|
|
1203
|
+
target: () => engagementsCollection,
|
|
1204
|
+
cardinality: "many",
|
|
1205
|
+
direction: "inverse",
|
|
1206
|
+
foreignKeyOnTarget: "client_id"
|
|
1207
|
+
}
|
|
1208
|
+
]
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1211
|
+
const engagementsCollection: EntityCollection = {
|
|
1212
|
+
slug: "engagements",
|
|
1213
|
+
table: "engagements",
|
|
1214
|
+
name: "Engagements",
|
|
1215
|
+
properties: {
|
|
1216
|
+
id: { type: "string" },
|
|
1217
|
+
clientId: {
|
|
1218
|
+
type: "relation",
|
|
1219
|
+
columnName: "client_id",
|
|
1220
|
+
target: () => clientsCollection,
|
|
1221
|
+
cardinality: "one",
|
|
1222
|
+
direction: "owning",
|
|
1223
|
+
localKey: "clientId",
|
|
1224
|
+
relationName: "client"
|
|
1225
|
+
} as any,
|
|
1226
|
+
title: { type: "string" }
|
|
1227
|
+
},
|
|
1228
|
+
relations: [
|
|
1229
|
+
{
|
|
1230
|
+
relationName: "client",
|
|
1231
|
+
target: () => clientsCollection,
|
|
1232
|
+
cardinality: "one",
|
|
1233
|
+
direction: "owning",
|
|
1234
|
+
localKey: "clientId"
|
|
1235
|
+
}
|
|
1236
|
+
]
|
|
1237
|
+
};
|
|
1238
|
+
|
|
1239
|
+
const result = await generateSchema([clientsCollection, engagementsCollection]);
|
|
1240
|
+
|
|
1241
|
+
// Extract relation names from both sides
|
|
1242
|
+
const engagementsRelBlock = result.match(/export const engagementsRelations[\s\S]*?\}\)\);/)?.[0] ?? "";
|
|
1243
|
+
const clientsRelBlock = result.match(/export const clientsRelations[\s\S]*?\}\)\);/)?.[0] ?? "";
|
|
1244
|
+
|
|
1245
|
+
const engagementsRelNames = extractRelationNames(engagementsRelBlock);
|
|
1246
|
+
const clientsRelNames = extractRelationNames(clientsRelBlock);
|
|
1247
|
+
|
|
1248
|
+
// Both sides must share the same relation name
|
|
1249
|
+
expect(engagementsRelNames.length).toBeGreaterThanOrEqual(1);
|
|
1250
|
+
expect(clientsRelNames.length).toBeGreaterThanOrEqual(1);
|
|
1251
|
+
|
|
1252
|
+
// The owning side's relation name should appear in the inverse side too
|
|
1253
|
+
const sharedName = engagementsRelNames[0];
|
|
1254
|
+
expect(clientsRelNames).toContain(sharedName);
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
it("should correctly handle inverse one-to-one with columnName on target", async () => {
|
|
1258
|
+
// One-to-one: user has a profile, profile has userId with columnName: "user_id"
|
|
1259
|
+
const usersCollection: EntityCollection = {
|
|
1260
|
+
slug: "users",
|
|
1261
|
+
table: "users",
|
|
1262
|
+
name: "Users",
|
|
1263
|
+
properties: {
|
|
1264
|
+
id: { type: "string" },
|
|
1265
|
+
name: { type: "string" }
|
|
1266
|
+
},
|
|
1267
|
+
relations: [
|
|
1268
|
+
{
|
|
1269
|
+
relationName: "profile",
|
|
1270
|
+
target: () => profilesCollection,
|
|
1271
|
+
cardinality: "one",
|
|
1272
|
+
direction: "inverse",
|
|
1273
|
+
foreignKeyOnTarget: "user_id"
|
|
1274
|
+
}
|
|
1275
|
+
]
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
const profilesCollection: EntityCollection = {
|
|
1279
|
+
slug: "profiles",
|
|
1280
|
+
table: "profiles",
|
|
1281
|
+
name: "Profiles",
|
|
1282
|
+
properties: {
|
|
1283
|
+
id: { type: "string" },
|
|
1284
|
+
userId: {
|
|
1285
|
+
type: "relation",
|
|
1286
|
+
columnName: "user_id",
|
|
1287
|
+
target: () => usersCollection,
|
|
1288
|
+
cardinality: "one",
|
|
1289
|
+
direction: "owning",
|
|
1290
|
+
localKey: "userId",
|
|
1291
|
+
relationName: "user"
|
|
1292
|
+
} as any,
|
|
1293
|
+
bio: { type: "string" }
|
|
1294
|
+
},
|
|
1295
|
+
relations: [
|
|
1296
|
+
{
|
|
1297
|
+
relationName: "user",
|
|
1298
|
+
target: () => usersCollection,
|
|
1299
|
+
cardinality: "one",
|
|
1300
|
+
direction: "owning",
|
|
1301
|
+
localKey: "userId"
|
|
1302
|
+
}
|
|
1303
|
+
]
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
const result = await generateSchema([usersCollection, profilesCollection]);
|
|
1307
|
+
|
|
1308
|
+
// profiles should have exactly ONE one() entry
|
|
1309
|
+
const profilesRelBlock = result.match(/export const profilesRelations[\s\S]*?\}\)\);/)?.[0] ?? "";
|
|
1310
|
+
const oneEntries = (profilesRelBlock.match(/:\s*one\(/g) ?? []).length;
|
|
1311
|
+
expect(oneEntries).toBe(1);
|
|
1312
|
+
|
|
1313
|
+
// Must reference profiles.userId (property key), NOT profiles.user_id
|
|
1314
|
+
expect(profilesRelBlock).toContain("profiles.userId");
|
|
1315
|
+
expect(profilesRelBlock).not.toContain("profiles.user_id");
|
|
1316
|
+
|
|
1317
|
+
// No _synth_ entry
|
|
1318
|
+
expect(profilesRelBlock).not.toContain("_synth_");
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
it("should still emit synthetic relations when no explicit owning relation exists", async () => {
|
|
1322
|
+
// When a collection has a FK column but no explicit owning relation defined,
|
|
1323
|
+
// the synthetic loop should still create the missing relation.
|
|
1324
|
+
const categoriesCollection: EntityCollection = {
|
|
1325
|
+
slug: "categories",
|
|
1326
|
+
table: "categories",
|
|
1327
|
+
name: "Categories",
|
|
1328
|
+
properties: {
|
|
1329
|
+
id: { type: "string" },
|
|
1330
|
+
name: { type: "string" }
|
|
1331
|
+
},
|
|
1332
|
+
relations: [
|
|
1333
|
+
{
|
|
1334
|
+
relationName: "products",
|
|
1335
|
+
target: () => productsCollection,
|
|
1336
|
+
cardinality: "many",
|
|
1337
|
+
direction: "inverse",
|
|
1338
|
+
foreignKeyOnTarget: "category_id"
|
|
1339
|
+
}
|
|
1340
|
+
]
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
const productsCollection: EntityCollection = {
|
|
1344
|
+
slug: "products",
|
|
1345
|
+
table: "products",
|
|
1346
|
+
name: "Products",
|
|
1347
|
+
properties: {
|
|
1348
|
+
id: { type: "string" },
|
|
1349
|
+
name: { type: "string" },
|
|
1350
|
+
category_id: { type: "string" }
|
|
1351
|
+
// NOTE: no type: "relation" and no explicit relations[]
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1355
|
+
const result = await generateSchema([categoriesCollection, productsCollection]);
|
|
1356
|
+
|
|
1357
|
+
// products should get a synthetic one() for category_id
|
|
1358
|
+
const productsRelBlock = result.match(/export const productsRelations[\s\S]*?\}\)\);/)?.[0] ?? "";
|
|
1359
|
+
expect(productsRelBlock).toContain("one(categories");
|
|
1360
|
+
expect(productsRelBlock).toContain("products.category_id");
|
|
1361
|
+
});
|
|
1362
|
+
});
|
|
@@ -49,7 +49,9 @@ function buildPrevSnapshot(tables: Record<string, any>, enums: Record<string, an
|
|
|
49
49
|
roles: {},
|
|
50
50
|
policies: {},
|
|
51
51
|
views: {},
|
|
52
|
-
_meta: { schemas: {},
|
|
52
|
+
_meta: { schemas: {},
|
|
53
|
+
tables: {},
|
|
54
|
+
columns: {} }
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -82,7 +84,9 @@ function snapshotColumn(name: string, type: string, opts: {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
function snapshotEnum(name: string, schema: string, values: string[]) {
|
|
85
|
-
return { name,
|
|
87
|
+
return { name,
|
|
88
|
+
schema,
|
|
89
|
+
values };
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
// ── Drizzle schema objects (the "managed" schema) ───────────────────────
|
|
@@ -106,7 +110,8 @@ describe("Unmapped tables safety", () => {
|
|
|
106
110
|
describe("tablesFilter scoping", () => {
|
|
107
111
|
|
|
108
112
|
it("should extract only managed table names from the tables export", () => {
|
|
109
|
-
const tables = { managedUsers,
|
|
113
|
+
const tables = { managedUsers,
|
|
114
|
+
managedPosts };
|
|
110
115
|
const tableNames = Object.values(tables).map(t => getTableName(t as Table));
|
|
111
116
|
|
|
112
117
|
expect(tableNames).toEqual(["users", "posts"]);
|
|
@@ -115,7 +120,8 @@ describe("Unmapped tables safety", () => {
|
|
|
115
120
|
});
|
|
116
121
|
|
|
117
122
|
it("should produce a tablesFilter that excludes unmapped tables", () => {
|
|
118
|
-
const tables = { managedUsers,
|
|
123
|
+
const tables = { managedUsers,
|
|
124
|
+
managedPosts };
|
|
119
125
|
const tablesFilter = Object.values(tables).map(t => getTableName(t as Table));
|
|
120
126
|
|
|
121
127
|
const unmappedTables = [
|
|
@@ -209,7 +215,8 @@ describe("Unmapped tables safety", () => {
|
|
|
209
215
|
})
|
|
210
216
|
});
|
|
211
217
|
|
|
212
|
-
const curJson = generateDrizzleJson({ managedUsers,
|
|
218
|
+
const curJson = generateDrizzleJson({ managedUsers,
|
|
219
|
+
managedPosts });
|
|
213
220
|
const statements = await generateMigration(prevOnlyManaged as any, curJson as any);
|
|
214
221
|
|
|
215
222
|
// No changes needed — managed schema is identical
|
|
@@ -276,7 +283,8 @@ describe("Unmapped tables safety", () => {
|
|
|
276
283
|
name: "Posts",
|
|
277
284
|
properties: {
|
|
278
285
|
title: { type: "string" },
|
|
279
|
-
tags: { type: "relation",
|
|
286
|
+
tags: { type: "relation",
|
|
287
|
+
relationName: "tags" }
|
|
280
288
|
},
|
|
281
289
|
relations: [{
|
|
282
290
|
relationName: "tags",
|
package/test/websocket.test.ts
CHANGED
|
@@ -61,7 +61,7 @@ describe("WebSocket Server SQL error handling", () => {
|
|
|
61
61
|
it("should handle EXECUTE_SQL errors cleanly and return ERROR message without throwing", async () => {
|
|
62
62
|
expect(mockWssInstance).toBeDefined();
|
|
63
63
|
expect(mockWssInstance.on).toHaveBeenCalledWith("connection", expect.any(Function));
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
const connectionCallback = mockWssInstance.on.mock.calls.find(
|
|
66
66
|
(call: any[]) => call[0] === "connection"
|
|
67
67
|
)[1];
|
package/tsconfig.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
+
"ignoreDeprecations": "6.0",
|
|
4
|
+
"rootDir": ".",
|
|
3
5
|
"outDir": "dist",
|
|
4
6
|
"module": "ESNEXT",
|
|
5
7
|
"target": "ESNEXT",
|
|
@@ -35,6 +37,9 @@
|
|
|
35
37
|
],
|
|
36
38
|
"@rebasepro/common": [
|
|
37
39
|
"../common/src"
|
|
40
|
+
],
|
|
41
|
+
"@rebasepro/server-core": [
|
|
42
|
+
"../server-core/src"
|
|
38
43
|
]
|
|
39
44
|
}
|
|
40
45
|
},
|
package/tsconfig.prod.json
CHANGED
package/vite.config.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-ignore
|
|
2
1
|
import path from "path";
|
|
3
2
|
|
|
4
3
|
import { defineConfig } from "vite";
|
|
@@ -10,8 +9,8 @@ const ReactCompilerConfig = {
|
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Only externalize dependencies that the consumer app installs directly.
|
|
13
|
-
* Everything else
|
|
14
|
-
*
|
|
12
|
+
* Everything else gets inlined so linked consumers work without installing them.
|
|
13
|
+
* The createRequire banner in output config provides require() for inlined CJS deps.
|
|
15
14
|
*/
|
|
16
15
|
const CONSUMER_EXTERNALS = [
|
|
17
16
|
"hono",
|
|
@@ -33,8 +32,8 @@ const isExternal = (id: string) => {
|
|
|
33
32
|
// Externalize only deps the consumer app explicitly installs
|
|
34
33
|
if (CONSUMER_EXTERNALS.some(ext => id === ext || id.startsWith(ext + "/"))) return true;
|
|
35
34
|
// Externalize Node built-ins
|
|
36
|
-
if (["fs", "path", "url", "util", "crypto", "http", "https", "net", "tls", "stream", "events", "os", "child_process", "buffer", "assert", "dns", "zlib", "querystring", "node:"].some(b => id === b || id.startsWith("node:") || id.startsWith(b + "/"))) return true;
|
|
37
|
-
// Inline everything else (
|
|
35
|
+
if (["fs", "path", "url", "util", "crypto", "http", "https", "net", "tls", "stream", "events", "os", "child_process", "buffer", "assert", "dns", "zlib", "querystring", "process", "module", "worker_threads", "v8", "vm", "string_decoder", "node:"].some(b => id === b || id.startsWith("node:") || id.startsWith(b + "/"))) return true;
|
|
36
|
+
// Inline everything else — createRequire banner handles require() for CJS deps
|
|
38
37
|
return false;
|
|
39
38
|
};
|
|
40
39
|
|
|
@@ -54,6 +53,7 @@ export default defineConfig(() => ({
|
|
|
54
53
|
rollupOptions: {
|
|
55
54
|
external: isExternal,
|
|
56
55
|
output: {
|
|
56
|
+
banner: 'import { createRequire as __createRequire } from "module"; import process from "process"; const require = __createRequire(import.meta.url);',
|
|
57
57
|
globals: {
|
|
58
58
|
"json-logic-js": "jsonLogic",
|
|
59
59
|
"fast-equals": "fastEquals",
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { EntityCollection } from "@rebasepro/types";
|
|
2
|
-
export declare class CollectionRegistry {
|
|
3
|
-
private collectionsByTableName;
|
|
4
|
-
private collectionsBySlug;
|
|
5
|
-
private rootCollections;
|
|
6
|
-
private cachedCollectionsList;
|
|
7
|
-
private rawCollectionsByTableName;
|
|
8
|
-
private rawCollectionsBySlug;
|
|
9
|
-
private rawRootCollections;
|
|
10
|
-
private cachedRawCollectionsList;
|
|
11
|
-
private lastRawInputSnapshot;
|
|
12
|
-
constructor(collections?: EntityCollection[]);
|
|
13
|
-
reset(): void;
|
|
14
|
-
/**
|
|
15
|
-
* Registers a collection and its subcollections recursively.
|
|
16
|
-
* Returns true if the collections have changed, false otherwise.
|
|
17
|
-
*
|
|
18
|
-
* Idempotent: compares the raw input (before normalization) against a stored
|
|
19
|
-
* snapshot. Only re-normalizes and re-registers when the raw input actually changed.
|
|
20
|
-
* @param collections
|
|
21
|
-
*/
|
|
22
|
-
registerMultiple(collections: EntityCollection[]): boolean;
|
|
23
|
-
register(collection: EntityCollection, rawCollection?: EntityCollection): void;
|
|
24
|
-
private _registerRecursively;
|
|
25
|
-
normalizeCollection(collection: EntityCollection): EntityCollection;
|
|
26
|
-
/**
|
|
27
|
-
* Extract Relation[] from properties that have inline relation config (i.e. `target` is set).
|
|
28
|
-
* This allows developers to define relations directly on properties without a separate
|
|
29
|
-
* `relations[]` entry on the collection.
|
|
30
|
-
*/
|
|
31
|
-
private extractRelationsFromProperties;
|
|
32
|
-
private normalizeProperties;
|
|
33
|
-
private normalizeProperty;
|
|
34
|
-
get(path: string): EntityCollection | undefined;
|
|
35
|
-
/**
|
|
36
|
-
* Gets the pristine, un-normalized collection exactly as it was provided.
|
|
37
|
-
* Useful for the AST editor so it doesn't accidentally serialize injected metadata back to disk.
|
|
38
|
-
*/
|
|
39
|
-
getRaw(path: string): EntityCollection | undefined;
|
|
40
|
-
/**
|
|
41
|
-
* Get collection by resolving multi-segment paths through relations
|
|
42
|
-
* e.g., "authors/70/posts" resolves to the posts collection
|
|
43
|
-
*/
|
|
44
|
-
getCollectionByPath(collectionPath: string): EntityCollection | undefined;
|
|
45
|
-
getCollections(): EntityCollection[];
|
|
46
|
-
getRawCollections(): EntityCollection[];
|
|
47
|
-
/**
|
|
48
|
-
* Resolves a multi-segment path like "products/123/locales" and returns
|
|
49
|
-
* information about the collections and entity IDs along the path
|
|
50
|
-
*/
|
|
51
|
-
resolvePathToCollections(path: string): {
|
|
52
|
-
collections: EntityCollection[];
|
|
53
|
-
entityIds: (string | number)[];
|
|
54
|
-
finalCollection: EntityCollection;
|
|
55
|
-
};
|
|
56
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { PostgresCollection } from "@rebasepro/types";
|
|
2
|
-
/**
|
|
3
|
-
* Default users collection.
|
|
4
|
-
*
|
|
5
|
-
* Prepended to the developer's collections array by the admin and server.
|
|
6
|
-
* Slug-based dedup (Map keyed by slug, last-write-wins) lets developers
|
|
7
|
-
* override by defining their own collection with `slug: "users"`.
|
|
8
|
-
*/
|
|
9
|
-
export declare const defaultUsersCollection: PostgresCollection;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { DataDriver, RebaseData } from "@rebasepro/types";
|
|
2
|
-
/**
|
|
3
|
-
* Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.
|
|
4
|
-
*
|
|
5
|
-
* This is the key bridge: any property access like `data.products` returns
|
|
6
|
-
* a `CollectionAccessor` backed by the underlying DataDriver, without
|
|
7
|
-
* needing per-collection code generation.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const data = buildRebaseData(driver);
|
|
11
|
-
* await data.products.create({ name: "Camera", price: 299 });
|
|
12
|
-
* const { data: items } = await data.products.find({ where: { status: "eq.published" } });
|
|
13
|
-
*/
|
|
14
|
-
export declare function buildRebaseData(driver: DataDriver): RebaseData;
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator, LogicalCondition, WhereValue, FilterCondition } from "@rebasepro/types";
|
|
2
|
-
export declare function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition;
|
|
3
|
-
export declare function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition;
|
|
4
|
-
export declare function cond(column: string, operator: FilterOperator, value: unknown): FilterCondition;
|
|
5
|
-
export declare class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {
|
|
6
|
-
private collection;
|
|
7
|
-
private params;
|
|
8
|
-
constructor(collection: CollectionAccessor<M>);
|
|
9
|
-
/**
|
|
10
|
-
* Add a filter condition to your query.
|
|
11
|
-
* @example
|
|
12
|
-
* client.collection('users').where('age', '>=', 18).find()
|
|
13
|
-
*/
|
|
14
|
-
where<K extends keyof M & string>(column: K, operator: FilterOperator, value: WhereValue<M[K]>): this;
|
|
15
|
-
where(logicalCondition: LogicalCondition): this;
|
|
16
|
-
/**
|
|
17
|
-
* Order the results by a specific column.
|
|
18
|
-
* @example
|
|
19
|
-
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
20
|
-
*/
|
|
21
|
-
orderBy(column: keyof M & string, ascending?: "asc" | "desc"): this;
|
|
22
|
-
/**
|
|
23
|
-
* Limit the number of results returned.
|
|
24
|
-
*/
|
|
25
|
-
limit(count: number): this;
|
|
26
|
-
/**
|
|
27
|
-
* Skip the first N results.
|
|
28
|
-
*/
|
|
29
|
-
offset(count: number): this;
|
|
30
|
-
/**
|
|
31
|
-
* Set a free-text search string if supported by the backend.
|
|
32
|
-
*/
|
|
33
|
-
search(searchString: string): this;
|
|
34
|
-
/**
|
|
35
|
-
* Include related entities in the response.
|
|
36
|
-
* Relations will be populated with full entity data instead of just IDs.
|
|
37
|
-
*
|
|
38
|
-
* @param relations - Relation names to include, or "*" for all.
|
|
39
|
-
* @example
|
|
40
|
-
* // Include specific relations
|
|
41
|
-
* client.data.posts.include("tags", "author").find()
|
|
42
|
-
*
|
|
43
|
-
* // Include all relations
|
|
44
|
-
* client.data.posts.include("*").find()
|
|
45
|
-
*/
|
|
46
|
-
include(...relations: string[]): this;
|
|
47
|
-
/**
|
|
48
|
-
* Execute the find query and return the results.
|
|
49
|
-
*/
|
|
50
|
-
find(): Promise<FindResponse<M>>;
|
|
51
|
-
/**
|
|
52
|
-
* Listen to realtime updates matching this query.
|
|
53
|
-
*/
|
|
54
|
-
listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void;
|
|
55
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { AdditionalFieldDelegate, ArrayProperty, BooleanProperty, DateProperty, EntityCallbacks, EntityCollection, EnumValueConfig, EnumValues, GeopointProperty, MapProperty, NumberProperty, Properties, Property, ReferenceProperty, StringProperty, User } from "@rebasepro/types";
|
|
2
|
-
/**
|
|
3
|
-
* Identity function we use to defeat the type system of Typescript and build
|
|
4
|
-
* collection views with all its properties
|
|
5
|
-
* @param collection
|
|
6
|
-
* @group Builder
|
|
7
|
-
*/
|
|
8
|
-
export declare function buildCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(collection: EntityCollection<M, USER>): EntityCollection<M, USER>;
|
|
9
|
-
/**
|
|
10
|
-
* Identity function we use to defeat the type system of Typescript and preserve
|
|
11
|
-
* the property keys.
|
|
12
|
-
* @param property
|
|
13
|
-
* @group Builder
|
|
14
|
-
*/
|
|
15
|
-
export declare function buildProperty<T, P extends Property = Property>(property: P): P extends StringProperty ? StringProperty : P extends NumberProperty ? NumberProperty : P extends BooleanProperty ? BooleanProperty : P extends DateProperty ? DateProperty : P extends GeopointProperty ? GeopointProperty : P extends ReferenceProperty ? ReferenceProperty : P extends ArrayProperty ? ArrayProperty : P extends MapProperty ? MapProperty : never;
|
|
16
|
-
/**
|
|
17
|
-
* Identity function we use to defeat the type system of Typescript and preserve
|
|
18
|
-
* the properties keys.
|
|
19
|
-
* @param properties
|
|
20
|
-
* @group Builder
|
|
21
|
-
*/
|
|
22
|
-
export declare function buildProperties<M extends Record<string, unknown>>(properties: Properties): Properties;
|
|
23
|
-
/**
|
|
24
|
-
* Identity function we use to defeat the type system of Typescript and preserve
|
|
25
|
-
* the properties keys.
|
|
26
|
-
* @param propertiesOrBuilder
|
|
27
|
-
* @group Builder
|
|
28
|
-
*/
|
|
29
|
-
export declare function buildPropertiesOrBuilder<M extends Record<string, unknown>>(propertiesOrBuilder: Properties): Properties;
|
|
30
|
-
/**
|
|
31
|
-
* Identity function we use to defeat the type system of Typescript and preserve
|
|
32
|
-
* the properties keys.
|
|
33
|
-
* @param enumValues
|
|
34
|
-
* @group Builder
|
|
35
|
-
*/
|
|
36
|
-
export declare function buildEnum(enumValues: EnumValues): EnumValues;
|
|
37
|
-
/**
|
|
38
|
-
* Identity function we use to defeat the type system of Typescript and preserve
|
|
39
|
-
* the properties keys.
|
|
40
|
-
* @param enumValueConfig
|
|
41
|
-
* @group Builder
|
|
42
|
-
*/
|
|
43
|
-
export declare function buildEnumValueConfig(enumValueConfig: EnumValueConfig): EnumValueConfig;
|
|
44
|
-
/**
|
|
45
|
-
* Identity function we use to defeat the type system of Typescript and preserve
|
|
46
|
-
* the properties keys.
|
|
47
|
-
* @param callbacks
|
|
48
|
-
* @group Builder
|
|
49
|
-
*/
|
|
50
|
-
export declare function buildEntityCallbacks<M extends Record<string, unknown> = Record<string, unknown>>(callbacks: EntityCallbacks<M>): EntityCallbacks<M>;
|
|
51
|
-
/**
|
|
52
|
-
* Identity function we use to defeat the type system of Typescript and build
|
|
53
|
-
* additional field delegates views with all its properties
|
|
54
|
-
* @param additionalFieldDelegate
|
|
55
|
-
* @group Builder
|
|
56
|
-
*/
|
|
57
|
-
export declare function buildAdditionalFieldDelegate<M extends Record<string, unknown>, USER extends User = User>(additionalFieldDelegate: AdditionalFieldDelegate<M, USER>): AdditionalFieldDelegate<M, USER>;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { EntityCallbacks, Properties } from "@rebasepro/types";
|
|
2
|
-
/**
|
|
3
|
-
* Helper function to extract field-level PropertyCallbacks from a properties schema
|
|
4
|
-
* and wrap them into an EntityCallbacks object recursively.
|
|
5
|
-
*/
|
|
6
|
-
export declare const buildPropertyCallbacks: (properties: Properties) => EntityCallbacks | undefined;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { DefaultSelectedViewBuilder, DefaultSelectedViewParams, EntityCollection, Properties } from "@rebasepro/types";
|
|
2
|
-
export declare function sortProperties<M extends Record<string, unknown>>(properties: Properties, propertiesOrder?: string[]): Properties;
|
|
3
|
-
export declare function resolveDefaultSelectedView(defaultSelectedView: string | DefaultSelectedViewBuilder | undefined, params: DefaultSelectedViewParams): string | undefined;
|
|
4
|
-
export declare function getLocalChangesBackup(collection: EntityCollection): "manual_apply" | "auto_apply";
|
|
5
|
-
/**
|
|
6
|
-
* Returns the primary keys for an entity collection by inspecting the properties
|
|
7
|
-
* and finding any properties with `isId`.
|
|
8
|
-
* Fallbacks to `["id"]` if no properties are marked as `isId: true`.
|
|
9
|
-
* @param collection
|
|
10
|
-
*/
|
|
11
|
-
export declare function getPrimaryKeys<M extends Record<string, unknown>>(collection: EntityCollection<M>): Extract<keyof M, string>[];
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { AuthController, ConditionContext, JsonLogicRule, Property } from "@rebasepro/types";
|
|
2
|
-
/**
|
|
3
|
-
* Register custom JSON Logic operations for Rebase.
|
|
4
|
-
* Call this once at app initialization.
|
|
5
|
-
*/
|
|
6
|
-
export declare function registerConditionOperations(): void;
|
|
7
|
-
/**
|
|
8
|
-
* Evaluate a JSON Logic rule against the given context.
|
|
9
|
-
*/
|
|
10
|
-
export declare function evaluateCondition(rule: JsonLogicRule, context: ConditionContext): unknown;
|
|
11
|
-
/**
|
|
12
|
-
* Build a ConditionContext from the current property resolution context.
|
|
13
|
-
*/
|
|
14
|
-
export declare function buildConditionContext(params: {
|
|
15
|
-
propertyKey?: string;
|
|
16
|
-
values?: Record<string, unknown>;
|
|
17
|
-
previousValues?: Record<string, unknown>;
|
|
18
|
-
path: string;
|
|
19
|
-
entityId?: string;
|
|
20
|
-
index?: number;
|
|
21
|
-
authController: AuthController;
|
|
22
|
-
}): ConditionContext;
|
|
23
|
-
/**
|
|
24
|
-
* Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.
|
|
25
|
-
*/
|
|
26
|
-
export declare function applyPropertyConditions(property: Property, context: ConditionContext): Property;
|