@rebasepro/types 0.17.3 → 0.18.1
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/README.md +4 -0
- package/dist/call_context.d.ts +20 -0
- package/dist/controllers/client.d.ts +36 -4
- package/dist/controllers/data.d.ts +120 -10
- package/dist/errors.d.ts +83 -4
- package/dist/index.es.js +522 -160
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +2 -2
- package/dist/types/auth_adapter.d.ts +41 -6
- package/dist/types/backend.d.ts +48 -0
- package/dist/types/collections.d.ts +25 -1
- package/dist/types/cron.d.ts +34 -0
- package/dist/types/database_adapter.d.ts +39 -0
- package/dist/types/entity_callbacks.d.ts +14 -1
- package/dist/types/filter-operators.d.ts +24 -1
- package/dist/types/policy.d.ts +29 -1
- package/dist/types/properties.d.ts +216 -3
- package/dist/types/relations.d.ts +65 -7
- package/dist/types/resource_kinds.d.ts +173 -17
- package/dist/types/resources.d.ts +108 -7
- package/dist/types/rls-functions.d.ts +11 -0
- package/dist/types/storage_source.d.ts +12 -23
- package/package.json +24 -23
- package/src/call_context.ts +0 -120
- package/src/controllers/auth_state.ts +0 -24
- package/src/controllers/client.ts +0 -494
- package/src/controllers/collection_registry.ts +0 -62
- package/src/controllers/data.ts +0 -1012
- package/src/controllers/data_driver.ts +0 -576
- package/src/controllers/effective_role.ts +0 -4
- package/src/controllers/email.ts +0 -91
- package/src/controllers/index.ts +0 -11
- package/src/controllers/storage.ts +0 -252
- package/src/errors.ts +0 -119
- package/src/index.ts +0 -5
- package/src/types/admin_block.ts +0 -209
- package/src/types/api_keys.ts +0 -108
- package/src/types/auth_adapter.ts +0 -580
- package/src/types/backend.ts +0 -987
- package/src/types/backup.ts +0 -26
- package/src/types/channel_bus.ts +0 -202
- package/src/types/chips.ts +0 -34
- package/src/types/collection_contract.ts +0 -278
- package/src/types/collections.ts +0 -763
- package/src/types/component_ref.ts +0 -92
- package/src/types/cron.ts +0 -213
- package/src/types/data_source.ts +0 -357
- package/src/types/database_adapter.ts +0 -267
- package/src/types/entities.ts +0 -226
- package/src/types/entity_callbacks.ts +0 -229
- package/src/types/filter-operators.ts +0 -444
- package/src/types/history.ts +0 -66
- package/src/types/index.ts +0 -36
- package/src/types/indexes.ts +0 -180
- package/src/types/policy.ts +0 -328
- package/src/types/postgres_introspection.ts +0 -101
- package/src/types/project_manifest.ts +0 -598
- package/src/types/properties.ts +0 -1368
- package/src/types/relations.ts +0 -417
- package/src/types/resource_kinds.ts +0 -390
- package/src/types/resources.ts +0 -368
- package/src/types/rls-functions.ts +0 -98
- package/src/types/schema_editing.ts +0 -157
- package/src/types/schema_version.ts +0 -112
- package/src/types/search.ts +0 -247
- package/src/types/security_rules.ts +0 -344
- package/src/types/storage_authorize.ts +0 -77
- package/src/types/storage_source.ts +0 -248
- package/src/types/websockets.ts +0 -117
- package/src/users/index.ts +0 -2
- package/src/users/user.ts +0 -69
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path prefix that marks an object as **public**. Files stored under this
|
|
3
|
-
* prefix are served without any auth token via a stable, permanent,
|
|
4
|
-
* CDN-cacheable URL (see {@link StorageSource.getSignedUrl}). Shared by the
|
|
5
|
-
* client SDK and the backend so both agree on which objects are public.
|
|
6
|
-
*
|
|
7
|
-
* @group Models
|
|
8
|
-
*/
|
|
9
|
-
export const PUBLIC_STORAGE_PREFIX = "public/";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* True when a storage key/path points at a public object (lives under
|
|
13
|
-
* {@link PUBLIC_STORAGE_PREFIX}). The check is applied to the key *within the
|
|
14
|
-
* bucket* — strip any `bucket/` and `scheme://` prefixes first.
|
|
15
|
-
*
|
|
16
|
-
* @group Models
|
|
17
|
-
*/
|
|
18
|
-
export function isPublicStoragePath(path: string | null | undefined): boolean {
|
|
19
|
-
if (!path) return false;
|
|
20
|
-
let p = path;
|
|
21
|
-
const scheme = p.indexOf("://");
|
|
22
|
-
if (scheme !== -1) p = p.substring(scheme + 3);
|
|
23
|
-
p = p.replace(/^\/+/, "");
|
|
24
|
-
|
|
25
|
-
// Defense-in-depth: a path containing traversal segments is never public,
|
|
26
|
-
// so an attacker can't reach a private object via `public/../secret`.
|
|
27
|
-
if (p.split("/").some((seg) => seg === "..")) return false;
|
|
28
|
-
|
|
29
|
-
// Public iff the object **key** starts with the public prefix. A single
|
|
30
|
-
// leading `default/` bucket segment is tolerated (the default bucket).
|
|
31
|
-
// A substring match is deliberately NOT used — a private object under a
|
|
32
|
-
// folder literally named `public` (e.g. `reports/public/q3.pdf`) must stay
|
|
33
|
-
// private. Named buckets: pass the key (not `bucket/key`) so the prefix is
|
|
34
|
-
// anchored; otherwise it falls back to a private, token-scoped URL (safe).
|
|
35
|
-
return p.startsWith(PUBLIC_STORAGE_PREFIX) || p.startsWith(`default/${PUBLIC_STORAGE_PREFIX}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @group Models
|
|
40
|
-
*/
|
|
41
|
-
export interface UploadFileProps {
|
|
42
|
-
file: File,
|
|
43
|
-
key: string,
|
|
44
|
-
metadata?: Record<string, unknown>,
|
|
45
|
-
bucket?: string,
|
|
46
|
-
/**
|
|
47
|
-
* Store this object as **public**: it is placed under
|
|
48
|
-
* {@link PUBLIC_STORAGE_PREFIX} and served via a stable, token-less,
|
|
49
|
-
* permanent URL (safe to persist in a database and cache on a CDN).
|
|
50
|
-
* Defaults to `false` (private, short-lived signed URLs).
|
|
51
|
-
*/
|
|
52
|
-
public?: boolean
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* @group Models
|
|
57
|
-
*/
|
|
58
|
-
export interface UploadFileResult {
|
|
59
|
-
/**
|
|
60
|
-
* Storage key including the file name where the file was uploaded.
|
|
61
|
-
*/
|
|
62
|
-
key: string;
|
|
63
|
-
/**
|
|
64
|
-
* Bucket where the file was uploaded
|
|
65
|
-
*/
|
|
66
|
-
bucket: string;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Fully qualified storage URL for the uploaded file.
|
|
70
|
-
*
|
|
71
|
-
* For example: `s3://my-bucket/path/to/file.png`. Every controller in the
|
|
72
|
-
* framework returns one — S3, GCS and local alike — and a caller that stores
|
|
73
|
-
* the reference needs it, so it is part of the result rather than a maybe.
|
|
74
|
-
*/
|
|
75
|
-
storageUrl: string;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @group Models
|
|
80
|
-
*/
|
|
81
|
-
export interface DownloadConfig {
|
|
82
|
-
/**
|
|
83
|
-
* Temporal url that can be used to download the file
|
|
84
|
-
*/
|
|
85
|
-
url: string | null;
|
|
86
|
-
|
|
87
|
-
metadata?: DownloadMetadata;
|
|
88
|
-
|
|
89
|
-
fileNotFound?: boolean;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* The full set of object metadata, including read-only properties.
|
|
94
|
-
* @public
|
|
95
|
-
*/
|
|
96
|
-
export declare interface DownloadMetadata {
|
|
97
|
-
/**
|
|
98
|
-
* The bucket this object is contained in.
|
|
99
|
-
*/
|
|
100
|
-
bucket: string;
|
|
101
|
-
/**
|
|
102
|
-
* The full path of this object.
|
|
103
|
-
*/
|
|
104
|
-
fullPath: string;
|
|
105
|
-
/**
|
|
106
|
-
* The short name of this object, which is the last component of the full path.
|
|
107
|
-
* For example, if path is 'full/path/image.png', name is 'image.png'.
|
|
108
|
-
*/
|
|
109
|
-
name: string;
|
|
110
|
-
/**
|
|
111
|
-
* The size of this object, in bytes.
|
|
112
|
-
*/
|
|
113
|
-
size: number;
|
|
114
|
-
/**
|
|
115
|
-
* Type of the uploaded file
|
|
116
|
-
* e.g. "image/jpeg"
|
|
117
|
-
*/
|
|
118
|
-
contentType: string;
|
|
119
|
-
|
|
120
|
-
customMetadata: Record<string, unknown>;
|
|
121
|
-
/**
|
|
122
|
-
* Optional short-lived download token (for local/server-mediated storage).
|
|
123
|
-
* Absent for public objects, which need no token.
|
|
124
|
-
*/
|
|
125
|
-
token?: string;
|
|
126
|
-
/**
|
|
127
|
-
* Optional remaining lifetime of the token, in seconds.
|
|
128
|
-
*/
|
|
129
|
-
tokenExpiresIn?: number;
|
|
130
|
-
/**
|
|
131
|
-
* True when this object is public: it is served without a token via a
|
|
132
|
-
* stable, permanent, CDN-cacheable URL. When set, the client builds a
|
|
133
|
-
* token-less URL and caches it indefinitely.
|
|
134
|
-
*/
|
|
135
|
-
public?: boolean;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* @group Models
|
|
140
|
-
*/
|
|
141
|
-
export interface StorageSource {
|
|
142
|
-
/**
|
|
143
|
-
* Upload an object, specifying a key
|
|
144
|
-
* @param file
|
|
145
|
-
* @param key
|
|
146
|
-
* @param metadata
|
|
147
|
-
* @param bucket
|
|
148
|
-
*/
|
|
149
|
-
putObject: ({
|
|
150
|
-
file,
|
|
151
|
-
key,
|
|
152
|
-
metadata,
|
|
153
|
-
bucket
|
|
154
|
-
}: UploadFileProps) => Promise<UploadFileResult>;
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Convert a storage key or URL into a download configuration (signed URL equivalent)
|
|
158
|
-
* @param keyOrUrl
|
|
159
|
-
* @param bucket
|
|
160
|
-
*/
|
|
161
|
-
getSignedUrl: (keyOrUrl: string, bucket?: string) => Promise<DownloadConfig>;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Get an object from a storage key.
|
|
165
|
-
* It returns null if the object does not exist.
|
|
166
|
-
* @param key
|
|
167
|
-
* @param bucket
|
|
168
|
-
*/
|
|
169
|
-
getObject: (key: string, bucket?: string) => Promise<File | null>;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Delete an object.
|
|
173
|
-
* @param key
|
|
174
|
-
* @param bucket
|
|
175
|
-
*/
|
|
176
|
-
deleteObject: (key: string, bucket?: string) => Promise<void>;
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* List the contents of a prefix.
|
|
180
|
-
* @param prefix
|
|
181
|
-
* @param options
|
|
182
|
-
*/
|
|
183
|
-
listObjects: (prefix: string, options?: {
|
|
184
|
-
bucket?: string,
|
|
185
|
-
maxResults?: number,
|
|
186
|
-
pageToken?: string
|
|
187
|
-
}) => Promise<StorageListResult>;
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Result returned by list().
|
|
193
|
-
* @public
|
|
194
|
-
*/
|
|
195
|
-
export declare interface StorageListResult {
|
|
196
|
-
/**
|
|
197
|
-
* References to prefixes (sub-folders). You can call list() on them to
|
|
198
|
-
* get its contents.
|
|
199
|
-
*
|
|
200
|
-
* Folders are implicit based on '/' in the object paths.
|
|
201
|
-
* For example, if a bucket has two objects '/a/b/1' and '/a/b/2', list('/a')
|
|
202
|
-
* will return '/a/b' as a prefix.
|
|
203
|
-
*/
|
|
204
|
-
prefixes: StorageReference[];
|
|
205
|
-
/**
|
|
206
|
-
* Objects in this directory.
|
|
207
|
-
* You can call getMetadata() and getDownloadUrl() on them.
|
|
208
|
-
*/
|
|
209
|
-
items: StorageReference[];
|
|
210
|
-
/**
|
|
211
|
-
* If set, there might be more results for this list. Use this token to resume the list.
|
|
212
|
-
*/
|
|
213
|
-
nextPageToken?: string;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Represents a reference to an S3-compatible storage object. Developers can
|
|
218
|
-
* upload, download, and delete objects, as well as get/set object metadata.
|
|
219
|
-
* @public
|
|
220
|
-
*/
|
|
221
|
-
export declare interface StorageReference {
|
|
222
|
-
/**
|
|
223
|
-
* Returns a s3:// URL for this object in the form
|
|
224
|
-
* `s3://<bucket>/<path>/<to>/<object>`
|
|
225
|
-
* @returns The s3:// URL.
|
|
226
|
-
*/
|
|
227
|
-
toString(): string;
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* A reference to the root of this object's bucket.
|
|
231
|
-
*/
|
|
232
|
-
root: StorageReference;
|
|
233
|
-
/**
|
|
234
|
-
* The name of the bucket containing this reference's object.
|
|
235
|
-
*/
|
|
236
|
-
bucket: string;
|
|
237
|
-
/**
|
|
238
|
-
* The full path of this object.
|
|
239
|
-
*/
|
|
240
|
-
fullPath: string;
|
|
241
|
-
/**
|
|
242
|
-
* The short name of this object, which is the last component of the full path.
|
|
243
|
-
* For example, if path is 'full/path/image.png', name is 'image.png'.
|
|
244
|
-
*/
|
|
245
|
-
name: string;
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* A reference pointing to the parent location of this reference, or null if
|
|
249
|
-
* this reference is the root.
|
|
250
|
-
*/
|
|
251
|
-
parent: StorageReference | null;
|
|
252
|
-
}
|
package/src/errors.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The error codes every route can produce, as `RebaseApiError.code`.
|
|
3
|
-
*
|
|
4
|
-
* These are the defaults on `ApiError`'s static constructors server-side, so
|
|
5
|
-
* any endpoint can answer with one. They are **not** the complete set: routes
|
|
6
|
-
* pass their own more specific codes too (`EMAIL_EXISTS`, `TOKEN_EXPIRED`,
|
|
7
|
-
* `INVALID_BULK_BODY`, …), and auth alone defines a couple of dozen.
|
|
8
|
-
*
|
|
9
|
-
* Hence the union is deliberately open rather than closed. It exists to give
|
|
10
|
-
* autocomplete and to catch a typo in the common cases — `code` was a bare
|
|
11
|
-
* `string`, so `e.code === "NOT_FOUND"` and `e.code === "NOTFOUND"` were
|
|
12
|
-
* equally valid and only one of them worked. Closing it would be a lie that
|
|
13
|
-
* broke the moment a route added a code.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* if (e instanceof RebaseApiError) {
|
|
17
|
-
* switch (e.code) {
|
|
18
|
-
* case "NOT_FOUND": return null; // completed
|
|
19
|
-
* case "FORBIDDEN": return redirect();
|
|
20
|
-
* default: throw e; // routes' own codes land here
|
|
21
|
-
* }
|
|
22
|
-
* }
|
|
23
|
-
*
|
|
24
|
-
* @group Errors
|
|
25
|
-
*/
|
|
26
|
-
export type RebaseErrorCode =
|
|
27
|
-
| "BAD_REQUEST"
|
|
28
|
-
| "UNAUTHORIZED"
|
|
29
|
-
| "FORBIDDEN"
|
|
30
|
-
| "NOT_FOUND"
|
|
31
|
-
| "CONFLICT"
|
|
32
|
-
| "INTERNAL_ERROR"
|
|
33
|
-
| "SERVICE_UNAVAILABLE"
|
|
34
|
-
| "DB_PERMISSION_DENIED"
|
|
35
|
-
| "SCHEMA_DRIFT"
|
|
36
|
-
// `string & {}` keeps the union open while preserving completion on the
|
|
37
|
-
// literals above — a bare `| string` would collapse them and offer nothing.
|
|
38
|
-
| (string & {});
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Structured initializer for {@link RebaseApiError}.
|
|
42
|
-
*
|
|
43
|
-
* @group Errors
|
|
44
|
-
*/
|
|
45
|
-
export interface RebaseErrorInit {
|
|
46
|
-
/**
|
|
47
|
-
* HTTP status code, when the error originated from an HTTP response.
|
|
48
|
-
* Left `undefined` for realtime/WebSocket, network, and client-side
|
|
49
|
-
* logic errors that have no HTTP status.
|
|
50
|
-
*/
|
|
51
|
-
status?: number;
|
|
52
|
-
/** Stable, machine-readable error code. See {@link RebaseErrorCode}. */
|
|
53
|
-
code?: RebaseErrorCode;
|
|
54
|
-
/** Structured error payload returned by the server, when present. */
|
|
55
|
-
details?: unknown;
|
|
56
|
-
/** The underlying error this one wraps, if any. */
|
|
57
|
-
cause?: unknown;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* The single error type thrown across the entire Rebase client surface —
|
|
62
|
-
* HTTP data/control-plane calls, realtime/WebSocket operations, and
|
|
63
|
-
* client-side logic errors (e.g. an unknown collection accessor). A `catch`
|
|
64
|
-
* block only ever needs to check for this one class:
|
|
65
|
-
*
|
|
66
|
-
* ```ts
|
|
67
|
-
* import { RebaseApiError } from "@rebasepro/client"; // re-exported
|
|
68
|
-
*
|
|
69
|
-
* try {
|
|
70
|
-
* await client.data.products.update(id, { price: 9 });
|
|
71
|
-
* } catch (e) {
|
|
72
|
-
* if (e instanceof RebaseApiError) {
|
|
73
|
-
* if (e.status === 404) { ... } // HTTP failures carry a status
|
|
74
|
-
* console.error(e.code, e.details);
|
|
75
|
-
* }
|
|
76
|
-
* }
|
|
77
|
-
* ```
|
|
78
|
-
*
|
|
79
|
-
* `status` is present for HTTP failures and `undefined` otherwise, so its
|
|
80
|
-
* presence distinguishes transport-level errors from realtime/logic errors.
|
|
81
|
-
*
|
|
82
|
-
* @group Errors
|
|
83
|
-
*/
|
|
84
|
-
export class RebaseApiError extends Error {
|
|
85
|
-
/** HTTP status code, or `undefined` for non-HTTP errors. */
|
|
86
|
-
readonly status?: number;
|
|
87
|
-
/** Stable machine-readable error code, when the server supplied one. See {@link RebaseErrorCode}. */
|
|
88
|
-
readonly code?: RebaseErrorCode;
|
|
89
|
-
/** Structured error payload from the server, when present. */
|
|
90
|
-
readonly details?: unknown;
|
|
91
|
-
|
|
92
|
-
constructor(message: string, init: RebaseErrorInit = {}) {
|
|
93
|
-
super(message);
|
|
94
|
-
this.name = "RebaseApiError";
|
|
95
|
-
this.status = init.status;
|
|
96
|
-
this.code = init.code;
|
|
97
|
-
this.details = init.details;
|
|
98
|
-
if (init.cause !== undefined) {
|
|
99
|
-
// `cause` is standard on Error but not always in the lib target's type.
|
|
100
|
-
(this as { cause?: unknown }).cause = init.cause;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Client-side logic error — raised before any request is made (e.g. accessing
|
|
107
|
-
* an unknown collection accessor when a typed dictionary is configured).
|
|
108
|
-
*
|
|
109
|
-
* A subclass of {@link RebaseApiError} (with no `status`), so a single
|
|
110
|
-
* `catch (e) { if (e instanceof RebaseApiError) ... }` handles it too.
|
|
111
|
-
*
|
|
112
|
-
* @group Errors
|
|
113
|
-
*/
|
|
114
|
-
export class RebaseClientError extends RebaseApiError {
|
|
115
|
-
constructor(message: string) {
|
|
116
|
-
super(message);
|
|
117
|
-
this.name = "RebaseClientError";
|
|
118
|
-
}
|
|
119
|
-
}
|
package/src/index.ts
DELETED
package/src/types/admin_block.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The keys of a collection's admin block, as data.
|
|
3
|
-
*
|
|
4
|
-
* There is no *type* for the block in this package any more, and that is the point:
|
|
5
|
-
* `admin` is not declared on `BaseCollectionConfig` or on any property here, so a
|
|
6
|
-
* BaaS install cannot even write one. `@rebasepro/cms-types` adds the field back by
|
|
7
|
-
* declaration merging, which is why installing it is what makes the admin surface
|
|
8
|
-
* appear.
|
|
9
|
-
*
|
|
10
|
-
* The *list* still has to live here, because three runtime consumers need it and two
|
|
11
|
-
* of them are core — see below.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Every key that belongs inside a collection's `admin` block, as data.
|
|
16
|
-
*
|
|
17
|
-
* The type that describes these fields is `AdminCollectionOptions` in
|
|
18
|
-
* `@rebasepro/cms-types`, and it is erased at build time — but three runtime
|
|
19
|
-
* consumers need the list, and two of them are core:
|
|
20
|
-
*
|
|
21
|
-
* - `serializeCollections`, to drop the block from the contract
|
|
22
|
-
* - the ts-morph schema editor in `@rebasepro/server`, which rewrites collection
|
|
23
|
-
* files on disk from the admin panel and has to know where each key goes. A key
|
|
24
|
-
* missing from this list gets written to the *top level* of the file, where the
|
|
25
|
-
* backend ignores it and the panel never finds it again.
|
|
26
|
-
* - the `collections-admin-block` codemod
|
|
27
|
-
*
|
|
28
|
-
* `@rebasepro/cms-types` re-exports this and asserts it names only real option
|
|
29
|
-
* keys; the count is pinned by a test there.
|
|
30
|
-
*
|
|
31
|
-
* @group Models
|
|
32
|
-
*/
|
|
33
|
-
export const ADMIN_COLLECTION_KEYS = [
|
|
34
|
-
"Actions",
|
|
35
|
-
"additionalFields",
|
|
36
|
-
"alwaysApplyDefaultValues",
|
|
37
|
-
"components",
|
|
38
|
-
"customViews",
|
|
39
|
-
"defaultEntityAction",
|
|
40
|
-
"defaultFilter",
|
|
41
|
-
"defaultSelectedView",
|
|
42
|
-
"defaultSize",
|
|
43
|
-
"defaultViewMode",
|
|
44
|
-
"disableDefaultActions",
|
|
45
|
-
"display",
|
|
46
|
-
"enabledViews",
|
|
47
|
-
"entityActions",
|
|
48
|
-
"entityViews",
|
|
49
|
-
"exportable",
|
|
50
|
-
"filterPresets",
|
|
51
|
-
"fixedFilter",
|
|
52
|
-
"form",
|
|
53
|
-
"formAutoSave",
|
|
54
|
-
"formView",
|
|
55
|
-
"group",
|
|
56
|
-
"hideFromEntityViews",
|
|
57
|
-
"hideFromNavigation",
|
|
58
|
-
"hideIdFromCollection",
|
|
59
|
-
"hideIdFromForm",
|
|
60
|
-
"icon",
|
|
61
|
-
"includeJsonView",
|
|
62
|
-
"inlineEditing",
|
|
63
|
-
"kanban",
|
|
64
|
-
"listProperties",
|
|
65
|
-
"localChangesBackup",
|
|
66
|
-
"openEntityMode",
|
|
67
|
-
"orderProperty",
|
|
68
|
-
"pagination",
|
|
69
|
-
"previewProperties",
|
|
70
|
-
"propertiesOrder",
|
|
71
|
-
"selectionController",
|
|
72
|
-
"selectionEnabled",
|
|
73
|
-
"sideDialogWidth",
|
|
74
|
-
"sort"
|
|
75
|
-
] as const;
|
|
76
|
-
|
|
77
|
-
/** A key of a collection's `admin` block. @group Models */
|
|
78
|
-
export type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Every key that belongs inside a *property's* `admin` block, as data.
|
|
82
|
-
*
|
|
83
|
-
* The union of `AdminPropertyOptions` and its per-type extensions
|
|
84
|
-
* (`AdminStringOptions`, `AdminArrayOptions`, …) in `@rebasepro/cms-types`.
|
|
85
|
-
* It lives here for the same reason {@link ADMIN_COLLECTION_KEYS} does: the
|
|
86
|
-
* runtime consumers are core packages that the BaaS guard forbids from
|
|
87
|
-
* importing `@rebasepro/cms-types`. Here it is the boot-time collection
|
|
88
|
-
* validator in `@rebasepro/server`, which has to tell "you left `readOnly` at
|
|
89
|
-
* the top of the property, where nothing reads it" apart from "you invented a
|
|
90
|
-
* key we have never heard of".
|
|
91
|
-
*
|
|
92
|
-
* `@rebasepro/cms-types` re-exports this and asserts it names only real
|
|
93
|
-
* option keys.
|
|
94
|
-
*
|
|
95
|
-
* @group Models
|
|
96
|
-
*/
|
|
97
|
-
export const ADMIN_PROPERTY_KEYS = [
|
|
98
|
-
"canAddElements",
|
|
99
|
-
"clearable",
|
|
100
|
-
"columnWidth",
|
|
101
|
-
"customProps",
|
|
102
|
-
"disabled",
|
|
103
|
-
"expanded",
|
|
104
|
-
"Field",
|
|
105
|
-
"Filter",
|
|
106
|
-
"filterOperators",
|
|
107
|
-
"fixedFilter",
|
|
108
|
-
"hideFromCollection",
|
|
109
|
-
"includeEntityLink",
|
|
110
|
-
"includeId",
|
|
111
|
-
"markdown",
|
|
112
|
-
"minimalistView",
|
|
113
|
-
"multiline",
|
|
114
|
-
"Preview",
|
|
115
|
-
"previewAsTag",
|
|
116
|
-
"previewProperties",
|
|
117
|
-
"readOnly",
|
|
118
|
-
"sortable",
|
|
119
|
-
"span",
|
|
120
|
-
"spreadChildren",
|
|
121
|
-
"urlPreview",
|
|
122
|
-
"widget",
|
|
123
|
-
] as const;
|
|
124
|
-
|
|
125
|
-
/** A key of a property's `admin` block. @group Models */
|
|
126
|
-
export type AdminPropertyKey = typeof ADMIN_PROPERTY_KEYS[number];
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Move flattened admin keys back down into the `admin` block.
|
|
130
|
-
*
|
|
131
|
-
* The admin panel works with a *flat* view model — the block merged onto the
|
|
132
|
-
* collection — so what comes back from a form has `icon` and `defaultViewMode`
|
|
133
|
-
* at the top level while `admin` still holds whatever the file was loaded with.
|
|
134
|
-
* This is the way back.
|
|
135
|
-
*
|
|
136
|
-
* **The top-level value wins.** It is the one the form just wrote; the block is
|
|
137
|
-
* the copy the collection was loaded with, and preferring it resolves every edit
|
|
138
|
-
* in favour of the value the user changed away from.
|
|
139
|
-
*
|
|
140
|
-
* This lives here, next to the key lists, because it had two implementations —
|
|
141
|
-
* `toAdminCollectionConfig` in `@rebasepro/cms-types` and `nestAdminKeys` in
|
|
142
|
-
* `@rebasepro/server`'s schema editor — that agreed on everything except that
|
|
143
|
-
* precedence, which is the only part that decides whether a save is visible.
|
|
144
|
-
*
|
|
145
|
-
* @group Models
|
|
146
|
-
*/
|
|
147
|
-
export function nestAdminKeysOf(
|
|
148
|
-
source: Record<string, unknown>,
|
|
149
|
-
adminKeys: readonly string[]
|
|
150
|
-
): Record<string, unknown> {
|
|
151
|
-
const keys = new Set<string>(adminKeys);
|
|
152
|
-
const top: Record<string, unknown> = {};
|
|
153
|
-
const block: Record<string, unknown> = { ...((source.admin as Record<string, unknown> | undefined) ?? {}) };
|
|
154
|
-
|
|
155
|
-
for (const [key, value] of Object.entries(source)) {
|
|
156
|
-
if (key === "admin") continue;
|
|
157
|
-
if (keys.has(key)) block[key] = value;
|
|
158
|
-
else top[key] = value;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (Object.keys(block).length > 0) top.admin = block;
|
|
162
|
-
return top;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* {@link nestAdminKeysOf} for a collection.
|
|
167
|
-
*
|
|
168
|
-
* @group Models
|
|
169
|
-
*/
|
|
170
|
-
export function nestAdminCollectionKeys(collection: Record<string, unknown>): Record<string, unknown> {
|
|
171
|
-
return nestAdminKeysOf(collection, ADMIN_COLLECTION_KEYS);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* {@link nestAdminKeysOf} for a property, applied to its children too.
|
|
176
|
-
*
|
|
177
|
-
* A map property carries `properties`, an array property carries `of`, and both
|
|
178
|
-
* hold properties with `admin` blocks of their own. A flat `readOnly` left on a
|
|
179
|
-
* child is as dead — and as fatal at the next boot — as one left on the parent,
|
|
180
|
-
* so the walk goes all the way down.
|
|
181
|
-
*
|
|
182
|
-
* @group Models
|
|
183
|
-
*/
|
|
184
|
-
export function nestAdminPropertyKeys(property: Record<string, unknown>): Record<string, unknown> {
|
|
185
|
-
const nested = nestAdminKeysOf(property, ADMIN_PROPERTY_KEYS);
|
|
186
|
-
|
|
187
|
-
const children = nested.properties;
|
|
188
|
-
if (children && typeof children === "object" && !Array.isArray(children)) {
|
|
189
|
-
nested.properties = Object.fromEntries(
|
|
190
|
-
Object.entries(children as Record<string, unknown>).map(([key, child]) => [
|
|
191
|
-
key,
|
|
192
|
-
child && typeof child === "object" && !Array.isArray(child)
|
|
193
|
-
? nestAdminPropertyKeys(child as Record<string, unknown>)
|
|
194
|
-
: child
|
|
195
|
-
])
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const of = nested.of;
|
|
200
|
-
if (Array.isArray(of)) {
|
|
201
|
-
nested.of = of.map(entry => entry && typeof entry === "object" && !Array.isArray(entry)
|
|
202
|
-
? nestAdminPropertyKeys(entry as Record<string, unknown>)
|
|
203
|
-
: entry);
|
|
204
|
-
} else if (of && typeof of === "object") {
|
|
205
|
-
nested.of = nestAdminPropertyKeys(of as Record<string, unknown>);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return nested;
|
|
209
|
-
}
|
package/src/types/api_keys.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Service API keys — machine-to-machine authentication for scripts, cron jobs
|
|
3
|
-
* and third-party integrations.
|
|
4
|
-
*
|
|
5
|
-
* The wire contract lives here because all three sides need it and used to
|
|
6
|
-
* declare it separately: `@rebasepro/server` implements the routes,
|
|
7
|
-
* `@rebasepro/client` calls them, and {@link ApiKeysAPI} types the SDK surface.
|
|
8
|
-
* The client's copy had already drifted — it never gained `admin`.
|
|
9
|
-
*
|
|
10
|
-
* The database row itself (`ApiKey`, which carries `key_hash`) stays in the
|
|
11
|
-
* server package: nothing off the server may see it.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A single permission entry scoping an API key to a collection and set of
|
|
16
|
-
* operations.
|
|
17
|
-
*
|
|
18
|
-
* Use `"*"` as the collection value to grant access to all collections (and all
|
|
19
|
-
* custom functions). Custom functions are addressed with the `functions`
|
|
20
|
-
* namespace: `"functions"` grants every function, `"functions/<name>"` grants a
|
|
21
|
-
* single one.
|
|
22
|
-
*
|
|
23
|
-
* @group Models
|
|
24
|
-
*/
|
|
25
|
-
export interface ApiKeyPermission {
|
|
26
|
-
/** Collection slug, `"functions"`/`"functions/<name>"`, or `"*"` for everything. */
|
|
27
|
-
collection: string;
|
|
28
|
-
/** Allowed operations on the collection. */
|
|
29
|
-
operations: ("read" | "write" | "delete")[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* An API key with the secret portion masked — what list / get / update return.
|
|
34
|
-
* @group Models
|
|
35
|
-
*/
|
|
36
|
-
export interface ApiKeyMasked {
|
|
37
|
-
id: string;
|
|
38
|
-
name: string;
|
|
39
|
-
/** First 12 characters of the plaintext key, for display only. */
|
|
40
|
-
key_prefix: string;
|
|
41
|
-
permissions: ApiKeyPermission[];
|
|
42
|
-
/**
|
|
43
|
-
* When true, the key is granted the `admin` role: it passes the admin-gated
|
|
44
|
-
* routes (users, roles, cron, backups, logs, API keys) and the RLS
|
|
45
|
-
* `default_admin` policies. Non-admin keys carry only the `service` role —
|
|
46
|
-
* RLS grants them nothing unless a collection policy names that role.
|
|
47
|
-
*/
|
|
48
|
-
admin: boolean;
|
|
49
|
-
/**
|
|
50
|
-
* Requests per 15-minute window. `null` means "no per-key override" — the
|
|
51
|
-
* data rate limiter then applies its default API-key limit (1000/window
|
|
52
|
-
* unless configured otherwise), not unlimited.
|
|
53
|
-
*/
|
|
54
|
-
rate_limit: number | null;
|
|
55
|
-
created_by: string;
|
|
56
|
-
created_at: string;
|
|
57
|
-
updated_at: string;
|
|
58
|
-
last_used_at: string | null;
|
|
59
|
-
expires_at: string | null;
|
|
60
|
-
revoked_at: string | null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Returned exactly once, when a key is created. The `key` field holds the full
|
|
65
|
-
* plaintext key — it is never stored or returned again.
|
|
66
|
-
* @group Models
|
|
67
|
-
*/
|
|
68
|
-
export interface ApiKeyWithSecret extends ApiKeyMasked {
|
|
69
|
-
/** Full plaintext API key (e.g. `rk_live_abc123...`). */
|
|
70
|
-
key: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Payload for creating a new API key.
|
|
75
|
-
* @group Models
|
|
76
|
-
*/
|
|
77
|
-
export interface CreateApiKeyRequest {
|
|
78
|
-
name: string;
|
|
79
|
-
permissions: ApiKeyPermission[];
|
|
80
|
-
/** When true, grants the `admin` role. See {@link ApiKeyMasked.admin}. */
|
|
81
|
-
admin?: boolean;
|
|
82
|
-
/** Requests per 15-minute window. Omit or `null` for the server default. */
|
|
83
|
-
rate_limit?: number | null;
|
|
84
|
-
/** ISO-8601 expiration timestamp. Omit for no expiration. */
|
|
85
|
-
expires_at?: string | null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Payload for updating an existing API key. Only the fields provided change.
|
|
90
|
-
* @group Models
|
|
91
|
-
*/
|
|
92
|
-
export interface UpdateApiKeyRequest {
|
|
93
|
-
name?: string;
|
|
94
|
-
permissions?: ApiKeyPermission[];
|
|
95
|
-
/** When true, grants the `admin` role. See {@link ApiKeyMasked.admin}. */
|
|
96
|
-
admin?: boolean;
|
|
97
|
-
rate_limit?: number | null;
|
|
98
|
-
expires_at?: string | null;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** @group Models */
|
|
102
|
-
export interface ApiKeysAPI {
|
|
103
|
-
listKeys(): Promise<{ keys: ApiKeyMasked[] }>;
|
|
104
|
-
getKey(id: string): Promise<{ key: ApiKeyMasked }>;
|
|
105
|
-
createKey(data: CreateApiKeyRequest): Promise<{ key: ApiKeyWithSecret }>;
|
|
106
|
-
updateKey(id: string, data: UpdateApiKeyRequest): Promise<{ key: ApiKeyMasked }>;
|
|
107
|
-
revokeKey(id: string): Promise<{ success: boolean }>;
|
|
108
|
-
}
|