@rebasepro/types 0.11.1-canary.gfd39654 → 0.12.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/controllers/collection_registry.d.ts +1 -1
- package/dist/controllers/data.d.ts +148 -9
- package/dist/index.es.js +287 -97
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +20 -0
- package/dist/types/api_keys.d.ts +60 -5
- package/dist/types/collections.d.ts +56 -31
- package/dist/types/data_source.d.ts +42 -1
- package/dist/types/database_adapter.d.ts +13 -8
- package/dist/types/entity_callbacks.d.ts +2 -2
- package/dist/types/history.d.ts +62 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/postgres_introspection.d.ts +95 -0
- package/dist/types/project_manifest.d.ts +146 -73
- package/dist/types/properties.d.ts +38 -49
- package/dist/types/storage_source.d.ts +60 -0
- package/dist/types/websockets.d.ts +0 -37
- package/package.json +1 -1
- package/src/controllers/collection_registry.ts +1 -1
- package/src/controllers/data.ts +155 -9
- package/src/types/admin_block.ts +48 -0
- package/src/types/api_keys.ts +61 -5
- package/src/types/collections.ts +68 -39
- package/src/types/data_source.ts +60 -1
- package/src/types/database_adapter.ts +13 -8
- package/src/types/entity_callbacks.ts +2 -2
- package/src/types/history.ts +66 -0
- package/src/types/index.ts +2 -0
- package/src/types/postgres_introspection.ts +101 -0
- package/src/types/project_manifest.ts +149 -80
- package/src/types/properties.ts +43 -56
- package/src/types/storage_source.ts +130 -0
- package/src/types/websockets.ts +0 -42
package/src/types/properties.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { ComponentRef } from "./component_ref";
|
|
|
2
2
|
|
|
3
3
|
import type { Entity, EntityReference, EntityRelation, EntityValues, GeoPoint, Vector } from "./entities";
|
|
4
4
|
import type { JoinStep, OnAction, Relation, ResolvedRelation } from "./relations";
|
|
5
|
-
import type { CollectionConfig, FilterValues, WhereFilterOp } from "./collections";
|
|
6
5
|
import type { ColorKey, ColorScheme } from "./chips";
|
|
7
6
|
import type { AuthState } from "../controllers/auth_state";
|
|
8
7
|
import type { AfterReadProps, BeforeSaveProps } from "./entity_callbacks";
|
|
@@ -65,19 +64,49 @@ export type Properties = {
|
|
|
65
64
|
[key: string]: Property;
|
|
66
65
|
};
|
|
67
66
|
|
|
67
|
+
/**
|
|
68
|
+
* `Omit` that survives a union.
|
|
69
|
+
*
|
|
70
|
+
* `Property` is a union discriminated on `type`, and a bare `Omit<Property, K>`
|
|
71
|
+
* collapses it into one object whose `type` is the union of every tag — so
|
|
72
|
+
* `property.type === "string"` stops narrowing and the concrete property types
|
|
73
|
+
* become unreachable. The `T extends unknown` clause makes it distribute, so
|
|
74
|
+
* each member is omitted from separately and keeps its own discriminant.
|
|
75
|
+
*/
|
|
76
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The fields that describe a property's **column**, which only an engine with
|
|
80
|
+
* columns has.
|
|
81
|
+
*
|
|
82
|
+
* `columnType` names a Postgres type (`uuid`, `bigserial`, `jsonb`, `text[]`)
|
|
83
|
+
* and `columnName` overrides the snake_case derivation used to build a column
|
|
84
|
+
* name. `DataSourceCapabilities.supportsColumnTypes` already reported this at
|
|
85
|
+
* runtime — `false` for both document engines — while the types let a MongoDB
|
|
86
|
+
* property declare `columnType: "bigserial"`.
|
|
87
|
+
*
|
|
88
|
+
* They stay declared on the concrete property interfaces rather than moving,
|
|
89
|
+
* because that is where their per-type value unions live; what changes is that
|
|
90
|
+
* the document engines' property aliases omit them.
|
|
91
|
+
*/
|
|
92
|
+
type SqlColumnFields = "columnType" | "columnName";
|
|
93
|
+
|
|
68
94
|
export type PostgresProperty = Exclude<Property, ReferenceProperty>;
|
|
69
95
|
export type PostgresProperties = {
|
|
70
96
|
[key: string]: PostgresProperty;
|
|
71
97
|
};
|
|
72
98
|
|
|
73
|
-
export type FirebaseProperty = Exclude<Property, RelationProperty>;
|
|
99
|
+
export type FirebaseProperty = DistributiveOmit<Exclude<Property, RelationProperty | VectorProperty>, SqlColumnFields>;
|
|
74
100
|
export type FirebaseProperties = {
|
|
75
101
|
[key: string]: FirebaseProperty;
|
|
76
102
|
};
|
|
77
103
|
|
|
78
104
|
// MongoDB is a document store: it uses references (stored pointers), not
|
|
79
105
|
// SQL-style relations/joins. Same gating as Firestore.
|
|
80
|
-
|
|
106
|
+
//
|
|
107
|
+
// `vector` goes with them: it is pgvector-shaped, only `@rebasepro/server-postgres`
|
|
108
|
+
// reads it, and `supportsVectors` on the engine's capabilities says so.
|
|
109
|
+
export type MongoProperty = DistributiveOmit<Exclude<Property, RelationProperty | VectorProperty>, SqlColumnFields>;
|
|
81
110
|
export type MongoProperties = {
|
|
82
111
|
[key: string]: MongoProperty;
|
|
83
112
|
};
|
|
@@ -164,7 +193,7 @@ export interface BaseProperty<CustomProps = unknown> {
|
|
|
164
193
|
|
|
165
194
|
/**
|
|
166
195
|
* You can use this prop to reuse a property that has been defined
|
|
167
|
-
* in the top level of the
|
|
196
|
+
* in the top level of the admin in the prop `fields`.
|
|
168
197
|
* All the configuration will be taken from the inherited config, and
|
|
169
198
|
* overwritten by the current property config.
|
|
170
199
|
*/
|
|
@@ -473,20 +502,6 @@ export interface ReferenceProperty extends BaseProperty {
|
|
|
473
502
|
* property.
|
|
474
503
|
*/
|
|
475
504
|
path?: string;
|
|
476
|
-
/**
|
|
477
|
-
* Allow selection of entities that pass the given filter only.
|
|
478
|
-
* e.g. `fixedFilter: { age: [">=", 18] }`
|
|
479
|
-
*/
|
|
480
|
-
fixedFilter?: FilterValues<string>;
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Should the reference include the ID of the entity. Defaults to `true`
|
|
484
|
-
*/
|
|
485
|
-
includeId?: boolean;
|
|
486
|
-
/**
|
|
487
|
-
* Should the reference include a link to the entity (open the entity details). Defaults to `true`
|
|
488
|
-
*/
|
|
489
|
-
includeEntityLink?: boolean;
|
|
490
505
|
}
|
|
491
506
|
|
|
492
507
|
/**
|
|
@@ -548,29 +563,6 @@ export interface RelationProperty extends BaseProperty {
|
|
|
548
563
|
* collection's `relations` array.
|
|
549
564
|
*/
|
|
550
565
|
resolvedRelation?: ResolvedRelation;
|
|
551
|
-
|
|
552
|
-
// ─── UI configuration ───
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Allow selection of entities that pass the given filter only.
|
|
556
|
-
* e.g. `fixedFilter: { age: [">=", 18] }`
|
|
557
|
-
*/
|
|
558
|
-
fixedFilter?: FilterValues<string>;
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Should the reference include the ID of the entity. Defaults to `true`
|
|
562
|
-
*/
|
|
563
|
-
includeId?: boolean;
|
|
564
|
-
/**
|
|
565
|
-
* Should the reference include a link to the entity (open the entity details). Defaults to `true`
|
|
566
|
-
*/
|
|
567
|
-
includeEntityLink?: boolean;
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Choose the widget to use for selecting the relation.
|
|
571
|
-
* Defaults to `select`.
|
|
572
|
-
*/
|
|
573
|
-
widget?: "select" | "dialog";
|
|
574
566
|
}
|
|
575
567
|
|
|
576
568
|
export interface ArrayProperty extends BaseProperty {
|
|
@@ -634,17 +626,6 @@ export interface ArrayProperty extends BaseProperty {
|
|
|
634
626
|
* Rules for validating this property
|
|
635
627
|
*/
|
|
636
628
|
validation?: ArrayPropertyValidationSchema;
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Can the elements in this array be reordered. Defaults to `true`.
|
|
640
|
-
* This prop has no effect if `disabled` is set to true.
|
|
641
|
-
*/
|
|
642
|
-
sortable?: boolean;
|
|
643
|
-
/**
|
|
644
|
-
* Can the elements in this array be added. Defaults to `true`
|
|
645
|
-
* This prop has no effect if `disabled` is set to true.
|
|
646
|
-
*/
|
|
647
|
-
canAddElements?: boolean;
|
|
648
629
|
}
|
|
649
630
|
|
|
650
631
|
export interface MapProperty extends BaseProperty {
|
|
@@ -665,6 +646,12 @@ export interface MapProperty extends BaseProperty {
|
|
|
665
646
|
* Order in which the properties are displayed.
|
|
666
647
|
* If you are specifying your collection as code, the order is the same as the
|
|
667
648
|
* one you define in `properties`, and you don't need to specify this prop.
|
|
649
|
+
*
|
|
650
|
+
* Stays on the property rather than moving to the `admin` block, unlike the
|
|
651
|
+
* rest of the map's presentation options: `sortProperties` in
|
|
652
|
+
* `@rebasepro/common` reads it recursively, and `@rebasepro/firebase` calls
|
|
653
|
+
* that when it builds collections. A core package cannot read the admin
|
|
654
|
+
* block — the field exists only once `@rebasepro/admin-types` is installed.
|
|
668
655
|
*/
|
|
669
656
|
propertiesOrder?: string[];
|
|
670
657
|
/**
|
|
@@ -673,13 +660,13 @@ export interface MapProperty extends BaseProperty {
|
|
|
673
660
|
* will be considered valid, even if you set `required` in the properties.
|
|
674
661
|
*/
|
|
675
662
|
validation?: PropertyValidationSchema;
|
|
676
|
-
/**
|
|
677
|
-
* Properties that are displayed when rendered as a preview
|
|
678
|
-
*/
|
|
679
|
-
previewProperties?: string[];
|
|
680
663
|
/**
|
|
681
664
|
* Render this map as a key-value table that allows to use
|
|
682
665
|
* arbitrary keys. You don't need to define the properties in this case.
|
|
666
|
+
*
|
|
667
|
+
* Core rather than admin despite the wording: it says the map has no
|
|
668
|
+
* declared shape, which is what the OpenAPI generator emits the schema
|
|
669
|
+
* from (`additionalProperties` instead of a property list).
|
|
683
670
|
*/
|
|
684
671
|
keyValue?: boolean;
|
|
685
672
|
}
|
|
@@ -88,3 +88,133 @@ export interface ResolvedStorageSource {
|
|
|
88
88
|
/** Human-readable label. */
|
|
89
89
|
label?: string;
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The environment-variable suffix for a storage or data source key.
|
|
94
|
+
*
|
|
95
|
+
* `""` for the default source — so a single-bucket project keeps configuring
|
|
96
|
+
* plain `S3_BUCKET` — and `__<KEY>` for every named one, uppercased with
|
|
97
|
+
* non-alphanumerics collapsed to underscores: `media-cdn` → `S3_BUCKET__MEDIA_CDN`.
|
|
98
|
+
*
|
|
99
|
+
* The rule derives the variable name from the declared key rather than
|
|
100
|
+
* discovering keys by scanning the environment. Scanning would have to guess how
|
|
101
|
+
* `S3_BUCKET__MEDIA_CDN` splits into a key; deriving cannot be ambiguous, and a
|
|
102
|
+
* typo surfaces as a missing source at boot instead of a silently ignored
|
|
103
|
+
* variable.
|
|
104
|
+
*
|
|
105
|
+
* It lives in this package, with no dependencies, because four things must agree
|
|
106
|
+
* on it exactly: the CLI (validating a build), the runtime (reading its own
|
|
107
|
+
* environment), the control plane (writing a tenant's Secret), and the docs. A
|
|
108
|
+
* second implementation of a naming convention is a second chance to disagree.
|
|
109
|
+
*
|
|
110
|
+
* @group Models
|
|
111
|
+
*/
|
|
112
|
+
export function storageEnvSuffix(key: string, defaultKey: string = DEFAULT_STORAGE_SOURCE_KEY): string {
|
|
113
|
+
if (!key || key === defaultKey) return "";
|
|
114
|
+
const normalized = key
|
|
115
|
+
.replace(/[^A-Za-z0-9]+/g, "_")
|
|
116
|
+
.replace(/^_+|_+$/g, "")
|
|
117
|
+
.toUpperCase();
|
|
118
|
+
if (!normalized) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Source key "${key}" cannot be turned into an environment variable name. ` +
|
|
121
|
+
"Use a key containing at least one letter or digit."
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return `__${normalized}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Two distinct keys that collapse onto the same variable name, or `null`.
|
|
129
|
+
*
|
|
130
|
+
* `media-cdn` and `media_cdn` are different source keys but the same suffix, so
|
|
131
|
+
* without this one of them silently reads the other's configuration. Returns the
|
|
132
|
+
* offending pair rather than throwing, so each caller can raise it in its own
|
|
133
|
+
* idiom — a `BundleError` at boot, a build failure in the CLI, a rejected deploy
|
|
134
|
+
* in a control plane.
|
|
135
|
+
*
|
|
136
|
+
* @group Models
|
|
137
|
+
*/
|
|
138
|
+
export function findStorageSuffixCollision(
|
|
139
|
+
keys: string[],
|
|
140
|
+
defaultKey: string = DEFAULT_STORAGE_SOURCE_KEY
|
|
141
|
+
): { a: string; b: string; suffix: string } | null {
|
|
142
|
+
const seen = new Map<string, string>();
|
|
143
|
+
for (const key of keys) {
|
|
144
|
+
const suffix = storageEnvSuffix(key, defaultKey);
|
|
145
|
+
const existing = seen.get(suffix);
|
|
146
|
+
if (existing !== undefined && existing !== key) {
|
|
147
|
+
return { a: existing, b: key, suffix };
|
|
148
|
+
}
|
|
149
|
+
seen.set(suffix, key);
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** The `storage` block of `rebase.json`, structurally. */
|
|
155
|
+
export type DeclaredStorageSources = Record<string, {
|
|
156
|
+
engine: string;
|
|
157
|
+
transport?: StorageSourceTransport;
|
|
158
|
+
label?: string;
|
|
159
|
+
}>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Merge the two places a project may declare storage sources into one list.
|
|
163
|
+
*
|
|
164
|
+
* `rebase.json` is authoritative for every field it states. Config code may add
|
|
165
|
+
* sources it does not mention and fill in fields it left out, but may not
|
|
166
|
+
* contradict it: the manifest is what a host reads to decide which buckets need
|
|
167
|
+
* configuring, and a runtime that quietly disagreed with it would put the
|
|
168
|
+
* console back to describing a topology the tenant does not have — the exact
|
|
169
|
+
* failure this whole mechanism exists to end.
|
|
170
|
+
*
|
|
171
|
+
* Note what is *not* here: no default source is invented when both inputs are
|
|
172
|
+
* empty. That decision belongs to the resolver, which knows whether declaring
|
|
173
|
+
* nothing means "one plain bucket" (it does) or "no storage at all".
|
|
174
|
+
*
|
|
175
|
+
* @group Models
|
|
176
|
+
*/
|
|
177
|
+
export function normalizeStorageSources(
|
|
178
|
+
declared: DeclaredStorageSources | StorageSourceDefinition[] | undefined,
|
|
179
|
+
exported: StorageSourceDefinition[] | undefined
|
|
180
|
+
): StorageSourceDefinition[] {
|
|
181
|
+
const merged = new Map<string, StorageSourceDefinition>();
|
|
182
|
+
|
|
183
|
+
// Two shapes, one meaning. `rebase.json` states sources as a record keyed by
|
|
184
|
+
// source key, which is how JSON expresses a set of named things; the bundle
|
|
185
|
+
// manifest stores the already-resolved array. Accepting both is what lets the
|
|
186
|
+
// CLI and the runtime call this with what each of them happens to hold.
|
|
187
|
+
const declaredEntries: [string, { engine: string; transport?: StorageSourceTransport; label?: string }][] =
|
|
188
|
+
Array.isArray(declared)
|
|
189
|
+
? declared.filter(d => d?.key).map(d => [d.key, d])
|
|
190
|
+
: Object.entries(declared ?? {});
|
|
191
|
+
|
|
192
|
+
for (const [key, config] of declaredEntries) {
|
|
193
|
+
merged.set(key, {
|
|
194
|
+
key,
|
|
195
|
+
engine: config.engine,
|
|
196
|
+
transport: config.transport ?? "server",
|
|
197
|
+
...(config.label !== undefined ? { label: config.label } : {})
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
for (const definition of exported ?? []) {
|
|
202
|
+
if (!definition?.key) continue;
|
|
203
|
+
const existing = merged.get(definition.key);
|
|
204
|
+
if (!existing) {
|
|
205
|
+
merged.set(definition.key, {
|
|
206
|
+
key: definition.key,
|
|
207
|
+
engine: definition.engine,
|
|
208
|
+
transport: definition.transport ?? "server",
|
|
209
|
+
...(definition.label !== undefined ? { label: definition.label } : {})
|
|
210
|
+
});
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
// Fill gaps only. `rebase.json` stated these; code does not overrule it.
|
|
214
|
+
if (existing.label === undefined && definition.label !== undefined) {
|
|
215
|
+
existing.label = definition.label;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return Array.from(merged.values());
|
|
220
|
+
}
|
package/src/types/websockets.ts
CHANGED
|
@@ -115,45 +115,3 @@ export interface ChannelHistoryMessage extends WebSocketMessage {
|
|
|
115
115
|
*/
|
|
116
116
|
latestSeq?: number;
|
|
117
117
|
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Column metadata returned by table introspection.
|
|
121
|
-
*/
|
|
122
|
-
export interface TableColumnInfo {
|
|
123
|
-
column_name: string;
|
|
124
|
-
data_type: string;
|
|
125
|
-
udt_name: string;
|
|
126
|
-
is_nullable: string;
|
|
127
|
-
column_default: string | null;
|
|
128
|
-
character_maximum_length: number | null;
|
|
129
|
-
/** Enum values, populated for USER-DEFINED (enum) columns */
|
|
130
|
-
enum_values?: string[];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface TableForeignKeyInfo {
|
|
134
|
-
column_name: string;
|
|
135
|
-
foreign_table_name: string;
|
|
136
|
-
foreign_column_name: string;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export interface TableJunctionInfo {
|
|
140
|
-
junction_table_name: string;
|
|
141
|
-
source_column_name: string;
|
|
142
|
-
target_table_name: string;
|
|
143
|
-
target_column_name: string;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export interface TablePolicyInfo {
|
|
147
|
-
policy_name: string;
|
|
148
|
-
roles: string[];
|
|
149
|
-
cmd: string;
|
|
150
|
-
qual?: string;
|
|
151
|
-
with_check?: string;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export interface TableMetadata {
|
|
155
|
-
columns: TableColumnInfo[];
|
|
156
|
-
foreignKeys: TableForeignKeyInfo[];
|
|
157
|
-
junctions: TableJunctionInfo[];
|
|
158
|
-
policies: TablePolicyInfo[];
|
|
159
|
-
}
|