@rebasepro/types 0.16.0 → 0.16.1-canary.g0d7af95
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/call_context.d.ts +5 -5
- package/dist/controllers/auth_state.d.ts +1 -1
- package/dist/controllers/client.d.ts +22 -9
- package/dist/controllers/collection_registry.d.ts +2 -2
- package/dist/controllers/data.d.ts +4 -12
- package/dist/controllers/data_driver.d.ts +7 -7
- package/dist/controllers/email.d.ts +54 -2
- package/dist/controllers/index.d.ts +8 -9
- package/dist/controllers/storage.d.ts +4 -4
- package/dist/index.d.ts +5 -5
- package/dist/index.es.js +433 -3
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +1 -1
- package/dist/types/auth_adapter.d.ts +18 -9
- package/dist/types/backend.d.ts +23 -10
- package/dist/types/collection_contract.d.ts +1 -1
- package/dist/types/collections.d.ts +34 -9
- package/dist/types/component_ref.d.ts +3 -2
- package/dist/types/cron.d.ts +1 -25
- package/dist/types/data_source.d.ts +1 -1
- package/dist/types/database_adapter.d.ts +5 -5
- package/dist/types/entities.d.ts +1 -1
- package/dist/types/entity_callbacks.d.ts +4 -4
- package/dist/types/index.d.ts +33 -29
- package/dist/types/indexes.d.ts +179 -0
- package/dist/types/project_manifest.d.ts +132 -27
- package/dist/types/properties.d.ts +58 -6
- package/dist/types/relations.d.ts +1 -1
- package/dist/types/resource_kinds.d.ts +189 -0
- package/dist/types/resources.d.ts +197 -0
- package/dist/types/schema_editing.d.ts +127 -0
- package/dist/types/schema_version.d.ts +1 -1
- package/dist/types/security_rules.d.ts +1 -1
- package/dist/types/storage_source.d.ts +27 -0
- package/dist/users/index.d.ts +1 -1
- package/package.json +2 -2
- package/src/controllers/client.ts +14 -1
- package/src/controllers/data.ts +0 -9
- package/src/controllers/email.ts +55 -2
- package/src/controllers/index.ts +0 -1
- package/src/controllers/storage.ts +4 -4
- package/src/types/admin_block.ts +1 -2
- package/src/types/auth_adapter.ts +18 -10
- package/src/types/backend.ts +18 -1
- package/src/types/collections.ts +28 -2
- package/src/types/component_ref.ts +3 -2
- package/src/types/cron.ts +0 -24
- package/src/types/index.ts +4 -0
- package/src/types/indexes.ts +180 -0
- package/src/types/project_manifest.ts +139 -26
- package/src/types/properties.ts +54 -0
- package/src/types/resource_kinds.ts +324 -0
- package/src/types/resources.ts +368 -0
- package/src/types/schema_editing.ts +154 -0
- package/src/types/storage_source.ts +28 -0
- package/dist/controllers/database_admin.d.ts +0 -11
- package/src/controllers/database_admin.ts +0 -22
package/src/types/collections.ts
CHANGED
|
@@ -3,11 +3,13 @@ import type { CollectionCallbacks } from "./entity_callbacks";
|
|
|
3
3
|
import type { EnumValues, Properties, PostgresProperties, FirebaseProperties, MongoProperties } from "./properties";
|
|
4
4
|
|
|
5
5
|
import type { User } from "../users";
|
|
6
|
+
import type { EmailSendResult } from "../controllers/email";
|
|
6
7
|
import type { Relation } from "./relations";
|
|
7
8
|
import type { SecurityRule } from "./security_rules";
|
|
8
9
|
import { getDataSourceCapabilities } from "./data_source";
|
|
9
10
|
import type { WhereFilterOp, FilterValues, FilterPreset } from "./filter-operators";
|
|
10
11
|
import type { SearchConfig } from "./search";
|
|
12
|
+
import type { CollectionIndex } from "./indexes";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Base interface containing all driver-agnostic collection properties.
|
|
@@ -322,6 +324,24 @@ export interface PostgresCollectionConfig<M extends Record<string, unknown> = Re
|
|
|
322
324
|
* @see SearchConfig
|
|
323
325
|
*/
|
|
324
326
|
search?: SearchConfig;
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Ordinary indexes on this collection's table.
|
|
330
|
+
*
|
|
331
|
+
* Collection-level, not per-property, because an index over two columns
|
|
332
|
+
* has no single property to hang on and a partial index has none at all —
|
|
333
|
+
* and because a second declaration site for the single-column case would
|
|
334
|
+
* put the same object in two places. An index's identity is a column list
|
|
335
|
+
* in an order; the single-column case is a degenerate one, not a special
|
|
336
|
+
* one.
|
|
337
|
+
*
|
|
338
|
+
* `VectorProperty.index` stays where it is: an ANN structure is a property
|
|
339
|
+
* of the column's type, not of a query.
|
|
340
|
+
*
|
|
341
|
+
* Postgres-only, like {@link SearchConfig}: refused on another engine
|
|
342
|
+
* rather than silently ignored.
|
|
343
|
+
*/
|
|
344
|
+
indexes?: readonly CollectionIndex<Extract<keyof M, string>>[];
|
|
325
345
|
}
|
|
326
346
|
|
|
327
347
|
/**
|
|
@@ -702,8 +722,14 @@ export interface AuthCollectionConfig {
|
|
|
702
722
|
export interface AuthCollectionContext {
|
|
703
723
|
/** Hash a password using the configured algorithm (scrypt by default). */
|
|
704
724
|
hashPassword: (password: string) => Promise<string>;
|
|
705
|
-
/**
|
|
706
|
-
|
|
725
|
+
/**
|
|
726
|
+
* Send an email. Only available when email service is configured.
|
|
727
|
+
*
|
|
728
|
+
* Resolves with what the provider reported — the assigned Message-ID, most
|
|
729
|
+
* usefully — so a hook that sends a message can store the id and later
|
|
730
|
+
* thread a reply back to it. Callers that do not care may ignore it.
|
|
731
|
+
*/
|
|
732
|
+
sendEmail?: (options: { to: string; subject: string; html: string; text?: string }) => Promise<EmailSendResult>;
|
|
707
733
|
/** Whether the email service is configured and available. */
|
|
708
734
|
emailConfigured: boolean;
|
|
709
735
|
/** The app name from email config (for templates). */
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* How a collection points at a UI component without the backend learning about React.
|
|
3
3
|
*
|
|
4
4
|
* This file is the hinge the BaaS/admin split turns on. `ComponentRef` is named
|
|
5
|
-
* by `
|
|
6
|
-
* must stay in the React-free core
|
|
5
|
+
* by a property's `admin` block (`admin.Field`, `admin.Preview`, `admin.Filter`)
|
|
6
|
+
* and imported by `properties.ts`, which must stay in the React-free core
|
|
7
|
+
* because every backend subsystem — validation,
|
|
7
8
|
* the drizzle schema generator, the OpenAPI generator, the SDK codegen — reads
|
|
8
9
|
* property definitions. If `ComponentRef` needed `React.ComponentType`, the whole
|
|
9
10
|
* property model would have to move to the admin layer with it.
|
package/src/types/cron.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { RebaseServerClient } from "../controllers/client";
|
|
2
|
-
import type { RebaseSdkData } from "../controllers/data";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Cron Job type definitions for Rebase.
|
|
@@ -126,29 +125,6 @@ export interface CronJobContext {
|
|
|
126
125
|
*/
|
|
127
126
|
rebase: RebaseServerClient;
|
|
128
127
|
|
|
129
|
-
/**
|
|
130
|
-
* The same object as {@link rebase}, under the name this context used
|
|
131
|
-
* before.
|
|
132
|
-
*
|
|
133
|
-
* @deprecated Use `rebase` instead. Two things made the old name a problem,
|
|
134
|
-
* and neither was cosmetic. It contradicted every other server surface,
|
|
135
|
-
* where the singleton is `rebase` — the previous docstring had to end with
|
|
136
|
-
* *"it is only named `client` here"*. And typing it as `RebaseClient`
|
|
137
|
-
* re-exposed `client.data`, the alias that {@link RebaseServerClient}
|
|
138
|
-
* deliberately `Omit`s so the RLS-bypassing plane has exactly one name and
|
|
139
|
-
* the privilege is visible at the call site. A reader who learned
|
|
140
|
-
* `client.data` here carried it to a collection callback, where
|
|
141
|
-
* `context.data` is the *user-scoped* plane — same spelling, opposite
|
|
142
|
-
* privilege.
|
|
143
|
-
*
|
|
144
|
-
* Still the full server client at runtime, and `data` still resolves, so
|
|
145
|
-
* existing cron files keep working and keep compiling. It will be removed
|
|
146
|
-
* in the next major.
|
|
147
|
-
*/
|
|
148
|
-
client: RebaseServerClient & {
|
|
149
|
-
/** @deprecated Use `rebase.dataAsAdmin` — the name states the privilege. */
|
|
150
|
-
data: RebaseSdkData;
|
|
151
|
-
};
|
|
152
128
|
}
|
|
153
129
|
|
|
154
130
|
// =============================================================================
|
package/src/types/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./properties";
|
|
|
6
6
|
export * from "./admin_block";
|
|
7
7
|
export * from "./collections";
|
|
8
8
|
export * from "./search";
|
|
9
|
+
export * from "./indexes";
|
|
9
10
|
export * from "./relations";
|
|
10
11
|
export * from "./policy";
|
|
11
12
|
export * from "./rls-functions";
|
|
@@ -14,8 +15,11 @@ export * from "./security_rules";
|
|
|
14
15
|
export * from "./entity_callbacks";
|
|
15
16
|
export * from "./websockets";
|
|
16
17
|
export * from "./backend";
|
|
18
|
+
export * from "./schema_editing";
|
|
17
19
|
export * from "./channel_bus";
|
|
18
20
|
export * from "./data_source";
|
|
21
|
+
export * from "./resources";
|
|
22
|
+
export * from "./resource_kinds";
|
|
19
23
|
export * from "./storage_source";
|
|
20
24
|
export * from "./cron";
|
|
21
25
|
export * from "./backup";
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordinary indexes, declared on a collection.
|
|
3
|
+
*
|
|
4
|
+
* Distinct from the two index-shaped things Rebase already builds. A `search`
|
|
5
|
+
* block builds a GIN index over a generated `tsvector`, and a `vector`
|
|
6
|
+
* property builds an ANN index over an embedding; both are structures the
|
|
7
|
+
* *feature* owns and neither is a query the developer wrote. This is the plain
|
|
8
|
+
* case — the btree behind a `where` clause — which had no declaration site at
|
|
9
|
+
* all, so the only way to have one was to write it by hand, where the next
|
|
10
|
+
* `rebase db push` planned it away.
|
|
11
|
+
*
|
|
12
|
+
* Every form here is core Postgres, deliberately. See {@link CollectionIndex}.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A key column of an index whose access method has no ordering.
|
|
17
|
+
*
|
|
18
|
+
* `gin` and `brin` reject `ASC`/`DESC`/`NULLS` outright — Postgres answers
|
|
19
|
+
* `access method "gin" does not support ASC/DESC options` — so those methods
|
|
20
|
+
* take this narrower shape and the combination is unrepresentable rather than
|
|
21
|
+
* refused at build time.
|
|
22
|
+
*/
|
|
23
|
+
export interface UnorderedIndexKey<Keys extends string = string> {
|
|
24
|
+
/**
|
|
25
|
+
* A property key on this collection — never a column name.
|
|
26
|
+
*
|
|
27
|
+
* Which column that resolves to depends on the property, and the two
|
|
28
|
+
* differ in exactly the case an index is most often wanted for: a
|
|
29
|
+
* `belongsTo` relation compiles to its resolved `localKey`
|
|
30
|
+
* (`primaryCategory` → `primary_category_id`), not to the snake-cased
|
|
31
|
+
* property key. Anything else resolves through `columnName`, or the
|
|
32
|
+
* snake-case default when it declares none.
|
|
33
|
+
*
|
|
34
|
+
* Writing the column name here would work for most properties and quietly
|
|
35
|
+
* index nothing for a foreign key, which is the one people reach for.
|
|
36
|
+
*/
|
|
37
|
+
prop: Keys | (string & {});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A key column of an index, when its order matters.
|
|
42
|
+
*
|
|
43
|
+
* `direction` and `nulls` earn their place only when a query's `ORDER BY`
|
|
44
|
+
* mixes directions. A lone `DESC` index is redundant with its `ASC` twin —
|
|
45
|
+
* Postgres scans a btree backwards just as fast — and declaring both is
|
|
46
|
+
* refused.
|
|
47
|
+
*
|
|
48
|
+
* Writing the Postgres default down explicitly is free: the derived name
|
|
49
|
+
* hashes the *effective* order, so adding `direction: "asc"` to a column that
|
|
50
|
+
* was already ascending is not a redefinition and rebuilds nothing.
|
|
51
|
+
*/
|
|
52
|
+
export interface IndexKey<Keys extends string = string> extends UnorderedIndexKey<Keys> {
|
|
53
|
+
direction?: "asc" | "desc";
|
|
54
|
+
/** Postgres's own default: `last` under `asc`, `first` under `desc`. */
|
|
55
|
+
nulls?: "first" | "last";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The rows a partial index covers.
|
|
60
|
+
*
|
|
61
|
+
* Structure rather than a SQL string, and this is the most load-bearing choice
|
|
62
|
+
* in the type. A string would be replayed verbatim by Atlas in a scratch
|
|
63
|
+
* database, would be the one place a caller reaches for an extension operator
|
|
64
|
+
* class or a subquery, could not be checked against the collection's
|
|
65
|
+
* properties, and could not be fingerprinted — its own text would have to go
|
|
66
|
+
* into the derived name, so reformatting it would rename a live index.
|
|
67
|
+
*
|
|
68
|
+
* Structure keeps every reference resolvable at build time, keeps literals
|
|
69
|
+
* going through the same quoting as the rest of the DDL, and keeps the name
|
|
70
|
+
* stable under any rendering change.
|
|
71
|
+
*
|
|
72
|
+
* There is no `or`. An OR predicate almost always means the index should not
|
|
73
|
+
* be partial at all; a caller who genuinely needs one declares two indexes.
|
|
74
|
+
*/
|
|
75
|
+
export type IndexPredicate<Keys extends string = string> =
|
|
76
|
+
| { prop: Keys | (string & {}); op: "="; value: string | number | boolean }
|
|
77
|
+
| { prop: Keys | (string & {}); op: "!=" | "<" | "<=" | ">" | ">="; value: string | number }
|
|
78
|
+
| { prop: Keys | (string & {}); op: "is null" | "is not null" }
|
|
79
|
+
/**
|
|
80
|
+
* A non-empty list, enforced in the type. An empty `IN` is a predicate
|
|
81
|
+
* matching nothing: it builds an index over zero rows and reports success,
|
|
82
|
+
* which is the silent-empty-condition shape this codebase has been bitten
|
|
83
|
+
* by before.
|
|
84
|
+
*/
|
|
85
|
+
| { prop: Keys | (string & {}); op: "in"; value: readonly [string | number, ...(string | number)[]] }
|
|
86
|
+
| { and: readonly [IndexPredicate<Keys>, ...IndexPredicate<Keys>[]] };
|
|
87
|
+
|
|
88
|
+
interface BaseCollectionIndex<Keys extends string = string> {
|
|
89
|
+
/**
|
|
90
|
+
* The key columns, in order. This *is* the index's identity.
|
|
91
|
+
*
|
|
92
|
+
* Postgres can only use a leading subset, so `["ownerId", "createdAt"]`
|
|
93
|
+
* serves a query filtering on `ownerId`, and one filtering on both, and
|
|
94
|
+
* never one filtering on `createdAt` alone.
|
|
95
|
+
*
|
|
96
|
+
* Capped at five keys. Postgres allows thirty-two; past four the trailing
|
|
97
|
+
* columns are dead weight on every write, and the declaration is usually
|
|
98
|
+
* someone hoping a query gets faster by accretion. Payload columns that
|
|
99
|
+
* are not searched belong in `include`, which does not count against this.
|
|
100
|
+
*/
|
|
101
|
+
on: readonly [Keys | IndexKey<Keys>, ...(Keys | IndexKey<Keys>)[]];
|
|
102
|
+
|
|
103
|
+
where?: IndexPredicate<Keys>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Why this index exists, in one line. Required, and the only required
|
|
107
|
+
* field carrying no SQL.
|
|
108
|
+
*
|
|
109
|
+
* An index is the only thing a Rebase config can declare that costs money
|
|
110
|
+
* forever and whose benefit is invisible from the config. `rebase doctor`
|
|
111
|
+
* prints this beside "0 scans in 34 days, 412 MB", which is the one moment
|
|
112
|
+
* anyone is in a position to decide whether to delete it. Without it
|
|
113
|
+
* nobody can decide, so nobody does, and the table accretes indexes for
|
|
114
|
+
* the life of the product.
|
|
115
|
+
*/
|
|
116
|
+
reason: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The default. Answers equality, range, `ORDER BY`, and uniqueness.
|
|
121
|
+
*/
|
|
122
|
+
export interface BtreeIndex<Keys extends string = string> extends BaseCollectionIndex<Keys> {
|
|
123
|
+
using?: "btree";
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* A composite uniqueness guarantee.
|
|
127
|
+
*
|
|
128
|
+
* Single-column uniqueness is `validation.unique` on the property, and
|
|
129
|
+
* declaring it here is refused rather than accepted as a synonym.
|
|
130
|
+
* `validation.unique` compiles to an inline `UNIQUE` whose backing index
|
|
131
|
+
* Postgres — not Rebase — names `<table>_<column>_key`. That name is in
|
|
132
|
+
* every deployed database, appears in no contract file, and no release can
|
|
133
|
+
* reach in and rename it.
|
|
134
|
+
*/
|
|
135
|
+
unique?: boolean;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Payload columns carried in the leaf pages, for index-only scans. Not
|
|
139
|
+
* searchable and not ordered — they save a heap fetch at the cost of a
|
|
140
|
+
* fatter index. May not overlap `on`.
|
|
141
|
+
*/
|
|
142
|
+
include?: readonly (Keys | (string & {}))[];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Containment over an `array` property or a JSONB `map`, using core operator
|
|
147
|
+
* classes only. Trigram and full-text search are `search:`, not this.
|
|
148
|
+
*/
|
|
149
|
+
export interface GinIndex<Keys extends string = string> extends BaseCollectionIndex<Keys> {
|
|
150
|
+
using: "gin";
|
|
151
|
+
on: readonly [Keys | UnorderedIndexKey<Keys>, ...(Keys | UnorderedIndexKey<Keys>)[]];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* A naturally-ordered column on an append-only table — tiny, and useless the
|
|
156
|
+
* moment rows arrive out of order.
|
|
157
|
+
*/
|
|
158
|
+
export interface BrinIndex<Keys extends string = string> extends BaseCollectionIndex<Keys> {
|
|
159
|
+
using: "brin";
|
|
160
|
+
on: readonly [Keys | UnorderedIndexKey<Keys>, ...(Keys | UnorderedIndexKey<Keys>)[]];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* An index on a collection's table.
|
|
165
|
+
*
|
|
166
|
+
* No `gist` and no `hash`: every interesting gist operator class ships in an
|
|
167
|
+
* extension, and hash indexes cannot be unique, composite, or ordered.
|
|
168
|
+
*
|
|
169
|
+
* The restriction to core Postgres is not conservatism, it is what keeps the
|
|
170
|
+
* whole model on the Atlas path. `rebase db push` materialises the desired
|
|
171
|
+
* state in a bare scratch database to plan against, `--exclude` does not
|
|
172
|
+
* suppress that replay, and `CREATE EXTENSION` cannot be put in the file — so
|
|
173
|
+
* an index needing `gin_trgm_ops` or `vector_cosine_ops` is refused at build
|
|
174
|
+
* time rather than emitted to fail later against a database the author has
|
|
175
|
+
* never heard of. Trigram search is `search:`; ANN is a `vector` property.
|
|
176
|
+
*/
|
|
177
|
+
export type CollectionIndex<Keys extends string = string> =
|
|
178
|
+
| BtreeIndex<Keys>
|
|
179
|
+
| GinIndex<Keys>
|
|
180
|
+
| BrinIndex<Keys>;
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import type { StorageSourceDefinition } from "./storage_source";
|
|
29
|
+
import type { ResourceGraph } from "./resources";
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Which kind of thing an app is.
|
|
@@ -61,7 +62,7 @@ export interface RebaseBackendAppConfig {
|
|
|
61
62
|
*
|
|
62
63
|
* Independent of *where* it runs. Both run on Rebase Cloud and both
|
|
63
64
|
* self-host — the destination lives in `.rebase/cloud.json`, not here. See
|
|
64
|
-
* `docker/docker-compose.selfhost.yml`, which boots a managed bundle on a
|
|
65
|
+
* `infra/docker/docker-compose.selfhost.yml`, which boots a managed bundle on a
|
|
65
66
|
* developer's own Docker host.
|
|
66
67
|
*
|
|
67
68
|
* This is authored rather than inferred on purpose. It is the single most
|
|
@@ -132,6 +133,38 @@ export interface RebaseStaticAppConfig {
|
|
|
132
133
|
|
|
133
134
|
export type RebaseAppConfig = RebaseBackendAppConfig | RebaseStaticAppConfig;
|
|
134
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Path prefixes the backend owns, which no static app may claim.
|
|
138
|
+
*
|
|
139
|
+
* One process — and, on the platform, one hostname — serves both the API and
|
|
140
|
+
* however many static apps a project has. Mounting is longest-path-first, so an
|
|
141
|
+
* app declaring `/api` would win against the API itself and every request to it
|
|
142
|
+
* would be answered with that app's `index.html`: a 200 carrying HTML where the
|
|
143
|
+
* caller expected JSON, from a project that looks deployed and healthy.
|
|
144
|
+
*
|
|
145
|
+
* Declared here rather than in either enforcer because both must agree. The CLI
|
|
146
|
+
* checks it so a developer finds out while editing `rebase.json`; the control
|
|
147
|
+
* plane checks it again at deploy intake, because the front door's correctness
|
|
148
|
+
* cannot rest on a check that ran in somebody else's CLI — and a repository can
|
|
149
|
+
* be deployed by a CLI older than this rule.
|
|
150
|
+
*/
|
|
151
|
+
export const RESERVED_BACKEND_PREFIXES = ["/api", "/health", "/healthz", "/livez", "/readyz", "/metrics"] as const;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Whether `path` collides with a prefix the backend owns.
|
|
155
|
+
*
|
|
156
|
+
* Compares at segment boundaries, so `/api` and `/api/v2` collide while
|
|
157
|
+
* `/apidocs` does not — the same rule the router matches with, because a check
|
|
158
|
+
* that is stricter than the router rejects paths that would have worked, and one
|
|
159
|
+
* that is looser admits paths that will not.
|
|
160
|
+
*/
|
|
161
|
+
export function reservedPrefixFor(path: string): string | undefined {
|
|
162
|
+
const normalized = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
163
|
+
return RESERVED_BACKEND_PREFIXES.find(
|
|
164
|
+
reserved => normalized === reserved || normalized.startsWith(`${reserved}/`)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
135
168
|
/**
|
|
136
169
|
* One declared storage source, as authored in `rebase.json`.
|
|
137
170
|
*
|
|
@@ -179,27 +212,20 @@ export interface RebaseProjectManifest {
|
|
|
179
212
|
*/
|
|
180
213
|
apps: Record<string, RebaseAppConfig>;
|
|
181
214
|
/**
|
|
182
|
-
*
|
|
215
|
+
* Buckets are NOT declared here any more.
|
|
183
216
|
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* default source takes no suffix, so a single-bucket project configured with
|
|
189
|
-
* plain `S3_BUCKET` keeps working having declared nothing at all.
|
|
217
|
+
* They were, and the runtime merged this block with the declarations in
|
|
218
|
+
* config code — a bucket named in both had one engine kept and the other
|
|
219
|
+
* silently discarded. Two homes for one concept, with a merge to decide
|
|
220
|
+
* between them, is the shape this whole model replaced.
|
|
190
221
|
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
* project invisible.
|
|
198
|
-
*
|
|
199
|
-
* Omitted entirely means one default source, which is the overwhelmingly
|
|
200
|
-
* common project and must not be required to say so.
|
|
222
|
+
* `bucket("media", { engine: "s3" })` in the project's config declares one
|
|
223
|
+
* now, and `rebase resources --write` generates `rebase.resources.json`,
|
|
224
|
+
* which is what a host reads before a build. A `storage` block left in this
|
|
225
|
+
* file is refused by the validator, by name, with the replacement in the
|
|
226
|
+
* message — not ignored, because a key that still parses and does nothing
|
|
227
|
+
* is the failure this removed.
|
|
201
228
|
*/
|
|
202
|
-
storage?: Record<string, RebaseStorageSourceConfig>;
|
|
203
229
|
/**
|
|
204
230
|
* Repository-wide opt-out from anonymous CLI usage sharing.
|
|
205
231
|
*
|
|
@@ -279,6 +305,31 @@ export const BUNDLE_FORMAT_VERSION = 2;
|
|
|
279
305
|
* any number of minors and patches while this stays put. It changes only when
|
|
280
306
|
* the bundle/runtime contract breaks compatibility, and a project's
|
|
281
307
|
* `manifest.runtime` range is matched against *this*.
|
|
308
|
+
*
|
|
309
|
+
* ## v2 — resources are declared, not configured
|
|
310
|
+
*
|
|
311
|
+
* `RebaseBackendConfig.dataSources` and `.storageSources` are gone. A project
|
|
312
|
+
* declares its databases and buckets with `database()` / `bucket()` in its
|
|
313
|
+
* config, and the runtime reads those declarations.
|
|
314
|
+
*
|
|
315
|
+
* This had to be a major, and the reason is the managed tier: it moves projects
|
|
316
|
+
* onto new images WITHOUT rebuilding them. A bundle built against v1 exports
|
|
317
|
+
* those keys, and a v2 runtime refuses them at boot — so without this bump, one
|
|
318
|
+
* image rollout would crash-loop every tenant that had ever declared a second
|
|
319
|
+
* database or bucket, in a wave, with the cause in a container log nobody is
|
|
320
|
+
* watching.
|
|
321
|
+
*
|
|
322
|
+
* With the bump, a v1 bundle on a v2 runtime is refused by
|
|
323
|
+
* `assertBundleCompatibility` with the remedy in the message, and the platform
|
|
324
|
+
* keeps it on a v1 image until it is rebuilt. That is the whole purpose of this
|
|
325
|
+
* number.
|
|
326
|
+
*
|
|
327
|
+
* **Release order matters and is not optional.** The control plane is the side
|
|
328
|
+
* that rejects, so it ships FIRST: raise `SUPPORTED_RUNTIME_CONTRACT` in the
|
|
329
|
+
* saas repo (it rejects only `contract >` its own, so it then accepts both),
|
|
330
|
+
* deploy that, and only then release a runtime implementing v2. Shipping the
|
|
331
|
+
* runtime first turns every deploy into a rejected intake blaming the tenant's
|
|
332
|
+
* bundle.
|
|
282
333
|
*/
|
|
283
334
|
export const RUNTIME_CONTRACT_VERSION = 1;
|
|
284
335
|
|
|
@@ -333,6 +384,36 @@ export interface NativeDependency {
|
|
|
333
384
|
* `manifest.json` — generated, and the document the runtime and control plane
|
|
334
385
|
* both validate against.
|
|
335
386
|
*/
|
|
387
|
+
/**
|
|
388
|
+
* One custom function, as recorded in a built bundle.
|
|
389
|
+
*
|
|
390
|
+
* @see RebaseBundleManifest.functions
|
|
391
|
+
*/
|
|
392
|
+
export interface RebaseBundleFunction {
|
|
393
|
+
/**
|
|
394
|
+
* The filename without its extension — which is also the URL segment it
|
|
395
|
+
* mounts at (`/api/functions/<name>`), the API-key permission that grants
|
|
396
|
+
* it, and the name `REBASE_FUNCTIONS_ONLY` selects by. One identity, used
|
|
397
|
+
* everywhere.
|
|
398
|
+
*/
|
|
399
|
+
name: string;
|
|
400
|
+
/** Path inside the bundle, so a host can point at the file. */
|
|
401
|
+
file: string;
|
|
402
|
+
/**
|
|
403
|
+
* `false` when the function's own source imports a Node built-in or a
|
|
404
|
+
* package that needs one.
|
|
405
|
+
*
|
|
406
|
+
* Descriptive, never a gate: nothing refuses to build or deploy on this. It
|
|
407
|
+
* says where this function *could* run, not where it should.
|
|
408
|
+
*/
|
|
409
|
+
portable: boolean;
|
|
410
|
+
/**
|
|
411
|
+
* Why it is not portable — one short phrase per reason, deduplicated.
|
|
412
|
+
* Absent when it is.
|
|
413
|
+
*/
|
|
414
|
+
requires?: string[];
|
|
415
|
+
}
|
|
416
|
+
|
|
336
417
|
export interface RebaseBundleManifest {
|
|
337
418
|
/** @see BUNDLE_FORMAT_VERSION */
|
|
338
419
|
bundleFormat: number;
|
|
@@ -373,6 +454,28 @@ export interface RebaseBundleManifest {
|
|
|
373
454
|
entry: RebaseBundleEntrypoints;
|
|
374
455
|
/** Collection slugs contained in the bundle, for quick inspection. */
|
|
375
456
|
collections?: string[];
|
|
457
|
+
/**
|
|
458
|
+
* Every custom function in the bundle, named and classified.
|
|
459
|
+
*
|
|
460
|
+
* Two things are recorded per function, and both are answers a host would
|
|
461
|
+
* otherwise have to get by importing user code:
|
|
462
|
+
*
|
|
463
|
+
* - **What it is called.** That name is the function's identity everywhere —
|
|
464
|
+
* the URL segment it mounts at, the `functions/<name>` API-key
|
|
465
|
+
* permission, the value `REBASE_FUNCTIONS_ONLY` selects by. A host that
|
|
466
|
+
* wants to give one slow function its own replica count currently has to
|
|
467
|
+
* boot the bundle to discover what is in it.
|
|
468
|
+
* - **Whether it needs Node.** Purely descriptive: a function that opens a
|
|
469
|
+
* file or runs raw SQL is a fine function, and every deployment today is
|
|
470
|
+
* a Node process. It is recorded because the question "which of these
|
|
471
|
+
* could run somewhere else" has to be answerable from the artifact, and
|
|
472
|
+
* because answering it per-file after the fact — across a codebase
|
|
473
|
+
* already written — is the expensive version of the same question.
|
|
474
|
+
*
|
|
475
|
+
* Absent on a bundle built before this field existed, which is why every
|
|
476
|
+
* consumer must treat it as optional rather than as an empty list.
|
|
477
|
+
*/
|
|
478
|
+
functions?: RebaseBundleFunction[];
|
|
376
479
|
hooks: {
|
|
377
480
|
/**
|
|
378
481
|
* Whether the dependency closure contains native code.
|
|
@@ -401,17 +504,27 @@ export interface RebaseBundleManifest {
|
|
|
401
504
|
/** Whether the config package exports a `storageAuthorize` hook. */
|
|
402
505
|
authorize: boolean;
|
|
403
506
|
/**
|
|
404
|
-
*
|
|
405
|
-
* `rebase.json`'s `storage` block merged with any `storageSources` the
|
|
406
|
-
* config package exports.
|
|
507
|
+
* Buckets, on bundles built before {@link RebaseBundleManifest.resources}.
|
|
407
508
|
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
* built before this field existed, which means one default source.
|
|
509
|
+
* No longer written. A host reads `resources`, which carries every kind
|
|
510
|
+
* in one list; this stays declared so a control plane can keep reading
|
|
511
|
+
* the bundles a project shipped before it was rebuilt.
|
|
412
512
|
*/
|
|
413
513
|
sources?: StorageSourceDefinition[];
|
|
414
514
|
};
|
|
515
|
+
/**
|
|
516
|
+
* Everything the project declares it needs — databases, buckets, topics,
|
|
517
|
+
* and whatever kind is registered next.
|
|
518
|
+
*
|
|
519
|
+
* Recorded so a host can tell, from the artifact alone and before starting
|
|
520
|
+
* anything, what a deploy will need provisioned. That question used to be
|
|
521
|
+
* answerable for buckets and for nothing else, because buckets were the
|
|
522
|
+
* only kind written into an artifact — which is how a project's databases
|
|
523
|
+
* became invisible to the platform that runs them.
|
|
524
|
+
*
|
|
525
|
+
* Absent on bundles built before this field existed.
|
|
526
|
+
*/
|
|
527
|
+
resources?: ResourceGraph;
|
|
415
528
|
deps: {
|
|
416
529
|
/** Runtime dependencies of user code, as declared. */
|
|
417
530
|
declared: Record<string, string>;
|
package/src/types/properties.ts
CHANGED
|
@@ -400,6 +400,48 @@ export interface BooleanProperty extends BaseProperty {
|
|
|
400
400
|
validation?: PropertyValidationSchema;
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Which pgvector distance a query measures with, and therefore which operator
|
|
405
|
+
* class an index has to be built for. The names match the `distance` option on
|
|
406
|
+
* `vectorSearch`, because an index built for one operator is not used by a
|
|
407
|
+
* query that asks for another.
|
|
408
|
+
*
|
|
409
|
+
* @group Entity properties
|
|
410
|
+
*/
|
|
411
|
+
export type VectorDistance = "cosine" | "l2" | "inner_product";
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* How the ANN index over a vector column is built.
|
|
415
|
+
*
|
|
416
|
+
* Without an index, `vectorSearch` is an exact scan: correct at any size,
|
|
417
|
+
* and linear in the number of rows. With one, it is approximate and fast.
|
|
418
|
+
* That trade is why this is configurable rather than implied.
|
|
419
|
+
*
|
|
420
|
+
* @group Entity properties
|
|
421
|
+
*/
|
|
422
|
+
export interface VectorIndexConfig {
|
|
423
|
+
/**
|
|
424
|
+
* `hnsw` (the default) builds a navigable-graph index: slower to build,
|
|
425
|
+
* better recall, and it needs no training data, so it works on an empty
|
|
426
|
+
* table. `ivfflat` is cheaper to build but partitions by centroid, so an
|
|
427
|
+
* index built on an empty or tiny table has useless partitions — build it
|
|
428
|
+
* after the data is loaded, and set {@link lists}.
|
|
429
|
+
*/
|
|
430
|
+
method?: "hnsw" | "ivfflat";
|
|
431
|
+
/**
|
|
432
|
+
* Which distance operators to index, defaulting to `cosine` — the default
|
|
433
|
+
* `vectorSearch` measures with. Name several to index several; each one is
|
|
434
|
+
* a separate index with its own build cost and its own storage.
|
|
435
|
+
*/
|
|
436
|
+
distance?: VectorDistance | VectorDistance[];
|
|
437
|
+
/** HNSW: connections per node. Postgres defaults to 16. */
|
|
438
|
+
m?: number;
|
|
439
|
+
/** HNSW: candidate-list size while building. Postgres defaults to 64. */
|
|
440
|
+
efConstruction?: number;
|
|
441
|
+
/** IVFFlat: number of partitions. Postgres defaults to 100. */
|
|
442
|
+
lists?: number;
|
|
443
|
+
}
|
|
444
|
+
|
|
403
445
|
export interface VectorProperty extends BaseProperty {
|
|
404
446
|
type: "vector";
|
|
405
447
|
/**
|
|
@@ -407,6 +449,18 @@ export interface VectorProperty extends BaseProperty {
|
|
|
407
449
|
*/
|
|
408
450
|
defaultValue?: Vector;
|
|
409
451
|
dimensions: number;
|
|
452
|
+
/**
|
|
453
|
+
* ANN index configuration for this column.
|
|
454
|
+
*
|
|
455
|
+
* Omitted, a single HNSW index for cosine distance is created — which is
|
|
456
|
+
* what the default `vectorSearch` uses. `false` creates none, leaving
|
|
457
|
+
* `vectorSearch` an exact scan.
|
|
458
|
+
*
|
|
459
|
+
* Indexes are only created when {@link dimensions} is at most 2000:
|
|
460
|
+
* pgvector cannot index a wider `vector` column, so a 3072-dimension
|
|
461
|
+
* embedding is left unindexed rather than failing the boot.
|
|
462
|
+
*/
|
|
463
|
+
index?: VectorIndexConfig | false;
|
|
410
464
|
validation?: PropertyValidationSchema;
|
|
411
465
|
}
|
|
412
466
|
|