@rebasepro/server 0.13.1-canary.gf57a27e → 0.14.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/{admin_block-CLeUMG8G.js → admin_block-H86dCPsj.js} +60 -2
- package/dist/admin_block-H86dCPsj.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +74 -6
- package/dist/api/errors.d.ts +12 -0
- package/dist/api/rest/api-generator.d.ts +26 -1
- package/dist/api/rest/idempotency.d.ts +25 -4
- package/dist/api/rest/query-parser.d.ts +15 -1
- package/dist/api/rest/write-validation.d.ts +40 -4
- package/dist/auth/adapter-middleware.d.ts +3 -1
- package/dist/auth/admin-roles.d.ts +24 -0
- package/dist/auth/apple-oauth.d.ts +16 -11
- package/dist/auth/bitbucket-oauth.d.ts +2 -4
- package/dist/auth/builtin-auth-adapter.d.ts +11 -0
- package/dist/auth/discord-oauth.d.ts +5 -6
- package/dist/auth/facebook-oauth.d.ts +2 -4
- package/dist/auth/github-oauth.d.ts +2 -4
- package/dist/auth/gitlab-oauth.d.ts +6 -4
- package/dist/auth/google-oauth.d.ts +1 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/interfaces.d.ts +81 -1
- package/dist/auth/jwt.d.ts +40 -0
- package/dist/auth/linkedin-oauth.d.ts +2 -4
- package/dist/auth/mfa-crypto.d.ts +54 -3
- package/dist/auth/mfa-gate.d.ts +42 -0
- package/dist/auth/mfa-routes.d.ts +26 -1
- package/dist/auth/mfa.d.ts +16 -0
- package/dist/auth/microsoft-oauth.d.ts +19 -4
- package/dist/auth/middleware.d.ts +26 -0
- package/dist/auth/oauth-code-flow.d.ts +66 -0
- package/dist/auth/oauth-signin-policy.d.ts +61 -0
- package/dist/auth/oidc-id-token.d.ts +61 -0
- package/dist/auth/rate-limiter.d.ts +62 -5
- package/dist/auth/registration-policy.d.ts +31 -0
- package/dist/auth/rls-scope.d.ts +25 -0
- package/dist/auth/routes.d.ts +15 -0
- package/dist/auth/slack-oauth.d.ts +2 -4
- package/dist/auth/spotify-oauth.d.ts +2 -4
- package/dist/auth/token-revocation.d.ts +38 -0
- package/dist/auth/twitter-oauth.d.ts +2 -5
- package/dist/{auth-BANhRhN9.js → auth-CYoPVf-E.js} +1814 -354
- package/dist/auth-CYoPVf-E.js.map +1 -0
- package/dist/{backup-BJiY_mM-.js → backup-C6ljYVTp.js} +2 -2
- package/dist/{backup-BJiY_mM-.js.map → backup-C6ljYVTp.js.map} +1 -1
- package/dist/{contract-routes-FSt0Nyfk.js → contract-routes-Bet-eCNJ.js} +3 -3
- package/dist/{contract-routes-FSt0Nyfk.js.map → contract-routes-Bet-eCNJ.js.map} +1 -1
- package/dist/cron/cron-loader.d.ts +26 -1
- package/dist/cron/cron-routes.d.ts +1 -1
- package/dist/cron/cron-scheduler.d.ts +5 -0
- package/dist/cron/define-cron.d.ts +14 -2
- package/dist/cron/index.d.ts +2 -2
- package/dist/{cron-loader-B1S2MCSl.js → cron-loader-YhhQeVBM.js} +43 -8
- package/dist/cron-loader-YhhQeVBM.js.map +1 -0
- package/dist/{cron-routes-D5a9v1HY.js → cron-routes-maM_RlUu.js} +10 -4
- package/dist/cron-routes-maM_RlUu.js.map +1 -0
- package/dist/{cron-scheduler-CsQtYLMq.js → cron-scheduler-DIpYBmZP.js} +13 -7
- package/dist/cron-scheduler-DIpYBmZP.js.map +1 -0
- package/dist/{cron-store-ZbjfuvMb.js → cron-store-Dvr4Y1sZ.js} +5 -5
- package/dist/{cron-store-ZbjfuvMb.js.map → cron-store-Dvr4Y1sZ.js.map} +1 -1
- package/dist/{ddl-bootstrap-Bp3mr6Td.js → ddl-bootstrap-BhXbTnBl.js} +2 -2
- package/dist/{ddl-bootstrap-Bp3mr6Td.js.map → ddl-bootstrap-BhXbTnBl.js.map} +1 -1
- package/dist/email/html.d.ts +54 -0
- package/dist/email/index.d.ts +3 -0
- package/dist/email/link-base.d.ts +39 -0
- package/dist/email/smtp-email-service.d.ts +7 -1
- package/dist/email/templates.d.ts +9 -1
- package/dist/email/types.d.ts +11 -1
- package/dist/env.d.ts +5 -0
- package/dist/{errors-BpudWAVU.js → errors-EBYiaJ2E.js} +14 -2
- package/dist/errors-EBYiaJ2E.js.map +1 -0
- package/dist/function-loader-DDS1v7YX.js +139 -0
- package/dist/function-loader-DDS1v7YX.js.map +1 -0
- package/dist/{function-routes-C0cLIy3N.js → function-routes-Btcez1T-.js} +18 -6
- package/dist/function-routes-Btcez1T-.js.map +1 -0
- package/dist/functions/define-function.d.ts +24 -5
- package/dist/functions/function-loader.d.ts +23 -0
- package/dist/functions/function-routes.d.ts +7 -1
- package/dist/functions/request-timeout.d.ts +34 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.es.js +1437 -694
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +10 -1
- package/dist/init/process-safety.d.ts +25 -0
- package/dist/init.d.ts +38 -0
- package/dist/{jwt-CzeviDcB.js → jwt-_IFqfTOg.js} +57 -9
- package/dist/jwt-_IFqfTOg.js.map +1 -0
- package/dist/logger-DfvF_8r-.js +190 -0
- package/dist/logger-DfvF_8r-.js.map +1 -0
- package/dist/{openapi-generator-VB0MUH8h.js → openapi-generator-DPKtUC9X.js} +87 -31
- package/dist/openapi-generator-DPKtUC9X.js.map +1 -0
- package/dist/request-timeout-RivJsME0.js +65 -0
- package/dist/request-timeout-RivJsME0.js.map +1 -0
- package/dist/schema-editor-routes-CRcS3ArS.js +437 -0
- package/dist/schema-editor-routes-CRcS3ArS.js.map +1 -0
- package/dist/serve-spa.d.ts +5 -4
- package/dist/services/outbound-url-guard.d.ts +53 -0
- package/dist/services/routed-realtime-service.d.ts +10 -2
- package/dist/services/webhook-service.d.ts +76 -1
- package/dist/singleton.d.ts +25 -8
- package/dist/{src-DVb5a0bp.js → src-C7rkDGxA.js} +536 -61
- package/dist/src-C7rkDGxA.js.map +1 -0
- package/dist/{src-Ca6NhxKs.js → src-Cz9nMgUR.js} +16 -3
- package/dist/src-Cz9nMgUR.js.map +1 -0
- package/dist/storage/LocalStorageController.d.ts +12 -1
- package/dist/storage/image-transform.d.ts +57 -0
- package/dist/storage/keys.d.ts +34 -1
- package/dist/storage/routes.d.ts +11 -0
- package/dist/storage/tus-handler.d.ts +22 -2
- package/dist/utils/logger.d.ts +18 -0
- package/package.json +6 -5
- package/dist/admin_block-CLeUMG8G.js.map +0 -1
- package/dist/auth-BANhRhN9.js.map +0 -1
- package/dist/cron-loader-B1S2MCSl.js.map +0 -1
- package/dist/cron-routes-D5a9v1HY.js.map +0 -1
- package/dist/cron-scheduler-CsQtYLMq.js.map +0 -1
- package/dist/errors-BpudWAVU.js.map +0 -1
- package/dist/function-loader-B_1fYfUY.js +0 -86
- package/dist/function-loader-B_1fYfUY.js.map +0 -1
- package/dist/function-routes-C0cLIy3N.js.map +0 -1
- package/dist/jwt-CzeviDcB.js.map +0 -1
- package/dist/logger-BYU66ENZ.js +0 -94
- package/dist/logger-BYU66ENZ.js.map +0 -1
- package/dist/openapi-generator-VB0MUH8h.js.map +0 -1
- package/dist/schema-editor-routes-BqjzvvWU.js +0 -248
- package/dist/schema-editor-routes-BqjzvvWU.js.map +0 -1
- package/dist/src-Ca6NhxKs.js.map +0 -1
- package/dist/src-DVb5a0bp.js.map +0 -1
|
@@ -120,7 +120,65 @@ var ADMIN_PROPERTY_KEYS = [
|
|
|
120
120
|
"urlPreview",
|
|
121
121
|
"widget"
|
|
122
122
|
];
|
|
123
|
+
/**
|
|
124
|
+
* Move flattened admin keys back down into the `admin` block.
|
|
125
|
+
*
|
|
126
|
+
* The admin panel works with a *flat* view model — the block merged onto the
|
|
127
|
+
* collection — so what comes back from a form has `icon` and `defaultViewMode`
|
|
128
|
+
* at the top level while `admin` still holds whatever the file was loaded with.
|
|
129
|
+
* This is the way back.
|
|
130
|
+
*
|
|
131
|
+
* **The top-level value wins.** It is the one the form just wrote; the block is
|
|
132
|
+
* the copy the collection was loaded with, and preferring it resolves every edit
|
|
133
|
+
* in favour of the value the user changed away from.
|
|
134
|
+
*
|
|
135
|
+
* This lives here, next to the key lists, because it had two implementations —
|
|
136
|
+
* `toAdminCollectionConfig` in `@rebasepro/admin-types` and `nestAdminKeys` in
|
|
137
|
+
* `@rebasepro/server`'s schema editor — that agreed on everything except that
|
|
138
|
+
* precedence, which is the only part that decides whether a save is visible.
|
|
139
|
+
*
|
|
140
|
+
* @group Models
|
|
141
|
+
*/
|
|
142
|
+
function nestAdminKeysOf(source, adminKeys) {
|
|
143
|
+
const keys = new Set(adminKeys);
|
|
144
|
+
const top = {};
|
|
145
|
+
const block = { ...source.admin ?? {} };
|
|
146
|
+
for (const [key, value] of Object.entries(source)) {
|
|
147
|
+
if (key === "admin") continue;
|
|
148
|
+
if (keys.has(key)) block[key] = value;
|
|
149
|
+
else top[key] = value;
|
|
150
|
+
}
|
|
151
|
+
if (Object.keys(block).length > 0) top.admin = block;
|
|
152
|
+
return top;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* {@link nestAdminKeysOf} for a collection.
|
|
156
|
+
*
|
|
157
|
+
* @group Models
|
|
158
|
+
*/
|
|
159
|
+
function nestAdminCollectionKeys(collection) {
|
|
160
|
+
return nestAdminKeysOf(collection, ADMIN_COLLECTION_KEYS);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* {@link nestAdminKeysOf} for a property, applied to its children too.
|
|
164
|
+
*
|
|
165
|
+
* A map property carries `properties`, an array property carries `of`, and both
|
|
166
|
+
* hold properties with `admin` blocks of their own. A flat `readOnly` left on a
|
|
167
|
+
* child is as dead — and as fatal at the next boot — as one left on the parent,
|
|
168
|
+
* so the walk goes all the way down.
|
|
169
|
+
*
|
|
170
|
+
* @group Models
|
|
171
|
+
*/
|
|
172
|
+
function nestAdminPropertyKeys(property) {
|
|
173
|
+
const nested = nestAdminKeysOf(property, ADMIN_PROPERTY_KEYS);
|
|
174
|
+
const children = nested.properties;
|
|
175
|
+
if (children && typeof children === "object" && !Array.isArray(children)) nested.properties = Object.fromEntries(Object.entries(children).map(([key, child]) => [key, child && typeof child === "object" && !Array.isArray(child) ? nestAdminPropertyKeys(child) : child]));
|
|
176
|
+
const of = nested.of;
|
|
177
|
+
if (Array.isArray(of)) nested.of = of.map((entry) => entry && typeof entry === "object" && !Array.isArray(entry) ? nestAdminPropertyKeys(entry) : entry);
|
|
178
|
+
else if (of && typeof of === "object") nested.of = nestAdminPropertyKeys(of);
|
|
179
|
+
return nested;
|
|
180
|
+
}
|
|
123
181
|
//#endregion
|
|
124
|
-
export { ADMIN_PROPERTY_KEYS as n, ADMIN_COLLECTION_KEYS as t };
|
|
182
|
+
export { nestAdminPropertyKeys as i, ADMIN_PROPERTY_KEYS as n, nestAdminCollectionKeys as r, ADMIN_COLLECTION_KEYS as t };
|
|
125
183
|
|
|
126
|
-
//# sourceMappingURL=admin_block-
|
|
184
|
+
//# sourceMappingURL=admin_block-H86dCPsj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin_block-H86dCPsj.js","names":[],"sources":["../../types/src/types/admin_block.ts"],"sourcesContent":["/**\n * The keys of a collection's admin block, as data.\n *\n * There is no *type* for the block in this package any more, and that is the point:\n * `admin` is not declared on `BaseCollectionConfig` or on any property here, so a\n * BaaS install cannot even write one. `@rebasepro/admin-types` adds the field back by\n * declaration merging, which is why installing it is what makes the admin surface\n * appear.\n *\n * The *list* still has to live here, because three runtime consumers need it and two\n * of them are core — see below.\n */\n\n/**\n * Every key that belongs inside a collection's `admin` block, as data.\n *\n * The type that describes these fields is `AdminCollectionOptions` in\n * `@rebasepro/admin-types`, and it is erased at build time — but three runtime\n * consumers need the list, and two of them are core:\n *\n * - `serializeCollections`, to drop the block from the contract\n * - the ts-morph schema editor in `@rebasepro/server`, which rewrites collection\n * files on disk from the admin panel and has to know where each key goes. A key\n * missing from this list gets written to the *top level* of the file, where the\n * backend ignores it and the panel never finds it again.\n * - the `collections-admin-block` codemod\n *\n * `@rebasepro/admin-types` re-exports this and asserts it names only real option\n * keys; the count is pinned by a test there.\n *\n * @group Models\n */\nexport const ADMIN_COLLECTION_KEYS = [\n \"Actions\",\n \"additionalFields\",\n \"alwaysApplyDefaultValues\",\n \"components\",\n \"defaultEntityAction\",\n \"defaultFilter\",\n \"defaultSelectedView\",\n \"defaultSize\",\n \"defaultViewMode\",\n \"disableDefaultActions\",\n \"display\",\n \"enabledViews\",\n \"entityActions\",\n \"entityViews\",\n \"exportable\",\n \"filterPresets\",\n \"fixedFilter\",\n \"form\",\n \"formAutoSave\",\n \"formView\",\n \"group\",\n \"hideFromEntityViews\",\n \"hideFromNavigation\",\n \"hideIdFromCollection\",\n \"hideIdFromForm\",\n \"icon\",\n \"includeJsonView\",\n \"inlineEditing\",\n \"kanban\",\n \"listProperties\",\n \"localChangesBackup\",\n \"openEntityMode\",\n \"orderProperty\",\n \"pagination\",\n \"previewProperties\",\n \"propertiesOrder\",\n \"selectionController\",\n \"selectionEnabled\",\n \"sideDialogWidth\",\n \"sort\",\n \"titleProperty\"\n] as const;\n\n/** A key of a collection's `admin` block. @group Models */\nexport type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];\n\n/**\n * Every key that belongs inside a *property's* `admin` block, as data.\n *\n * The union of `AdminPropertyOptions` and its per-type extensions\n * (`AdminStringOptions`, `AdminArrayOptions`, …) in `@rebasepro/admin-types`.\n * It lives here for the same reason {@link ADMIN_COLLECTION_KEYS} does: the\n * runtime consumers are core packages that the BaaS guard forbids from\n * importing `@rebasepro/admin-types`. Here it is the boot-time collection\n * validator in `@rebasepro/server`, which has to tell \"you left `readOnly` at\n * the top of the property, where nothing reads it\" apart from \"you invented a\n * key we have never heard of\".\n *\n * `@rebasepro/admin-types` re-exports this and asserts it names only real\n * option keys.\n *\n * @group Models\n */\nexport const ADMIN_PROPERTY_KEYS = [\n \"canAddElements\",\n \"clearable\",\n \"columnWidth\",\n \"customProps\",\n \"disabled\",\n \"expanded\",\n \"Field\",\n \"Filter\",\n \"filterOperators\",\n \"fixedFilter\",\n \"hideFromCollection\",\n \"includeEntityLink\",\n \"includeId\",\n \"markdown\",\n \"minimalistView\",\n \"multiline\",\n \"Preview\",\n \"previewAsTag\",\n \"previewProperties\",\n \"readOnly\",\n \"sortable\",\n \"span\",\n \"spreadChildren\",\n \"urlPreview\",\n \"widget\",\n] as const;\n\n/** A key of a property's `admin` block. @group Models */\nexport type AdminPropertyKey = typeof ADMIN_PROPERTY_KEYS[number];\n\n/**\n * Move flattened admin keys back down into the `admin` block.\n *\n * The admin panel works with a *flat* view model — the block merged onto the\n * collection — so what comes back from a form has `icon` and `defaultViewMode`\n * at the top level while `admin` still holds whatever the file was loaded with.\n * This is the way back.\n *\n * **The top-level value wins.** It is the one the form just wrote; the block is\n * the copy the collection was loaded with, and preferring it resolves every edit\n * in favour of the value the user changed away from.\n *\n * This lives here, next to the key lists, because it had two implementations —\n * `toAdminCollectionConfig` in `@rebasepro/admin-types` and `nestAdminKeys` in\n * `@rebasepro/server`'s schema editor — that agreed on everything except that\n * precedence, which is the only part that decides whether a save is visible.\n *\n * @group Models\n */\nexport function nestAdminKeysOf(\n source: Record<string, unknown>,\n adminKeys: readonly string[]\n): Record<string, unknown> {\n const keys = new Set<string>(adminKeys);\n const top: Record<string, unknown> = {};\n const block: Record<string, unknown> = { ...((source.admin as Record<string, unknown> | undefined) ?? {}) };\n\n for (const [key, value] of Object.entries(source)) {\n if (key === \"admin\") continue;\n if (keys.has(key)) block[key] = value;\n else top[key] = value;\n }\n\n if (Object.keys(block).length > 0) top.admin = block;\n return top;\n}\n\n/**\n * {@link nestAdminKeysOf} for a collection.\n *\n * @group Models\n */\nexport function nestAdminCollectionKeys(collection: Record<string, unknown>): Record<string, unknown> {\n return nestAdminKeysOf(collection, ADMIN_COLLECTION_KEYS);\n}\n\n/**\n * {@link nestAdminKeysOf} for a property, applied to its children too.\n *\n * A map property carries `properties`, an array property carries `of`, and both\n * hold properties with `admin` blocks of their own. A flat `readOnly` left on a\n * child is as dead — and as fatal at the next boot — as one left on the parent,\n * so the walk goes all the way down.\n *\n * @group Models\n */\nexport function nestAdminPropertyKeys(property: Record<string, unknown>): Record<string, unknown> {\n const nested = nestAdminKeysOf(property, ADMIN_PROPERTY_KEYS);\n\n const children = nested.properties;\n if (children && typeof children === \"object\" && !Array.isArray(children)) {\n nested.properties = Object.fromEntries(\n Object.entries(children as Record<string, unknown>).map(([key, child]) => [\n key,\n child && typeof child === \"object\" && !Array.isArray(child)\n ? nestAdminPropertyKeys(child as Record<string, unknown>)\n : child\n ])\n );\n }\n\n const of = nested.of;\n if (Array.isArray(of)) {\n nested.of = of.map(entry => entry && typeof entry === \"object\" && !Array.isArray(entry)\n ? nestAdminPropertyKeys(entry as Record<string, unknown>)\n : entry);\n } else if (of && typeof of === \"object\") {\n nested.of = nestAdminPropertyKeys(of as Record<string, unknown>);\n }\n\n return nested;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,wBAAwB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;;;;;;;;;;;;;;;;;;AAsBA,IAAa,sBAAsB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,gBACZ,QACA,WACuB;CACvB,MAAM,OAAO,IAAI,IAAY,SAAS;CACtC,MAAM,MAA+B,CAAC;CACtC,MAAM,QAAiC,EAAE,GAAK,OAAO,SAAiD,CAAC,EAAG;CAE1G,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EAC/C,IAAI,QAAQ,SAAS;EACrB,IAAI,KAAK,IAAI,GAAG,GAAG,MAAM,OAAO;OAC3B,IAAI,OAAO;CACpB;CAEA,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,IAAI,QAAQ;CAC/C,OAAO;AACX;;;;;;AAOA,SAAgB,wBAAwB,YAA8D;CAClG,OAAO,gBAAgB,YAAY,qBAAqB;AAC5D;;;;;;;;;;;AAYA,SAAgB,sBAAsB,UAA4D;CAC9F,MAAM,SAAS,gBAAgB,UAAU,mBAAmB;CAE5D,MAAM,WAAW,OAAO;CACxB,IAAI,YAAY,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,QAAQ,GACnE,OAAO,aAAa,OAAO,YACvB,OAAO,QAAQ,QAAmC,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CACtE,KACA,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACpD,sBAAsB,KAAgC,IACtD,KACV,CAAC,CACL;CAGJ,MAAM,KAAK,OAAO;CAClB,IAAI,MAAM,QAAQ,EAAE,GAChB,OAAO,KAAK,GAAG,KAAI,UAAS,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAChF,sBAAsB,KAAgC,IACtD,KAAK;MACR,IAAI,MAAM,OAAO,OAAO,UAC3B,OAAO,KAAK,sBAAsB,EAA6B;CAGnE,OAAO;AACX"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Move presentation keys into the `admin` block.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The rule itself lives in `@rebasepro/types`, next to `ADMIN_COLLECTION_KEYS`,
|
|
5
|
+
* because `@rebasepro/admin-types` has to apply the identical one on the panel's
|
|
6
|
+
* side and this package cannot import that one. Two copies used to exist and
|
|
7
|
+
* they disagreed about precedence, which decided whether a presentation edit was
|
|
8
|
+
* saved or silently reverted to the value the user had just changed away from.
|
|
9
9
|
*/
|
|
10
10
|
export declare function nestAdminKeys(collectionData: Record<string, unknown>): Record<string, unknown>;
|
|
11
11
|
export declare class AstSchemaEditor {
|
|
@@ -22,10 +22,78 @@ export declare class AstSchemaEditor {
|
|
|
22
22
|
*/
|
|
23
23
|
private safePath;
|
|
24
24
|
private getCollectionFile;
|
|
25
|
+
/**
|
|
26
|
+
* Find the object literal a collection is declared with, through whatever
|
|
27
|
+
* wraps it.
|
|
28
|
+
*
|
|
29
|
+
* `defineCollection({ … })` is the shape `rebase init` writes and the one the
|
|
30
|
+
* docs recommend; `satisfies` / `as` / parentheses are the other ways an
|
|
31
|
+
* author can dress the same literal. Returning `null` for any of them meant
|
|
32
|
+
* the three callers below each failed differently, and the worst of them
|
|
33
|
+
* overwrote the file.
|
|
34
|
+
*/
|
|
35
|
+
private unwrapCollectionObject;
|
|
25
36
|
private getCollectionObject;
|
|
37
|
+
/**
|
|
38
|
+
* The collection's object literal, or a refusal that says what to do.
|
|
39
|
+
*
|
|
40
|
+
* Every caller needs this to be all-or-nothing: a missing object literal used
|
|
41
|
+
* to mean "throw", "report success and do nothing" and "recreate the file
|
|
42
|
+
* from scratch" depending on which method you called.
|
|
43
|
+
*/
|
|
44
|
+
private requireCollectionObject;
|
|
45
|
+
private unreadableFileMessage;
|
|
46
|
+
/** Look a key up on an object literal, quoted or not. */
|
|
47
|
+
private findProperty;
|
|
48
|
+
private static quoteKey;
|
|
49
|
+
private static isPlainObject;
|
|
26
50
|
private convertJsonToAstString;
|
|
51
|
+
/**
|
|
52
|
+
* Write only the keys the patch names, leaving every sibling alone.
|
|
53
|
+
*
|
|
54
|
+
* A patch says what changed, not what the collection is. The panel sends one
|
|
55
|
+
* — `{ propertiesOrder }` is what adding a column posts — and rewriting the
|
|
56
|
+
* `admin` block from it deleted the collection's icon, group, list columns
|
|
57
|
+
* and kanban config in the same write.
|
|
58
|
+
*/
|
|
59
|
+
private mergeIntoObjectLiteral;
|
|
27
60
|
saveProperty(collectionId: string, propertyKey: string, propertyConfig: Record<string, unknown>): Promise<void>;
|
|
28
61
|
deleteProperty(collectionId: string, propertyKey: string): Promise<void>;
|
|
29
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Write a collection back to its file.
|
|
64
|
+
*
|
|
65
|
+
* `partial` is the difference between "this is the collection" and "this is
|
|
66
|
+
* what changed about it". The panel sends both — a full save from the editor
|
|
67
|
+
* dialog, and a one-key patch whenever a property is added, deleted or
|
|
68
|
+
* reordered — and they cannot be told apart by looking at the payload. Read
|
|
69
|
+
* as a full save, a patch deletes everything it does not mention, including
|
|
70
|
+
* `securityRules`; the loader then hands the collection the directory
|
|
71
|
+
* default, which in the scaffold is `access: "public"`.
|
|
72
|
+
*/
|
|
73
|
+
saveCollection(collectionId: string, collectionData: Record<string, unknown>, options?: {
|
|
74
|
+
partial?: boolean;
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Write the collection-level `relations` array.
|
|
78
|
+
*
|
|
79
|
+
* Relations are the one key whose values are not all data: `target` is a
|
|
80
|
+
* thunk in the file, and the panel sends the target collection's slug (or,
|
|
81
|
+
* for a relation it did not touch, nothing at all — `JSON.stringify` drops
|
|
82
|
+
* the function). So each entry's target is resolved in that order: a slug
|
|
83
|
+
* becomes `() => xCollection` plus the import it needs, and anything else
|
|
84
|
+
* falls back to the thunk already in the file.
|
|
85
|
+
*
|
|
86
|
+
* This used to be a `continue` with a comment saying relations were handled
|
|
87
|
+
* elsewhere. Nothing handled them; every edit in the Relations tab was
|
|
88
|
+
* dropped without a word.
|
|
89
|
+
*/
|
|
90
|
+
private writeRelations;
|
|
91
|
+
/**
|
|
92
|
+
* `() => targetCollection` for a target named by its slug, importing it if
|
|
93
|
+
* the file does not already.
|
|
94
|
+
*/
|
|
95
|
+
private targetThunk;
|
|
96
|
+
private hasDefaultImport;
|
|
97
|
+
private getDefaultExportName;
|
|
30
98
|
deleteCollection(collectionId: string): Promise<void>;
|
|
31
99
|
}
|
package/dist/api/errors.d.ts
CHANGED
|
@@ -17,6 +17,18 @@ export declare class ApiError extends Error {
|
|
|
17
17
|
* The motivating case is `POST /auth/refresh` with no session: clients
|
|
18
18
|
* refresh on page load before they know whether one exists, so every
|
|
19
19
|
* anonymous page view is a 401 — correct, and not worth a warning line.
|
|
20
|
+
*
|
|
21
|
+
* The other class is a caller-caused 4xx that never reached the database: a
|
|
22
|
+
* mistyped filter operator, sort direction or limit, a request for a
|
|
23
|
+
* collection that does not exist. Nothing on this server is wrong, and the
|
|
24
|
+
* response body has already told the caller what to fix — while one client
|
|
25
|
+
* holding a stale name would otherwise write a warning per request, forever,
|
|
26
|
+
* until the level means nothing. See `api/rest/query-parser.ts`.
|
|
27
|
+
*
|
|
28
|
+
* What stays at warn is anything that says something about the *server*:
|
|
29
|
+
* a schema that has drifted from the code, a permission the database
|
|
30
|
+
* refused, a dependency that failed. Those are 4xx too, and they are still
|
|
31
|
+
* incidents.
|
|
20
32
|
*/
|
|
21
33
|
readonly expected: boolean;
|
|
22
34
|
constructor(statusCode: number, code: string, message: string, details?: unknown, expected?: boolean);
|
|
@@ -32,6 +32,31 @@ export declare class RestApiGenerator {
|
|
|
32
32
|
* Generate REST routes using existing DataDriver
|
|
33
33
|
*/
|
|
34
34
|
generateRoutes(): Hono<HonoEnv>;
|
|
35
|
+
/**
|
|
36
|
+
* Answer anything left under `/api/data` in the canonical error envelope.
|
|
37
|
+
*
|
|
38
|
+
* Without this, `GET /api/data/nonexistent` fell through to Hono's default
|
|
39
|
+
* handler and came back as the plain text `404 Not Found` — the one error
|
|
40
|
+
* on the data API that is not `{"error":{"message","code","requestId"}}`.
|
|
41
|
+
* A client that does `res.json()` on the error path got a parse failure
|
|
42
|
+
* where every other 4xx hands it a code, so a mistyped collection name
|
|
43
|
+
* surfaced as "invalid JSON" rather than "no such collection".
|
|
44
|
+
*
|
|
45
|
+
* Registered after every real route, so a request reaches it only when the
|
|
46
|
+
* slug is unknown *or* the method/sub-path is: those are different
|
|
47
|
+
* mistakes, and the message says which. The collection list is deliberately
|
|
48
|
+
* not enumerated — an unauthenticated request to this same URL is answered
|
|
49
|
+
* 401 before routing, on purpose, and echoing the full set of slugs back
|
|
50
|
+
* would undo that for any signed-in caller.
|
|
51
|
+
*
|
|
52
|
+
* Every answer here is `expected`, so it logs at debug (see
|
|
53
|
+
* {@link ApiError.expected}). This path used to log *nothing at all* —
|
|
54
|
+
* Hono's default 404 never reaches `errorHandler` — so routing it through
|
|
55
|
+
* the handler at warn would have traded a missing error envelope for a
|
|
56
|
+
* `⚠️` line on every request from any frontend holding a stale slug. The
|
|
57
|
+
* envelope is the fix; the log volume is not part of it.
|
|
58
|
+
*/
|
|
59
|
+
private createUnmatchedRoute;
|
|
35
60
|
/**
|
|
36
61
|
* Check API key permissions for a collection operation.
|
|
37
62
|
* Throws 403 if the key doesn't have the required permission.
|
|
@@ -54,7 +79,7 @@ export declare class RestApiGenerator {
|
|
|
54
79
|
* Needed so a nested write can be checked against a schema at all. Without
|
|
55
80
|
* it these routes skipped `assertKnownWriteFields` entirely, which is why a
|
|
56
81
|
* typo `POST /posts` rejected with a 400 while the same typo on
|
|
57
|
-
* `POST /authors/1/posts`
|
|
82
|
+
* `POST /authors/1/posts` was dropped from the statement and answered 201.
|
|
58
83
|
*
|
|
59
84
|
* Returns `undefined` rather than throwing when the path cannot be walked:
|
|
60
85
|
* the driver raises the authoritative error a moment later, and duplicating
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { DataDriver } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* What a key was claimed *for*, so presenting it on a different request is
|
|
4
|
+
* caught instead of silently replaying the first one's answer.
|
|
5
|
+
*
|
|
6
|
+
* Nothing about the request used to enter the key, so `createMany(rows, { key })`
|
|
7
|
+
* followed by `deleteMany(ids, { key })` — the reusable business id the docs
|
|
8
|
+
* recommended — answered the delete with the create's `200` body and deleted
|
|
9
|
+
* nothing. Method and path separate the routes; the body separates a re-run
|
|
10
|
+
* carrying corrected rows from the retry of an unanswered one, which is the
|
|
11
|
+
* difference between "here is your earlier answer" and silently discarding a
|
|
12
|
+
* correction.
|
|
13
|
+
*/
|
|
14
|
+
export declare function requestFingerprint(method: string, path: string, body: unknown): string;
|
|
2
15
|
/**
|
|
3
16
|
* What a caller may do with a key.
|
|
4
17
|
*
|
|
@@ -20,6 +33,10 @@ export type IdempotencyClaim =
|
|
|
20
33
|
| {
|
|
21
34
|
status: "in-flight";
|
|
22
35
|
}
|
|
36
|
+
/** This key is live, but it was claimed for a different request. */
|
|
37
|
+
| {
|
|
38
|
+
status: "mismatch";
|
|
39
|
+
}
|
|
23
40
|
/** The key is ours; do the write, then `complete` or `release` it. */
|
|
24
41
|
| {
|
|
25
42
|
status: "claimed";
|
|
@@ -29,16 +46,20 @@ export interface IdempotencyStore {
|
|
|
29
46
|
* Take the key if it is free, atomically. Never throws — a store that
|
|
30
47
|
* cannot answer reports `claimed`, which degrades to no idempotency rather
|
|
31
48
|
* than to a refused write.
|
|
49
|
+
*
|
|
50
|
+
* `fingerprint` says what the key is being claimed for (see
|
|
51
|
+
* {@link requestFingerprint}); a live key presented with a different one is
|
|
52
|
+
* a reused key, not a replay, and is reported as `mismatch`.
|
|
32
53
|
*/
|
|
33
|
-
claim(key: string, uid: string | undefined): Promise<IdempotencyClaim>;
|
|
54
|
+
claim(key: string, uid: string | undefined, fingerprint: string): Promise<IdempotencyClaim>;
|
|
34
55
|
/** Record what this key answered. Never throws. */
|
|
35
56
|
complete(key: string, uid: string | undefined, response: unknown): Promise<void>;
|
|
36
57
|
/**
|
|
37
58
|
* Give the key back after the write it was claimed for failed.
|
|
38
59
|
*
|
|
39
|
-
* Without this a transient failure strands the key
|
|
40
|
-
*
|
|
41
|
-
*
|
|
60
|
+
* Without this a transient failure strands the key until its lease runs
|
|
61
|
+
* out, turning one failed write into a minute of refused retries. The lease
|
|
62
|
+
* is the floor, not the mechanism: it only covers the exits no code runs on.
|
|
42
63
|
*/
|
|
43
64
|
release(key: string, uid: string | undefined): Promise<void>;
|
|
44
65
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ListLimitBounds } from "@rebasepro/types";
|
|
1
2
|
import { QueryOptions } from "../types";
|
|
2
3
|
export declare const mapOperator: (op: string) => import("@rebasepro/types").WhereFilterOp | null;
|
|
3
4
|
export { DEFAULT_LIST_LIMIT, DEFAULT_VECTOR_LIST_LIMIT, MAX_LIST_LIMIT } from "@rebasepro/types";
|
|
@@ -13,9 +14,22 @@ export interface ListLimitOptions {
|
|
|
13
14
|
* text-search reads — a vector search falls back to its own default (10).
|
|
14
15
|
*/
|
|
15
16
|
defaultLimit?: number;
|
|
16
|
-
/**
|
|
17
|
+
/** Largest `?limit` a client may ask for. A larger one is a 400, not a clamp. */
|
|
17
18
|
maxLimit?: number;
|
|
18
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* {@link resolveClientListLimit} for an HTTP route: the same bounds, answered
|
|
22
|
+
* with a 400 rather than a 500.
|
|
23
|
+
*
|
|
24
|
+
* The shared resolver throws a `ListLimitError`, which carries `status` — but
|
|
25
|
+
* the Hono error handler discriminates on `statusCode`, so an unconverted one
|
|
26
|
+
* reaches the client as `INTERNAL_ERROR` with its message stripped, telling the
|
|
27
|
+
* caller nothing about the parameter it got wrong. Every REST list ingress
|
|
28
|
+
* routes its `limit` through here so all of them name the ceiling the same way.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveListLimitParam(rawLimit: number | string | null | undefined, opts?: ListLimitBounds & {
|
|
31
|
+
vectorSearch?: boolean;
|
|
32
|
+
}): number;
|
|
19
33
|
/**
|
|
20
34
|
* Parse query parameters into QueryOptions
|
|
21
35
|
*/
|
|
@@ -2,10 +2,18 @@ import { CollectionConfig } from "@rebasepro/types";
|
|
|
2
2
|
/**
|
|
3
3
|
* Reject a write naming a field the collection does not have.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* An unknown key does *not* reach the INSERT and come back as `column "titel"
|
|
6
|
+
* does not exist`, which is what this comment used to claim. Drizzle builds
|
|
7
|
+
* INSERT and UPDATE from the table's own column list, so a key the table does
|
|
8
|
+
* not carry is left out of the statement: the write answers 201 having stored
|
|
9
|
+
* nothing under that name. A typo is a request problem, it belongs in a 400,
|
|
10
|
+
* and nothing below this was ever going to raise one.
|
|
11
|
+
*
|
|
12
|
+
* This is the config-level check and it is skipped on several paths (see the
|
|
13
|
+
* `strictWrites` bail below, and the auth-adapter contract at the call site).
|
|
14
|
+
* `assertWritableColumns` in the Postgres driver is the backstop every write
|
|
15
|
+
* passes through, including in-process `rebase.data` writes that never come
|
|
16
|
+
* near this layer.
|
|
9
17
|
*
|
|
10
18
|
* What counts as known:
|
|
11
19
|
* - a declared property (for an introspected BaaS collection these *are* the
|
|
@@ -20,6 +28,34 @@ export declare function assertKnownWriteFields(values: Record<string, unknown>,
|
|
|
20
28
|
rowIndex?: number;
|
|
21
29
|
extraKnownFields?: readonly string[];
|
|
22
30
|
}): void;
|
|
31
|
+
/**
|
|
32
|
+
* Reject a write whose values break the constraints the collection declares.
|
|
33
|
+
*
|
|
34
|
+
* `validation.min`, `max`, `matches`, `positive`, `integer` and the date and
|
|
35
|
+
* array bounds were read by exactly three things: the DDL generators (for
|
|
36
|
+
* `integer` and for a `varchar` width), and the admin's client-side form. No
|
|
37
|
+
* layer on the write path looked at them, no `CHECK` constraint was emitted,
|
|
38
|
+
* and the generated OpenAPI published every one of them as `minimum`,
|
|
39
|
+
* `maximum`, `minLength`, `maxLength` and `pattern`. So the docs' own example —
|
|
40
|
+
* `price: { type: "number", validation: { min: 0 } }` — accepted `-5000` with a
|
|
41
|
+
* 201 while the spec said it would not, and any client or gateway generated
|
|
42
|
+
* from that spec believed the server was checking.
|
|
43
|
+
*
|
|
44
|
+
* Only `required` is left to the database, where it is a NOT NULL: a partial
|
|
45
|
+
* update legitimately omits keys, and this function sees one request's values,
|
|
46
|
+
* not the row they land on.
|
|
47
|
+
*
|
|
48
|
+
* A value of the wrong *type* is not this function's business — that reaches
|
|
49
|
+
* Postgres, which types every column and gives a 400 through the driver's
|
|
50
|
+
* SQLSTATE mapping. Constraints are checked against values of the right type,
|
|
51
|
+
* plus the numeric-string form a JSON body may carry for a number column.
|
|
52
|
+
*
|
|
53
|
+
* Every failing field is reported at once. A form that fixes one field per
|
|
54
|
+
* round trip is the reason validation errors get ignored.
|
|
55
|
+
*/
|
|
56
|
+
export declare function assertWriteValuesValid(values: Record<string, unknown>, collection: CollectionConfig, options?: {
|
|
57
|
+
rowIndex?: number;
|
|
58
|
+
}): void;
|
|
23
59
|
/**
|
|
24
60
|
* Narrow response rows to the fields the caller asked for.
|
|
25
61
|
*
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* 1. Checks for API key tokens (`rk_` prefix) first — these are Rebase-level
|
|
10
10
|
* 2. Falls back to `adapter.verifyRequest(request)` to resolve the user
|
|
11
11
|
* 3. Scopes the DataDriver via `withAuth()` for RLS
|
|
12
|
-
* 4.
|
|
12
|
+
* 4. Rejects a presented-but-unverifiable token with 401, whatever
|
|
13
|
+
* `requireAuth` says
|
|
14
|
+
* 5. Enforces auth (401) when `requireAuth` is true and no user is found
|
|
13
15
|
*
|
|
14
16
|
* The behavior is identical to `createAuthMiddleware()` — only the
|
|
15
17
|
* token verification strategy is pluggable.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which role names carry administrative privilege.
|
|
3
|
+
*
|
|
4
|
+
* One definition, because there used to be several and they disagreed.
|
|
5
|
+
* `requireAdmin` accepted `admin` **or** `schema-admin`; the guard that refuses
|
|
6
|
+
* a dangerous `defaultRole` compared against `admin` alone. So
|
|
7
|
+
* `AUTH_DEFAULT_ROLE=schema-admin` passed the guard and made every public
|
|
8
|
+
* registrant an administrator — and, since a `schema-admin` may edit users, one
|
|
9
|
+
* of them could then grant themselves real `admin` and keep it.
|
|
10
|
+
*
|
|
11
|
+
* The guard and the check have to read the same list or the gap comes back, so
|
|
12
|
+
* neither of them owns it.
|
|
13
|
+
*
|
|
14
|
+
* `schema-admin` is here deliberately: it can reach the schema editor and the
|
|
15
|
+
* SQL surfaces, which is administrative by any definition that matters. If a
|
|
16
|
+
* genuinely lesser role is added later, it does not belong in this list — it
|
|
17
|
+
* belongs in a capability check of its own.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ADMINISTRATIVE_ROLES: readonly ["admin", "schema-admin"];
|
|
20
|
+
export type AdministrativeRole = (typeof ADMINISTRATIVE_ROLES)[number];
|
|
21
|
+
/** Does this single role name carry administrative privilege? */
|
|
22
|
+
export declare function isAdministrativeRole(role: string): role is AdministrativeRole;
|
|
23
|
+
/** Does any role in this list carry administrative privilege? */
|
|
24
|
+
export declare function hasAdministrativeRole(roles: readonly string[] | null | undefined): boolean;
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
3
|
+
/** Extra field Apple's client sends that no other provider has. */
|
|
4
|
+
export interface AppleCodeFlowPayload extends OAuthCodeFlowPayload {
|
|
5
|
+
/**
|
|
6
|
+
* Apple sends the user's *name* only on the first authorization, so the
|
|
7
|
+
* frontend has to forward it. It is display data and nothing else — the
|
|
8
|
+
* identity email comes from the id_token, never from the request body.
|
|
9
|
+
*/
|
|
10
|
+
user?: {
|
|
11
|
+
name?: {
|
|
12
|
+
firstName?: string;
|
|
13
|
+
lastName?: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}
|
|
2
17
|
/**
|
|
3
18
|
* Creates an Apple Sign In OAuth Provider integration.
|
|
4
19
|
*
|
|
@@ -17,14 +32,4 @@ export declare function createAppleProvider(config: {
|
|
|
17
32
|
keyId: string;
|
|
18
33
|
/** The raw PEM contents of the .p8 private key file */
|
|
19
34
|
privateKey: string;
|
|
20
|
-
}): OAuthProvider<
|
|
21
|
-
code: string;
|
|
22
|
-
redirectUri: string;
|
|
23
|
-
user?: {
|
|
24
|
-
name?: {
|
|
25
|
-
firstName?: string;
|
|
26
|
-
lastName?: string;
|
|
27
|
-
};
|
|
28
|
-
email?: string;
|
|
29
|
-
};
|
|
30
|
-
}>;
|
|
35
|
+
}): OAuthProvider<AppleCodeFlowPayload>;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a Bitbucket OAuth Provider integration (OAuth 2.0 consumer).
|
|
4
5
|
*/
|
|
5
6
|
export declare function createBitbucketProvider(config: {
|
|
6
7
|
clientId: string;
|
|
7
8
|
clientSecret: string;
|
|
8
|
-
}): OAuthProvider<
|
|
9
|
-
code: string;
|
|
10
|
-
redirectUri: string;
|
|
11
|
-
}>;
|
|
9
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|
|
@@ -39,12 +39,23 @@ export interface BuiltinAuthAdapterConfig {
|
|
|
39
39
|
* wiring path a real backend uses).
|
|
40
40
|
*/
|
|
41
41
|
disableSelfRegistration?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Opt-in: allow `POST /auth/anonymous` to mint a user without credentials.
|
|
44
|
+
*
|
|
45
|
+
* Off by default, and overridden by `disableSelfRegistration`. Anonymous
|
|
46
|
+
* sign-in inserts a `users` row and assigns `defaultRole` exactly as
|
|
47
|
+
* registration does, so before this key existed a closed backend still
|
|
48
|
+
* handed out permanent accounts via `/auth/anonymous` + `/auth/anonymous/link`.
|
|
49
|
+
*/
|
|
50
|
+
allowAnonymous?: boolean;
|
|
42
51
|
/** Whether to expose the authenticated email→minimal-profile lookup route. */
|
|
43
52
|
allowUserLookup?: boolean;
|
|
44
53
|
/** Default role to assign to new users. */
|
|
45
54
|
defaultRole?: string;
|
|
46
55
|
/** OAuth providers to register. */
|
|
47
56
|
oauthProviders?: OAuthProvider<unknown>[];
|
|
57
|
+
/** Redirect URIs the OAuth routes accept, for every provider. */
|
|
58
|
+
allowedRedirectUris?: string[];
|
|
48
59
|
/** Static service key for server-to-server auth. */
|
|
49
60
|
serviceKey?: string;
|
|
50
61
|
/** Auth hooks for customizing password, credentials, lifecycle, etc. */
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a Discord OAuth2 Provider integration.
|
|
4
5
|
*
|
|
5
|
-
* Uses the authorization code flow
|
|
6
|
-
*
|
|
6
|
+
* Uses the authorization code flow (with PKCE when the client supplies a
|
|
7
|
+
* verifier). Requires the "identify" and "email" scopes to retrieve the
|
|
8
|
+
* user's email and profile information.
|
|
7
9
|
*/
|
|
8
10
|
export declare function createDiscordProvider(config: {
|
|
9
11
|
clientId: string;
|
|
10
12
|
clientSecret: string;
|
|
11
|
-
}): OAuthProvider<
|
|
12
|
-
code: string;
|
|
13
|
-
redirectUri: string;
|
|
14
|
-
}>;
|
|
13
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a Facebook / Meta OAuth Provider integration.
|
|
4
5
|
*
|
|
@@ -8,7 +9,4 @@ import type { OAuthProvider } from "./interfaces";
|
|
|
8
9
|
export declare function createFacebookProvider(config: {
|
|
9
10
|
clientId: string;
|
|
10
11
|
clientSecret: string;
|
|
11
|
-
}): OAuthProvider<
|
|
12
|
-
code: string;
|
|
13
|
-
redirectUri: string;
|
|
14
|
-
}>;
|
|
12
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a GitHub OAuth Provider integration.
|
|
4
5
|
*
|
|
@@ -9,7 +10,4 @@ import type { OAuthProvider } from "./interfaces";
|
|
|
9
10
|
export declare function createGitHubProvider(config: {
|
|
10
11
|
clientId: string;
|
|
11
12
|
clientSecret: string;
|
|
12
|
-
}): OAuthProvider<
|
|
13
|
-
code: string;
|
|
14
|
-
redirectUri: string;
|
|
15
|
-
}>;
|
|
13
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a GitLab OAuth Provider integration.
|
|
4
5
|
* Works with both GitLab.com and self-hosted instances.
|
|
6
|
+
*
|
|
7
|
+
* `baseUrl` is configuration only and must stay that way: a caller-supplied
|
|
8
|
+
* instance URL would make this provider an SSRF primitive and an
|
|
9
|
+
* arbitrary-identity oracle in one step.
|
|
5
10
|
*/
|
|
6
11
|
export declare function createGitLabProvider(config: {
|
|
7
12
|
clientId: string;
|
|
8
13
|
clientSecret: string;
|
|
9
14
|
baseUrl?: string;
|
|
10
|
-
}): OAuthProvider<
|
|
11
|
-
code: string;
|
|
12
|
-
redirectUri: string;
|
|
13
|
-
}>;
|
|
15
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -21,6 +21,13 @@ export { createGitLabProvider } from "./gitlab-oauth";
|
|
|
21
21
|
export { createBitbucketProvider } from "./bitbucket-oauth";
|
|
22
22
|
export { createSlackProvider } from "./slack-oauth";
|
|
23
23
|
export { createSpotifyProvider } from "./spotify-oauth";
|
|
24
|
+
export type { AppleCodeFlowPayload } from "./apple-oauth";
|
|
25
|
+
export { oauthCodeFlowSchema, pkceTokenParams, providerVerifiedEmail } from "./oauth-code-flow";
|
|
26
|
+
export type { OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
27
|
+
export { verifyOidcIdToken, tryVerifyOidcIdToken } from "./oidc-id-token";
|
|
28
|
+
export type { OidcIdTokenClaims, VerifyOidcIdTokenOptions } from "./oidc-id-token";
|
|
29
|
+
export { decideOAuthAutoLink, isRedirectUriAllowed } from "./oauth-signin-policy";
|
|
30
|
+
export type { AutoLinkDecision, AutoLinkRefusal } from "./oauth-signin-policy";
|
|
24
31
|
export { requireAuth, requireAdmin, optionalAuth, extractUserFromToken, createAuthMiddleware, queryTokenAuth, fileTokenAuth } from "./middleware";
|
|
25
32
|
export type { AuthMiddlewareOptions, AuthResult } from "./middleware";
|
|
26
33
|
export { createAuthRoutes } from "./routes";
|