@rebasepro/common 0.6.0 → 0.7.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/collections/CollectionRegistry.d.ts +14 -1
- package/dist/data/buildRoutedRebaseData.d.ts +53 -0
- package/dist/data/resolveDataSource.d.ts +43 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.es.js +186 -3
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +193 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/table-classification.d.ts +47 -0
- package/package.json +13 -13
- package/src/collections/CollectionRegistry.ts +34 -1
- package/src/data/buildRoutedRebaseData.ts +97 -0
- package/src/data/resolveDataSource.ts +79 -0
- package/src/index.ts +3 -1
- package/src/table-classification.ts +109 -0
package/dist/index.umd.js
CHANGED
|
@@ -1440,8 +1440,57 @@
|
|
|
1440
1440
|
return result;
|
|
1441
1441
|
}
|
|
1442
1442
|
//#endregion
|
|
1443
|
+
//#region src/data/resolveDataSource.ts
|
|
1444
|
+
/**
|
|
1445
|
+
* Build a keyed registry from a list of {@link DataSourceDefinition}s.
|
|
1446
|
+
* Later entries win on key collision.
|
|
1447
|
+
*/
|
|
1448
|
+
function createDataSourceRegistry(definitions) {
|
|
1449
|
+
const registry = {};
|
|
1450
|
+
for (const def of definitions ?? []) registry[def.key] = def;
|
|
1451
|
+
return registry;
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Resolve the effective data source for a collection — the single source of
|
|
1455
|
+
* truth shared by the frontend router, the backend driver registry, and the
|
|
1456
|
+
* editor's capability lookups.
|
|
1457
|
+
*
|
|
1458
|
+
* Resolution order:
|
|
1459
|
+
* 1. The routing **key** is `collection.dataSource`, else the legacy
|
|
1460
|
+
* `collection.driver`, else {@link DEFAULT_DATA_SOURCE_KEY}.
|
|
1461
|
+
* 2. If a definition is registered for that key, it provides `engine`,
|
|
1462
|
+
* `transport`, and `databaseId`.
|
|
1463
|
+
* 3. Otherwise values are synthesized for backward compatibility: `engine`
|
|
1464
|
+
* from the legacy `driver` (or the key, or `"postgres"`), `transport`
|
|
1465
|
+
* defaults to `"server"`, and `databaseId` from the collection.
|
|
1466
|
+
*
|
|
1467
|
+
* `capabilities` are always derived from the resolved `engine`, so two
|
|
1468
|
+
* data sources sharing an engine share capabilities.
|
|
1469
|
+
*
|
|
1470
|
+
* @param collection the collection (or any object carrying the routing fields)
|
|
1471
|
+
* @param registry optional registry of declared data sources
|
|
1472
|
+
*/
|
|
1473
|
+
function resolveDataSource(collection, registry) {
|
|
1474
|
+
const key = collection?.dataSource ?? collection?.driver ?? _rebasepro_types.DEFAULT_DATA_SOURCE_KEY;
|
|
1475
|
+
const def = registry?.[key];
|
|
1476
|
+
const engine = def?.engine ?? collection?.driver ?? (key !== _rebasepro_types.DEFAULT_DATA_SOURCE_KEY ? key : "postgres");
|
|
1477
|
+
return {
|
|
1478
|
+
key,
|
|
1479
|
+
engine,
|
|
1480
|
+
transport: def?.transport ?? "server",
|
|
1481
|
+
databaseId: collection?.databaseId ?? def?.databaseId,
|
|
1482
|
+
capabilities: (0, _rebasepro_types.getDataSourceCapabilities)(engine)
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
//#endregion
|
|
1443
1486
|
//#region src/collections/CollectionRegistry.ts
|
|
1444
1487
|
var CollectionRegistry = class {
|
|
1488
|
+
/**
|
|
1489
|
+
* Declared data sources, used during normalization to resolve each
|
|
1490
|
+
* collection's engine (so `dataSource`-only collections get the right
|
|
1491
|
+
* capabilities). Empty by default → behaviour keys off `driver` as before.
|
|
1492
|
+
*/
|
|
1493
|
+
dataSources = {};
|
|
1445
1494
|
collectionsByTableName = /* @__PURE__ */ new Map();
|
|
1446
1495
|
collectionsBySlug = /* @__PURE__ */ new Map();
|
|
1447
1496
|
rootCollections = [];
|
|
@@ -1451,9 +1500,20 @@
|
|
|
1451
1500
|
rawRootCollections = [];
|
|
1452
1501
|
cachedRawCollectionsList = null;
|
|
1453
1502
|
lastRawInputSnapshot = null;
|
|
1454
|
-
constructor(collections) {
|
|
1503
|
+
constructor(collections, dataSources) {
|
|
1504
|
+
if (dataSources) this.dataSources = dataSources;
|
|
1455
1505
|
if (collections) this.registerMultiple(collections);
|
|
1456
1506
|
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Provide the declared data sources used to resolve each collection's
|
|
1509
|
+
* engine during normalization. Set this before registering collections.
|
|
1510
|
+
* Returns true if the registry changed (callers may re-register).
|
|
1511
|
+
*/
|
|
1512
|
+
setDataSources(dataSources) {
|
|
1513
|
+
if ((0, fast_equals.deepEqual)(this.dataSources, dataSources)) return false;
|
|
1514
|
+
this.dataSources = dataSources ?? {};
|
|
1515
|
+
return true;
|
|
1516
|
+
}
|
|
1457
1517
|
reset() {
|
|
1458
1518
|
this.collectionsByTableName.clear();
|
|
1459
1519
|
this.collectionsBySlug.clear();
|
|
@@ -1522,6 +1582,10 @@
|
|
|
1522
1582
|
}
|
|
1523
1583
|
normalizeCollection(collection) {
|
|
1524
1584
|
const result = { ...collection };
|
|
1585
|
+
if (result.dataSource && !result.driver) {
|
|
1586
|
+
const engine = resolveDataSource(result, this.dataSources).engine;
|
|
1587
|
+
if (engine) result.driver = engine;
|
|
1588
|
+
}
|
|
1525
1589
|
const extractedRelations = this.extractRelationsFromProperties(result.properties);
|
|
1526
1590
|
const relResult = result;
|
|
1527
1591
|
const manualRelations = (0, _rebasepro_types.getDataSourceCapabilities)(result.driver).supportsRelations ? relResult.relations ?? [] : [];
|
|
@@ -2198,11 +2262,133 @@
|
|
|
2198
2262
|
} });
|
|
2199
2263
|
}
|
|
2200
2264
|
//#endregion
|
|
2265
|
+
//#region src/data/buildRoutedRebaseData.ts
|
|
2266
|
+
/**
|
|
2267
|
+
* Build a {@link RebaseData} that routes each collection to the right
|
|
2268
|
+
* backend based on its resolved data source.
|
|
2269
|
+
*
|
|
2270
|
+
* `.collection(path)` (and dynamic `data.products`-style access) resolves the
|
|
2271
|
+
* collection's data-source key via `resolveKey` and delegates to the matching
|
|
2272
|
+
* entry in `sources`, falling back to `defaultData` when there is no match.
|
|
2273
|
+
* Because routing keys off the *path being accessed*, a reference widget
|
|
2274
|
+
* inside a Firestore form that points at a Postgres collection is still
|
|
2275
|
+
* served by Postgres — routing follows the target, not the ancestor.
|
|
2276
|
+
*
|
|
2277
|
+
* When `sources` is empty this returns `defaultData` untouched, so the
|
|
2278
|
+
* single-driver setup keeps identical behaviour and identity (important for
|
|
2279
|
+
* effect dependencies that key off the data instance).
|
|
2280
|
+
*
|
|
2281
|
+
* @example
|
|
2282
|
+
* const data = buildRoutedRebaseData({
|
|
2283
|
+
* defaultData: client.data,
|
|
2284
|
+
* sources: { analytics: buildRebaseData(firestoreDriver) },
|
|
2285
|
+
* resolveKey: (path) => resolveDataSource(registry.getCollection(path), defs).key
|
|
2286
|
+
* });
|
|
2287
|
+
* await data.products.find(); // → default (server / Postgres)
|
|
2288
|
+
* await data.events.find(); // → Firestore, if `events.dataSource === "analytics"`
|
|
2289
|
+
*/
|
|
2290
|
+
function buildRoutedRebaseData({ defaultData, sources, resolveKey }) {
|
|
2291
|
+
if (!sources || Object.keys(sources).length === 0) return defaultData;
|
|
2292
|
+
function resolve(slugOrPath) {
|
|
2293
|
+
const key = resolveKey(slugOrPath);
|
|
2294
|
+
if (key && sources[key]) return sources[key];
|
|
2295
|
+
return defaultData;
|
|
2296
|
+
}
|
|
2297
|
+
function getAccessor(slugOrPath) {
|
|
2298
|
+
return resolve(slugOrPath).collection(slugOrPath);
|
|
2299
|
+
}
|
|
2300
|
+
return new Proxy({ collection: getAccessor }, { get(_target, prop) {
|
|
2301
|
+
if (prop === "collection") return getAccessor;
|
|
2302
|
+
if (typeof prop === "symbol") return void 0;
|
|
2303
|
+
if (prop === "then" || prop === "toJSON" || prop === "$$typeof") return void 0;
|
|
2304
|
+
return getAccessor((0, _rebasepro_utils.toSnakeCase)(prop));
|
|
2305
|
+
} });
|
|
2306
|
+
}
|
|
2307
|
+
//#endregion
|
|
2308
|
+
//#region src/table-classification.ts
|
|
2309
|
+
/** Schemas that are always considered Rebase-internal. */
|
|
2310
|
+
var REBASE_INTERNAL_SCHEMAS = ["rebase", "auth"];
|
|
2311
|
+
/** Table-name prefixes that mark a table as Rebase-internal regardless of schema. */
|
|
2312
|
+
var REBASE_INTERNAL_PREFIXES = [
|
|
2313
|
+
"_rebase_",
|
|
2314
|
+
"_auth_",
|
|
2315
|
+
"drizzle_"
|
|
2316
|
+
];
|
|
2317
|
+
/**
|
|
2318
|
+
* Synchronously classify a table based on naming conventions.
|
|
2319
|
+
*
|
|
2320
|
+
* @param tableName - The unqualified name of the table.
|
|
2321
|
+
* @param schemaName - The schema the table belongs to (e.g. `"public"`, `"rebase"`).
|
|
2322
|
+
* @returns `"rebase-internal"` when the table belongs to a reserved schema or
|
|
2323
|
+
* carries a reserved prefix; `"user"` otherwise.
|
|
2324
|
+
*
|
|
2325
|
+
* @remarks
|
|
2326
|
+
* Junction-table detection requires an async database query and is therefore
|
|
2327
|
+
* **not** handled by this function. Use {@link detectJunctionTables} to obtain
|
|
2328
|
+
* the set of junction tables, then reclassify as needed.
|
|
2329
|
+
*/
|
|
2330
|
+
function classifyTable(tableName, schemaName) {
|
|
2331
|
+
if (REBASE_INTERNAL_SCHEMAS.includes(schemaName) || REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))) return "rebase-internal";
|
|
2332
|
+
return "user";
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Convenience predicate that checks whether a table is Rebase-internal.
|
|
2336
|
+
*
|
|
2337
|
+
* @param tableName - The unqualified name of the table.
|
|
2338
|
+
* @param schemaName - The schema the table belongs to.
|
|
2339
|
+
* @returns `true` if the table is classified as `"rebase-internal"`.
|
|
2340
|
+
*/
|
|
2341
|
+
function isRebaseInternalTable(tableName, schemaName) {
|
|
2342
|
+
return classifyTable(tableName, schemaName) === "rebase-internal";
|
|
2343
|
+
}
|
|
2344
|
+
/** SQL query that detects junction tables in the `public` schema. */
|
|
2345
|
+
var JUNCTION_TABLES_SQL = `
|
|
2346
|
+
SELECT t.table_name
|
|
2347
|
+
FROM information_schema.tables t
|
|
2348
|
+
WHERE t.table_schema = 'public'
|
|
2349
|
+
AND t.table_type = 'BASE TABLE'
|
|
2350
|
+
AND NOT EXISTS (
|
|
2351
|
+
SELECT 1
|
|
2352
|
+
FROM information_schema.columns c
|
|
2353
|
+
WHERE c.table_schema = t.table_schema
|
|
2354
|
+
AND c.table_name = t.table_name
|
|
2355
|
+
AND c.column_name NOT IN (
|
|
2356
|
+
SELECT kcu.column_name
|
|
2357
|
+
FROM information_schema.key_column_usage kcu
|
|
2358
|
+
JOIN information_schema.table_constraints tc
|
|
2359
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
2360
|
+
AND tc.table_schema = kcu.table_schema
|
|
2361
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
2362
|
+
AND kcu.table_schema = t.table_schema
|
|
2363
|
+
AND kcu.table_name = t.table_name
|
|
2364
|
+
)
|
|
2365
|
+
)
|
|
2366
|
+
`;
|
|
2367
|
+
/**
|
|
2368
|
+
* Asynchronously detect junction (link) tables in the `public` schema.
|
|
2369
|
+
*
|
|
2370
|
+
* A junction table is defined as a table where **every** column participates in
|
|
2371
|
+
* at least one foreign-key constraint.
|
|
2372
|
+
*
|
|
2373
|
+
* @param executeSql - A callback that executes a raw SQL string and returns the
|
|
2374
|
+
* resulting rows.
|
|
2375
|
+
* @returns A `Set` containing the names of all detected junction tables.
|
|
2376
|
+
*/
|
|
2377
|
+
async function detectJunctionTables(executeSql) {
|
|
2378
|
+
const rows = await executeSql(JUNCTION_TABLES_SQL);
|
|
2379
|
+
const junctionTables = /* @__PURE__ */ new Set();
|
|
2380
|
+
for (const row of rows) if (typeof row.table_name === "string") junctionTables.add(row.table_name);
|
|
2381
|
+
return junctionTables;
|
|
2382
|
+
}
|
|
2383
|
+
//#endregion
|
|
2201
2384
|
exports.COLLECTION_PATH_SEPARATOR = COLLECTION_PATH_SEPARATOR;
|
|
2202
2385
|
exports.CollectionRegistry = CollectionRegistry;
|
|
2203
2386
|
exports.DEFAULT_ONE_OF_TYPE = DEFAULT_ONE_OF_TYPE;
|
|
2204
2387
|
exports.DEFAULT_ONE_OF_VALUE = DEFAULT_ONE_OF_VALUE;
|
|
2388
|
+
exports.JUNCTION_TABLES_SQL = JUNCTION_TABLES_SQL;
|
|
2205
2389
|
exports.QueryBuilder = QueryBuilder;
|
|
2390
|
+
exports.REBASE_INTERNAL_PREFIXES = REBASE_INTERNAL_PREFIXES;
|
|
2391
|
+
exports.REBASE_INTERNAL_SCHEMAS = REBASE_INTERNAL_SCHEMAS;
|
|
2206
2392
|
exports.addInitialSlash = addInitialSlash;
|
|
2207
2393
|
exports.and = and;
|
|
2208
2394
|
exports.applyPropertyConditions = applyPropertyConditions;
|
|
@@ -2217,15 +2403,19 @@
|
|
|
2217
2403
|
exports.buildProperty = buildProperty;
|
|
2218
2404
|
exports.buildPropertyCallbacks = buildPropertyCallbacks;
|
|
2219
2405
|
exports.buildRebaseData = buildRebaseData;
|
|
2406
|
+
exports.buildRoutedRebaseData = buildRoutedRebaseData;
|
|
2220
2407
|
exports.canCreateEntity = canCreateEntity;
|
|
2221
2408
|
exports.canDeleteEntity = canDeleteEntity;
|
|
2222
2409
|
exports.canEditEntity = canEditEntity;
|
|
2223
2410
|
exports.canReadCollection = canReadCollection;
|
|
2224
2411
|
exports.checkOperation = checkOperation;
|
|
2412
|
+
exports.classifyTable = classifyTable;
|
|
2225
2413
|
exports.cond = cond;
|
|
2414
|
+
exports.createDataSourceRegistry = createDataSourceRegistry;
|
|
2226
2415
|
exports.createRelationRef = createRelationRef;
|
|
2227
2416
|
exports.createRelationRefWithData = createRelationRefWithData;
|
|
2228
2417
|
exports.defaultUsersCollection = defaultUsersCollection;
|
|
2418
|
+
exports.detectJunctionTables = detectJunctionTables;
|
|
2229
2419
|
exports.enumToObjectEntries = enumToObjectEntries;
|
|
2230
2420
|
exports.evaluateCondition = evaluateCondition;
|
|
2231
2421
|
exports.findRelation = findRelation;
|
|
@@ -2253,6 +2443,7 @@
|
|
|
2253
2443
|
exports.isHidden = isHidden;
|
|
2254
2444
|
exports.isPropertyBuilder = isPropertyBuilder;
|
|
2255
2445
|
exports.isReadOnly = isReadOnly;
|
|
2446
|
+
exports.isRebaseInternalTable = isRebaseInternalTable;
|
|
2256
2447
|
exports.normalizeToEntityRelation = normalizeToEntityRelation;
|
|
2257
2448
|
exports.or = or;
|
|
2258
2449
|
exports.registerConditionOperations = registerConditionOperations;
|
|
@@ -2262,6 +2453,7 @@
|
|
|
2262
2453
|
exports.resolveArrayProperties = resolveArrayProperties;
|
|
2263
2454
|
exports.resolveCollectionPathIds = resolveCollectionPathIds;
|
|
2264
2455
|
exports.resolveCollectionRelations = resolveCollectionRelations;
|
|
2456
|
+
exports.resolveDataSource = resolveDataSource;
|
|
2265
2457
|
exports.resolveDefaultSelectedView = resolveDefaultSelectedView;
|
|
2266
2458
|
exports.resolveEnumValues = resolveEnumValues;
|
|
2267
2459
|
exports.resolveProperties = resolveProperties;
|