@rebasepro/common 0.17.3 → 0.18.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.
Files changed (58) hide show
  1. package/README.md +4 -0
  2. package/dist/collections/CollectionRegistry.d.ts +1 -1
  3. package/dist/collections/default-collections.d.ts +15 -84
  4. package/dist/data/buildRebaseData.d.ts +1 -1
  5. package/dist/data/filter-dialect.d.ts +11 -0
  6. package/dist/data/sort-dialect.d.ts +15 -3
  7. package/dist/index.es.js +375 -63
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/util/builders.d.ts +69 -24
  10. package/dist/util/callback-errors.d.ts +77 -0
  11. package/dist/util/callback-errors.test.d.ts +1 -0
  12. package/dist/util/index.d.ts +1 -0
  13. package/dist/util/policy/evaluatePolicy.d.ts +6 -0
  14. package/dist/util/relations.d.ts +41 -0
  15. package/dist/util/table-name.test.d.ts +1 -0
  16. package/package.json +26 -22
  17. package/src/collections/CollectionRegistry.ts +0 -485
  18. package/src/collections/default-collections.ts +0 -109
  19. package/src/collections/index.ts +0 -2
  20. package/src/data/buildRebaseData.ts +0 -816
  21. package/src/data/buildRoutedRebaseData.ts +0 -103
  22. package/src/data/filter-conditions.ts +0 -46
  23. package/src/data/filter-dialect.ts +0 -737
  24. package/src/data/paginate.ts +0 -334
  25. package/src/data/query_builder.ts +0 -176
  26. package/src/data/resolveDataSource.ts +0 -135
  27. package/src/data/sort-dialect.ts +0 -237
  28. package/src/index.ts +0 -11
  29. package/src/table-classification.ts +0 -109
  30. package/src/types/json-logic-js.d.ts +0 -8
  31. package/src/util/auth-default-policies.ts +0 -215
  32. package/src/util/builders.ts +0 -82
  33. package/src/util/callbacks.ts +0 -122
  34. package/src/util/collections.ts +0 -117
  35. package/src/util/common.ts +0 -2
  36. package/src/util/conditions.ts +0 -168
  37. package/src/util/email.ts +0 -32
  38. package/src/util/entities.ts +0 -282
  39. package/src/util/enums.ts +0 -26
  40. package/src/util/identity.ts +0 -202
  41. package/src/util/index.ts +0 -21
  42. package/src/util/internal-tables.test.ts +0 -188
  43. package/src/util/internal-tables.ts +0 -197
  44. package/src/util/junction-policies.ts +0 -355
  45. package/src/util/paths.ts +0 -27
  46. package/src/util/permissions.test.ts +0 -866
  47. package/src/util/permissions.ts +0 -206
  48. package/src/util/pg-column-to-property.ts +0 -377
  49. package/src/util/policy/evaluatePolicy.ts +0 -194
  50. package/src/util/policy/index.ts +0 -4
  51. package/src/util/policy/policyToPostgres.ts +0 -263
  52. package/src/util/policy/securityRuleToConditions.ts +0 -67
  53. package/src/util/policy/sqlToPolicy.ts +0 -422
  54. package/src/util/relations.ts +0 -236
  55. package/src/util/resolutions.ts +0 -534
  56. package/src/util/resolve-relation.ts +0 -243
  57. package/src/util/storage.ts +0 -177
  58. package/src/util/string-column-length.ts +0 -31
@@ -1,485 +0,0 @@
1
- import {
2
- ArrayProperty,
3
- CollectionCallbacks,
4
- EngineProperties,
5
- CollectionConfig,
6
- getDataSourceCapabilities,
7
- getDeclaredSubcollections,
8
- NumberProperty,
9
- Properties,
10
- Property,
11
- Relation,
12
- RelationProperty,
13
- StringProperty
14
- } from "@rebasepro/types";
15
- import { deepEqual } from "fast-equals";
16
-
17
- import {
18
- enumToObjectEntries,
19
- findRelation,
20
- getSubcollections,
21
- getTableName,
22
- resolveCollectionRelations,
23
- resolveRelation
24
- } from "../util";
25
- import { deepClone, mergeDeep, removeFunctions } from "@rebasepro/utils";
26
- import { DataSourceRegistry, resolveDataSource } from "../data/resolveDataSource";
27
-
28
- export class CollectionRegistry {
29
-
30
- /**
31
- * Declared data sources, used during normalization to resolve each
32
- * collection's engine (so `dataSource`-only collections get the right
33
- * capabilities). Empty by default.
34
- */
35
- private dataSources: DataSourceRegistry = {};
36
-
37
- /**
38
- * Global lifecycle callbacks applied to every collection.
39
- * Runs on all data paths (REST, WebSocket, `rebase.data`).
40
- * Execution order: global → collection → property callbacks.
41
- */
42
- private _globalCallbacks?: CollectionCallbacks;
43
-
44
- /**
45
- * Set global lifecycle callbacks that apply to every collection.
46
- * Typically called once during backend initialization.
47
- */
48
- setGlobalCallbacks(callbacks: CollectionCallbacks): void {
49
- this._globalCallbacks = callbacks;
50
- }
51
-
52
- /**
53
- * Get the currently registered global callbacks, if any.
54
- */
55
- getGlobalCallbacks(): CollectionCallbacks | undefined {
56
- return this._globalCallbacks;
57
- }
58
-
59
- // Normalized runtime layer (used by Data Grid / UI)
60
- private collectionsByTableName = new Map<string, CollectionConfig>();
61
- private collectionsBySlug = new Map<string, CollectionConfig>();
62
- private rootCollections: CollectionConfig[] = [];
63
- private cachedCollectionsList: CollectionConfig[] | null = null;
64
-
65
- // Raw configuration layer (used by Collection Editor AST generator)
66
- private rawCollectionsByTableName = new Map<string, CollectionConfig>();
67
- private rawCollectionsBySlug = new Map<string, CollectionConfig>();
68
- private rawRootCollections: CollectionConfig[] = [];
69
- private cachedRawCollectionsList: CollectionConfig[] | null = null;
70
-
71
- // Entity of raw input for idempotency check — compared BEFORE normalization
72
- // to avoid the issue where normalization creates new objects that always fail equality.
73
- private lastRawInputEntity: ReturnType<typeof removeFunctions>[] | null = null;
74
-
75
- constructor(collections?: CollectionConfig[], dataSources?: DataSourceRegistry) {
76
- if (dataSources) this.dataSources = dataSources;
77
- if (collections) {
78
- this.registerMultiple(collections);
79
- }
80
- }
81
-
82
- /**
83
- * Provide the declared data sources used to resolve each collection's
84
- * engine during normalization. Set this before registering collections.
85
- * Returns true if the registry changed (callers may re-register).
86
- */
87
- setDataSources(dataSources: DataSourceRegistry): boolean {
88
- if (deepEqual(this.dataSources, dataSources)) return false;
89
- this.dataSources = dataSources ?? {};
90
- return true;
91
- }
92
-
93
- reset() {
94
- this.collectionsByTableName.clear();
95
- this.collectionsBySlug.clear();
96
- this.rootCollections = [];
97
- this.cachedCollectionsList = null;
98
-
99
- this.rawCollectionsByTableName.clear();
100
- this.rawCollectionsBySlug.clear();
101
- this.rawRootCollections = [];
102
- this.cachedRawCollectionsList = null;
103
- }
104
-
105
- /**
106
- * Registers a collection and its subcollections recursively.
107
- * Returns true if the collections have changed, false otherwise.
108
- *
109
- * Idempotent: compares the raw input (before normalization) against a stored
110
- * entity. Only re-normalizes and re-registers when the raw input actually changed.
111
- * @param collections
112
- */
113
- registerMultiple(collections: CollectionConfig[]): boolean {
114
- // Compare raw input BEFORE normalization to detect actual changes.
115
- // This avoids the old issue where normalization creates new objects
116
- // that always fail deep-equal even when the source data is identical.
117
- const rawEntity = collections.map(c => removeFunctions(c));
118
- if (this.lastRawInputEntity && deepEqual(this.lastRawInputEntity, rawEntity)) {
119
- return false;
120
- }
121
-
122
- this.reset();
123
- // Phase 0: Populate maps with raw collections first for string target resolution
124
- collections.forEach((c) => {
125
- if (c.slug) {
126
- this.collectionsBySlug.set(c.slug, c);
127
- }
128
- this.collectionsByTableName.set(getTableName(c), c);
129
- });
130
-
131
- const normalizedCollections = collections.map(c => this.normalizeCollection({ ...c }));
132
-
133
- // Phase 1: Register all top-level collections first (without recursion).
134
- // This ensures that injected entityViews (e.g. History tab) are preserved.
135
- // Without this, _registerRecursively could register a relation-target collection
136
- // (e.g. Tags from Posts.relations) using the raw module object (without injected views)
137
- // before the top-level Tags collection (with injected views) gets its turn.
138
- normalizedCollections.forEach((c, index) => {
139
- const raw = deepClone(collections[index]);
140
- this.rootCollections.push(c);
141
- this.rawRootCollections.push(raw);
142
-
143
- const normalized = this.normalizeCollection(c);
144
- this.collectionsByTableName.set(getTableName(normalized), normalized);
145
- this.rawCollectionsByTableName.set(getTableName(raw), raw);
146
- if (normalized.slug) {
147
- this.collectionsBySlug.set(normalized.slug, normalized);
148
- }
149
- if (raw.slug) {
150
- this.rawCollectionsBySlug.set(raw.slug, raw);
151
- }
152
- });
153
-
154
- // Phase 2: Now recurse into subcollections (relations, etc.)
155
- normalizedCollections.forEach((c) => {
156
- const subcollections = getSubcollections(c);
157
- if (subcollections && subcollections.length > 0) {
158
- subcollections.forEach((subCollection) => {
159
- if (!subCollection) return;
160
- // Spread to avoid mutating the original target() return value
161
- this._registerRecursively(this.normalizeCollection({ ...subCollection }), deepClone(subCollection));
162
- });
163
- }
164
- });
165
-
166
- // Store the entity for future comparisons
167
- this.lastRawInputEntity = rawEntity;
168
-
169
- return true;
170
- }
171
-
172
- register(collection: CollectionConfig, rawCollection?: CollectionConfig) {
173
- const raw = rawCollection ? deepClone(rawCollection) : deepClone(collection);
174
-
175
- this.rootCollections.push(collection);
176
- this.rawRootCollections.push(raw);
177
-
178
- this._registerRecursively(collection, raw);
179
- }
180
-
181
- private _registerRecursively(collection: CollectionConfig, rawCollection: CollectionConfig) {
182
- if (this.collectionsByTableName.has(getTableName(collection))) {
183
- return;
184
- }
185
-
186
- const normalizedCollection = this.normalizeCollection(collection);
187
- this.collectionsByTableName.set(getTableName(normalizedCollection), normalizedCollection);
188
- this.rawCollectionsByTableName.set(getTableName(rawCollection), rawCollection);
189
-
190
- if (normalizedCollection.slug) {
191
- this.collectionsBySlug.set(normalizedCollection.slug, normalizedCollection);
192
- }
193
- if (rawCollection.slug) {
194
- this.rawCollectionsBySlug.set(rawCollection.slug, rawCollection);
195
- }
196
-
197
- // Use the normalized collection for subcollection discovery so that
198
- // both inline-extracted and explicit relations are considered.
199
- const subcollections = getSubcollections(normalizedCollection);
200
-
201
- if (subcollections && subcollections.length > 0) {
202
- subcollections.forEach((subCollection) => {
203
- if (!subCollection) return;
204
- // Spread to avoid mutating the original target() return value
205
- this._registerRecursively(this.normalizeCollection({ ...subCollection }), deepClone(subCollection));
206
- });
207
- }
208
- }
209
-
210
- public normalizeCollection(collection: CollectionConfig): CollectionConfig {
211
- // Work on a shallow copy to avoid mutating the caller's reference.
212
- // This is critical for idempotency (the raw input must not be changed)
213
- // and for preventing mutation of module-level collection singletons.
214
- const result = { ...collection } as CollectionConfig;
215
-
216
- // 0. Resolve and stamp `dataSource` and `engine` on the normalized copy.
217
- // After this block every normalized collection has both fields set,
218
- // so downstream code can read them directly without calling
219
- // `resolveDataSource()`. Only the normalized layer is affected —
220
- // the raw layer used by the collection editor keeps the author's
221
- // original fields.
222
- {
223
- const resolved = resolveDataSource(result, this.dataSources);
224
- if (!result.dataSource) (result as { dataSource?: string }).dataSource = resolved.key;
225
- if (!result.engine) (result as { engine?: string }).engine = resolved.engine;
226
- }
227
-
228
- // Relations are left exactly as authored.
229
- //
230
- // This used to hoist every inline relation property into
231
- // `collection.relations`, merge it with the declared ones, and run each
232
- // through `sanitizeRelation` — a pass that guessed at missing fields and
233
- // fell back to the raw relation when it threw. `resolveCollectionRelations`
234
- // now reads both sources itself and defaults deterministically, so there
235
- // is nothing to hoist, nothing to merge and nothing to guess.
236
- //
237
- // The hoisting also had a defect worth not reinstating: it flattened
238
- // relations declared inside a `map` up to the collection's top level,
239
- // where they became child-view tabs keyed by the inner property key.
240
-
241
- // Stamp each relation property with its resolved relation.
242
- const properties: Properties = this.normalizeProperties(result.properties, result);
243
- result.properties = properties as EngineProperties;
244
-
245
- // `childCollections` is deliberately NOT populated here.
246
- //
247
- // It used to be, from the same many-relations `getEntityChildViews`
248
- // reads — but stamped with the *target's* slug rather than the relation
249
- // key, and then cached onto the collection, so the registry's version
250
- // shadowed the correct one for every consumer downstream. Deriving on
251
- // read leaves one implementation and keeps `childCollections` meaning
252
- // what it documents: a custom driver's explicit override.
253
- return result;
254
- }
255
-
256
- private normalizeProperties(properties: Properties, collection: CollectionConfig): Properties {
257
- const newProperties: Properties = {};
258
- for (const key in properties) {
259
- newProperties[key] = this.normalizeProperty(key, properties[key], collection);
260
- }
261
- return newProperties;
262
- }
263
-
264
- private normalizeProperty(key: string, property: Property, collection: CollectionConfig): Property {
265
- const newProperty = { ...property };
266
-
267
- if (newProperty.type === "map" && newProperty.properties) {
268
- newProperty.properties = this.normalizeProperties(newProperty.properties, collection);
269
- } else if (newProperty.type === "array") {
270
- // Cast to get a properly typed mutable reference
271
- const arrayProp = newProperty as ArrayProperty;
272
- if (arrayProp.of) {
273
- if (Array.isArray(arrayProp.of)) {
274
- (arrayProp as { of: Property | Property[] }).of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, collection));
275
- } else {
276
- arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, collection);
277
- }
278
- } else if (arrayProp.oneOf && arrayProp.oneOf.properties) {
279
- arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, collection);
280
- }
281
- } else if ((newProperty.type === "string" || newProperty.type === "number") && newProperty.enum) {
282
- const stringOrNumberProperty = newProperty as StringProperty | NumberProperty;
283
- if (typeof stringOrNumberProperty.enum === "object" && !Array.isArray(stringOrNumberProperty.enum)) {
284
- stringOrNumberProperty.enum = enumToObjectEntries(stringOrNumberProperty.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [];
285
- }
286
- } else if (newProperty.type === "relation") {
287
- const relationProperty = newProperty as RelationProperty;
288
-
289
- // A property either declares its link inline, or names one the
290
- // collection declares. Resolve the first directly; look the second
291
- // up by name. Either way the property carries the fully-defaulted
292
- // relation, so no consumer has to re-derive it.
293
- if (relationProperty.relation) {
294
- relationProperty.resolvedRelation = resolveRelation(relationProperty.relation, collection, key);
295
- } else {
296
- const declared = resolveCollectionRelations(collection)[key];
297
- if (declared) {
298
- relationProperty.resolvedRelation = declared;
299
- } else {
300
- console.warn(
301
- `Relation property '${key}' on '${collection.slug}' declares no \`relation\`, and the ` +
302
- "collection has no relation of that name."
303
- );
304
- }
305
- }
306
- }
307
-
308
- return newProperty;
309
- }
310
-
311
- get(path: string): CollectionConfig | undefined {
312
- // First try slug lookup
313
- const bySlug = this.collectionsBySlug.get(path);
314
- if (bySlug) return bySlug;
315
-
316
- // Fallback: normalize hyphens → underscores (URLs use kebab-case, slugs use snake_case)
317
- if (path.includes("-")) {
318
- const normalized = path.replace(/-/g, "_");
319
- const byNormalized = this.collectionsBySlug.get(normalized);
320
- if (byNormalized) return byNormalized;
321
- }
322
-
323
- // Fallback to table name lookup
324
- return this.collectionsByTableName.get(path);
325
- }
326
-
327
- /**
328
- * Gets the pristine, un-normalized collection exactly as it was provided.
329
- * Useful for the AST editor so it doesn't accidentally serialize injected metadata back to disk.
330
- */
331
- getRaw(path: string): CollectionConfig | undefined {
332
- const bySlug = this.rawCollectionsBySlug.get(path);
333
- if (bySlug) return bySlug;
334
-
335
- // Fallback: normalize hyphens → underscores (URLs use kebab-case, slugs use snake_case)
336
- if (path.includes("-")) {
337
- const normalized = path.replace(/-/g, "_");
338
- const byNormalized = this.rawCollectionsBySlug.get(normalized);
339
- if (byNormalized) return byNormalized;
340
- }
341
-
342
- return this.rawCollectionsByTableName.get(path);
343
- }
344
-
345
- /**
346
- * Get collection by resolving multi-segment paths through relations
347
- * e.g., "authors/70/posts" resolves to the posts collection
348
- */
349
- getCollectionByPath(collectionPath: string): CollectionConfig | undefined {
350
- // Handle simple single collection path
351
- if (!collectionPath.includes("/")) {
352
- return this.get(collectionPath);
353
- }
354
-
355
- // Handle multi-segment paths by resolving through relations
356
- const pathSegments = collectionPath.split("/").filter(p => p);
357
-
358
- if (pathSegments.length < 3 || pathSegments.length % 2 === 0) {
359
- throw new Error(`Invalid relation path: ${collectionPath}. Expected format: collection/id/relation or collection/id/relation/id/relation`);
360
- }
361
-
362
- // Start with the root collection
363
- const rootCollectionPath = pathSegments[0];
364
- let currentCollection = this.get(rootCollectionPath);
365
-
366
- if (!currentCollection) {
367
- throw new Error(`Root collection not found: ${rootCollectionPath}`);
368
- }
369
-
370
- // Navigate through the path using relations
371
- for (let i = 2; i < pathSegments.length; i += 2) {
372
- const relationKey = pathSegments[i];
373
-
374
- // Get relations for current collection
375
- if (!getDataSourceCapabilities(currentCollection.engine).supportsRelations) {
376
- throw new Error(`Relation path navigation requires a collection that supports relations, but '${currentCollection.slug}' uses engine '${currentCollection.engine}'`);
377
- }
378
- const resolvedRelations = resolveCollectionRelations(currentCollection);
379
- const relation = findRelation(resolvedRelations, relationKey);
380
-
381
- if (!relation) {
382
- throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
383
- }
384
-
385
- // Move to the target collection.
386
- //
387
- // By the relation's own target, never by a slug lookup on its
388
- // *name*: `this.get(relation.relationName)` searches the global slug
389
- // map, so a relation named `people` that targets `notes` resolved to
390
- // an unrelated root collection called `people` — and a nested write
391
- // then ran that collection's callbacks against its properties.
392
- // The registered instance is preferred, matched by table, to pick up
393
- // whatever normalization and injection it received.
394
- const target = relation.target();
395
- currentCollection = this.collectionsByTableName.get(getTableName(target))
396
- ?? this.normalizeCollection(target);
397
-
398
- // If there are more segments, continue navigation
399
- if (i + 1 < pathSegments.length) {
400
- // Skip entity ID segment
401
- }
402
- }
403
-
404
- return currentCollection;
405
- }
406
-
407
- getCollections(): CollectionConfig[] {
408
- if (!this.cachedCollectionsList) {
409
- this.cachedCollectionsList = Array.from(this.collectionsByTableName.values());
410
- }
411
- return this.cachedCollectionsList;
412
- }
413
-
414
- getRawCollections(): CollectionConfig[] {
415
- if (!this.cachedRawCollectionsList) {
416
- this.cachedRawCollectionsList = Array.from(this.rawCollectionsByTableName.values());
417
- }
418
- return this.cachedRawCollectionsList;
419
- }
420
-
421
- /**
422
- * Resolves a multi-segment path like "products/123/locales" and returns
423
- * information about the collections and entity IDs along the path
424
- */
425
- resolvePathToCollections(path: string): {
426
- collections: CollectionConfig[],
427
- entityIds: (string | number)[],
428
- finalCollection: CollectionConfig
429
- } {
430
- const pathSegments = path.split("/").filter(p => p);
431
-
432
- if (pathSegments.length === 0) {
433
- throw new Error(`Invalid path: ${path}`);
434
- }
435
-
436
- if (pathSegments.length % 2 !== 1) {
437
- throw new Error(`Invalid collection path: ${path}. It must have an odd number of segments.`);
438
- }
439
-
440
- const collections: CollectionConfig[] = [];
441
- const entityIds: (string | number)[] = [];
442
-
443
- // Start with the first collection
444
- let currentCollection = this.get(pathSegments[0]);
445
-
446
- if (!currentCollection) {
447
- throw new Error(`Unknown collection path or slug: ${pathSegments[0]}`);
448
- }
449
-
450
- collections.push(currentCollection);
451
-
452
- // Process the rest of the path in pairs (entityId, subcollectionSlug)
453
- for (let i = 1; i < pathSegments.length; i += 2) {
454
- const entityId = pathSegments[i];
455
- entityIds.push(entityId);
456
-
457
- if (i + 1 < pathSegments.length) {
458
- const subcollectionSlug = pathSegments[i + 1];
459
- const subcollections: CollectionConfig[] | undefined = getSubcollections(currentCollection);
460
- if (!subcollections || subcollections.length === 0) {
461
- throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
462
- }
463
-
464
- const subcollection: CollectionConfig | undefined = subcollections.find(c => c.slug === subcollectionSlug);
465
- if (!subcollection) {
466
- throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
467
- }
468
- // The child as resolved, not whatever root collection happens to
469
- // share its slug. Re-looking it up globally both risked the wrong
470
- // collection and discarded the relation's `overrides`, which are
471
- // applied when the child view is built.
472
- currentCollection = this.normalizeCollection(subcollection);
473
- collections.push(currentCollection);
474
- }
475
- }
476
-
477
- return {
478
- collections,
479
- entityIds,
480
- finalCollection: currentCollection
481
- };
482
- }
483
-
484
- }
485
-
@@ -1,109 +0,0 @@
1
- import { defineCollection } from "../util/builders";
2
-
3
- /**
4
- * Default users collection.
5
- *
6
- * Prepended to the developer's collections array by the admin and server.
7
- * Slug-based dedup (Map keyed by slug, last-write-wins) lets developers
8
- * override by defining their own collection with `slug: "users"`.
9
- *
10
- * Schema only — no `admin` block. This package is on the backend's dependency path,
11
- * where that field does not exist: `@rebasepro/cms-types` adds it by declaration
12
- * merging, and a BaaS install never installs that. The scaffolded
13
- * `config/collections/users.ts` carries the presentation for projects that want this
14
- * collection in their panel, which is also where it is editable.
15
- */
16
- export const defaultUsersCollection = defineCollection({
17
- name: "Users",
18
- singularName: "User",
19
- slug: "users",
20
- auth: true,
21
- table: "users",
22
- schema: "rebase",
23
- securityRules: [
24
- { operation: "select",
25
- roles: ["admin"] },
26
- { operations: ["insert", "update", "delete"],
27
- roles: ["admin"] }
28
- ],
29
- properties: {
30
- id: {
31
- name: "ID",
32
- type: "string",
33
- isId: "uuid"
34
- },
35
- email: {
36
- name: "Email",
37
- type: "string",
38
- validation: { required: true,
39
- unique: true }
40
- },
41
- displayName: {
42
- name: "Name",
43
- type: "string",
44
- columnName: "display_name",
45
- validation: { required: true }
46
- },
47
- photoURL: {
48
- name: "Photo URL",
49
- type: "string",
50
- columnName: "photo_url"
51
- },
52
- roles: {
53
- name: "Roles",
54
- type: "array",
55
- columnType: "text[]",
56
- of: {
57
- name: "Role",
58
- type: "string",
59
- enum: {
60
- admin: "Admin",
61
- editor: "Editor",
62
- viewer: "Viewer"
63
- }
64
- }
65
- },
66
- passwordHash: {
67
- name: "Password Hash",
68
- type: "string",
69
- columnName: "password_hash",
70
- excludeFromApi: true
71
- },
72
- emailVerified: {
73
- name: "Email Verified",
74
- type: "boolean",
75
- columnName: "email_verified",
76
- defaultValue: false
77
- },
78
- emailVerificationToken: {
79
- name: "Email Verification Token",
80
- type: "string",
81
- columnName: "email_verification_token",
82
- excludeFromApi: true
83
- },
84
- emailVerificationSentAt: {
85
- name: "Email Verification Sent At",
86
- type: "date",
87
- columnName: "email_verification_sent_at"
88
- },
89
- metadata: {
90
- name: "Metadata",
91
- type: "map",
92
- keyValue: true,
93
- properties: {},
94
- defaultValue: {}
95
- },
96
- createdAt: {
97
- name: "Created At",
98
- type: "date",
99
- columnName: "created_at",
100
- autoValue: "on_create"
101
- },
102
- updatedAt: {
103
- name: "Updated At",
104
- type: "date",
105
- columnName: "updated_at",
106
- autoValue: "on_update"
107
- }
108
- }
109
- });
@@ -1,2 +0,0 @@
1
- export * from "./CollectionRegistry";
2
- export * from "./default-collections";