@rebasepro/types 0.16.0 → 0.16.1-canary.g2d1aec8

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.
@@ -333,6 +333,36 @@ export interface NativeDependency {
333
333
  * `manifest.json` — generated, and the document the runtime and control plane
334
334
  * both validate against.
335
335
  */
336
+ /**
337
+ * One custom function, as recorded in a built bundle.
338
+ *
339
+ * @see RebaseBundleManifest.functions
340
+ */
341
+ export interface RebaseBundleFunction {
342
+ /**
343
+ * The filename without its extension — which is also the URL segment it
344
+ * mounts at (`/api/functions/<name>`), the API-key permission that grants
345
+ * it, and the name `REBASE_FUNCTIONS_ONLY` selects by. One identity, used
346
+ * everywhere.
347
+ */
348
+ name: string;
349
+ /** Path inside the bundle, so a host can point at the file. */
350
+ file: string;
351
+ /**
352
+ * `false` when the function's own source imports a Node built-in or a
353
+ * package that needs one.
354
+ *
355
+ * Descriptive, never a gate: nothing refuses to build or deploy on this. It
356
+ * says where this function *could* run, not where it should.
357
+ */
358
+ portable: boolean;
359
+ /**
360
+ * Why it is not portable — one short phrase per reason, deduplicated.
361
+ * Absent when it is.
362
+ */
363
+ requires?: string[];
364
+ }
365
+
336
366
  export interface RebaseBundleManifest {
337
367
  /** @see BUNDLE_FORMAT_VERSION */
338
368
  bundleFormat: number;
@@ -373,6 +403,28 @@ export interface RebaseBundleManifest {
373
403
  entry: RebaseBundleEntrypoints;
374
404
  /** Collection slugs contained in the bundle, for quick inspection. */
375
405
  collections?: string[];
406
+ /**
407
+ * Every custom function in the bundle, named and classified.
408
+ *
409
+ * Two things are recorded per function, and both are answers a host would
410
+ * otherwise have to get by importing user code:
411
+ *
412
+ * - **What it is called.** That name is the function's identity everywhere —
413
+ * the URL segment it mounts at, the `functions/<name>` API-key
414
+ * permission, the value `REBASE_FUNCTIONS_ONLY` selects by. A host that
415
+ * wants to give one slow function its own replica count currently has to
416
+ * boot the bundle to discover what is in it.
417
+ * - **Whether it needs Node.** Purely descriptive: a function that opens a
418
+ * file or runs raw SQL is a fine function, and every deployment today is
419
+ * a Node process. It is recorded because the question "which of these
420
+ * could run somewhere else" has to be answerable from the artifact, and
421
+ * because answering it per-file after the fact — across a codebase
422
+ * already written — is the expensive version of the same question.
423
+ *
424
+ * Absent on a bundle built before this field existed, which is why every
425
+ * consumer must treat it as optional rather than as an empty list.
426
+ */
427
+ functions?: RebaseBundleFunction[];
376
428
  hooks: {
377
429
  /**
378
430
  * Whether the dependency closure contains native code.
@@ -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
 
@@ -0,0 +1,154 @@
1
+ /**
2
+ * The vocabulary a live schema change is described in.
3
+ *
4
+ * Declared here, and nowhere else, because two packages that must not import
5
+ * each other both need it: `@rebasepro/server-postgres` decides what a change
6
+ * means and renders the files it needs, while `@rebasepro/server` commits those
7
+ * files and serves the routes. Neither can reach the other — the server is
8
+ * engine-agnostic by design — so the shared kernel holds the shapes and the
9
+ * driver is detected structurally through {@link SchemaEditingAdmin}.
10
+ *
11
+ * Nothing here executes anything. These are the nouns.
12
+ */
13
+
14
+ /**
15
+ * What a change will do to a live database.
16
+ *
17
+ * - `safe` — the boot-time ensure path expresses it, and the result matches the
18
+ * configuration.
19
+ * - `diverges` — the ensure path applies *something*, but the database will not
20
+ * match what the configuration declares, and nothing reports it. This is the
21
+ * category worth having: adding a required property to a populated table
22
+ * yields a nullable column, and adding a value to an existing enum yields
23
+ * nothing at all. Both read as success.
24
+ * - `needs-migration` — the ensure path cannot express it. Dropping anything,
25
+ * changing a type, moving a primary key.
26
+ */
27
+ export type SchemaChangeVerdict = "safe" | "diverges" | "needs-migration";
28
+
29
+ export type SchemaChangeKind =
30
+ | "add-collection"
31
+ | "remove-collection"
32
+ | "add-property"
33
+ | "remove-property"
34
+ | "change-property-type"
35
+ | "rename-column"
36
+ | "add-enum-value"
37
+ | "remove-enum-value"
38
+ | "change-required"
39
+ | "change-primary-key";
40
+
41
+ export interface SchemaChange {
42
+ kind: SchemaChangeKind;
43
+ verdict: SchemaChangeVerdict;
44
+ /** Collection slug. */
45
+ collection: string;
46
+ /** Property name, where the change is to one. */
47
+ property?: string;
48
+ /** One line, specific: what changed and what it will do. */
49
+ detail: string;
50
+ /** What to do instead, when the verdict is not `safe`. */
51
+ remedy?: string;
52
+ }
53
+
54
+ export interface ClassifiedSchemaChanges {
55
+ changes: SchemaChange[];
56
+ /** The worst verdict present, or `safe` for an empty diff. */
57
+ verdict: SchemaChangeVerdict;
58
+ /** True only when every change is `safe` — the one case an editor may apply. */
59
+ applicable: boolean;
60
+ }
61
+
62
+ /**
63
+ * Where a project's generated schema artifacts live, relative to the **project**
64
+ * root — which is the repository root only when the project is the whole
65
+ * repository.
66
+ *
67
+ * Here rather than in the Postgres package because it is a contract, not an
68
+ * engine detail: `@rebasepro/server` has to derive these for a project in a
69
+ * subdirectory, and it cannot import a driver to do it.
70
+ */
71
+ export interface SchemaCommitPaths {
72
+ /** Drizzle schema, imported by the backend. */
73
+ schemaFile: string;
74
+ /** Declarative DDL, what `db push` applies and Atlas diffs against. */
75
+ ddlFile: string;
76
+ policiesFile: string;
77
+ searchFile: string;
78
+ }
79
+
80
+ export const DEFAULT_COMMIT_PATHS: SchemaCommitPaths = {
81
+ schemaFile: "backend/src/schema.generated.ts",
82
+ ddlFile: "drizzle/schema.sql",
83
+ policiesFile: "drizzle/policies.sql",
84
+ searchFile: "drizzle/search.sql"
85
+ };
86
+
87
+ /** One file the commit writes, as content rather than as a path on a disk. */
88
+ export interface SchemaChangeFile {
89
+ path: string;
90
+ contents: string;
91
+ }
92
+
93
+ /**
94
+ * Everything a change needs written and run.
95
+ *
96
+ * Computed without touching a disk or a network. The database is *read* — what
97
+ * a change means depends on what is already there, and a plan that guessed
98
+ * would be guessing about whether the statements it returns will be accepted.
99
+ */
100
+ export interface SchemaChangePlan {
101
+ /** Every file the commit writes — collection source and generated artifacts. */
102
+ files: SchemaChangeFile[];
103
+ /** The additive DDL this change adds, in dependency order. */
104
+ statements: string[];
105
+ classified: ClassifiedSchemaChanges;
106
+ /** A commit message describing the change rather than announcing one. */
107
+ message: string;
108
+ /**
109
+ * Constraints the configuration asks for that these statements do not
110
+ * carry, and why.
111
+ *
112
+ * Almost always empty. When it is not, it is the part the person confirming
113
+ * needs to read: the change will apply, and the database will still not
114
+ * enforce something the configuration says — a required property over a
115
+ * table that already holds rows with no value for it. Optional so a plan
116
+ * from an engine that does not distinguish these cases stays valid.
117
+ */
118
+ withheldConstraints?: WithheldSchemaConstraint[];
119
+ }
120
+
121
+ /** A constraint a plan asks for and does not apply. */
122
+ export interface WithheldSchemaConstraint {
123
+ /** `schema.table.column`. */
124
+ target: string;
125
+ kind: "not-null";
126
+ /** What is in the way, naming the obstacle rather than the rule. */
127
+ reason: string;
128
+ /** What would make it applicable. */
129
+ remedy: string;
130
+ }
131
+
132
+ /**
133
+ * An admin that can plan a schema change.
134
+ *
135
+ * Planning only. Applying is `executeSql`, which every SQL admin already has,
136
+ * and committing belongs to whatever holds the repository — keeping those three
137
+ * apart is what lets the same plan be committed locally on a developer's machine
138
+ * and through a GitHub App from a cloud tenant.
139
+ *
140
+ * @group Admin
141
+ */
142
+ export interface SchemaEditingAdmin {
143
+ /**
144
+ * Decide what the change means and render everything it needs.
145
+ *
146
+ * Rejects when the change is not applicable, carrying the classification so
147
+ * a caller can say which change was the problem.
148
+ */
149
+ planSchemaChange(
150
+ before: unknown[],
151
+ after: unknown[],
152
+ options?: { paths?: Partial<SchemaCommitPaths> }
153
+ ): Promise<SchemaChangePlan>;
154
+ }