node-type-registry 0.14.0 → 0.17.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/blueprint-types.generated.d.ts +2 -0
- package/codegen/generate-types.js +12 -2
- package/esm/blueprint-types.generated.d.ts +2 -0
- package/esm/codegen/generate-types.js +12 -2
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/module-presets/auth-email-magic.d.ts +15 -0
- package/esm/module-presets/auth-email-magic.js +55 -0
- package/esm/module-presets/auth-email.d.ts +17 -0
- package/esm/module-presets/auth-email.js +69 -0
- package/esm/module-presets/auth-hardened.d.ts +10 -0
- package/esm/module-presets/auth-hardened.js +65 -0
- package/esm/module-presets/auth-passkey.d.ts +14 -0
- package/esm/module-presets/auth-passkey.js +56 -0
- package/esm/module-presets/auth-sso.d.ts +21 -0
- package/esm/module-presets/auth-sso.js +65 -0
- package/esm/module-presets/b2b.d.ts +14 -0
- package/esm/module-presets/b2b.js +83 -0
- package/esm/module-presets/full.d.ts +15 -0
- package/esm/module-presets/full.js +38 -0
- package/esm/module-presets/index.d.ts +18 -0
- package/esm/module-presets/index.js +27 -0
- package/esm/module-presets/minimal.d.ts +14 -0
- package/esm/module-presets/minimal.js +48 -0
- package/esm/module-presets/types.d.ts +60 -0
- package/esm/module-presets/types.js +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/module-presets/auth-email-magic.d.ts +15 -0
- package/module-presets/auth-email-magic.js +58 -0
- package/module-presets/auth-email.d.ts +17 -0
- package/module-presets/auth-email.js +72 -0
- package/module-presets/auth-hardened.d.ts +10 -0
- package/module-presets/auth-hardened.js +68 -0
- package/module-presets/auth-passkey.d.ts +14 -0
- package/module-presets/auth-passkey.js +59 -0
- package/module-presets/auth-sso.d.ts +21 -0
- package/module-presets/auth-sso.js +68 -0
- package/module-presets/b2b.d.ts +14 -0
- package/module-presets/b2b.js +86 -0
- package/module-presets/full.d.ts +15 -0
- package/module-presets/full.js +41 -0
- package/module-presets/index.d.ts +18 -0
- package/module-presets/index.js +38 -0
- package/module-presets/minimal.d.ts +14 -0
- package/module-presets/minimal.js +51 -0
- package/module-presets/types.d.ts +60 -0
- package/module-presets/types.js +2 -0
- package/package.json +2 -2
|
@@ -781,6 +781,8 @@ export type BlueprintRelation = {
|
|
|
781
781
|
target_table: string;
|
|
782
782
|
source_schema_name?: string;
|
|
783
783
|
target_schema_name?: string;
|
|
784
|
+
/** Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag. */ source_field: string;
|
|
785
|
+
/** Name of the geometry/geography column on target_table that the predicate is evaluated against. */ target_field: string;
|
|
784
786
|
} & Partial<RelationSpatialParams>;
|
|
785
787
|
/** A table definition within a blueprint. */
|
|
786
788
|
export interface BlueprintTable {
|
|
@@ -352,13 +352,23 @@ function buildNodeTypes(dataNodes) {
|
|
|
352
352
|
// ---------------------------------------------------------------------------
|
|
353
353
|
function buildRelationTypes(relationNodes) {
|
|
354
354
|
const relationMembers = relationNodes.map((nt) => {
|
|
355
|
-
const
|
|
355
|
+
const baseMembers = [
|
|
356
356
|
requiredProp('$type', strLit(nt.name)),
|
|
357
357
|
requiredProp('source_table', t.tsStringKeyword()),
|
|
358
358
|
requiredProp('target_table', t.tsStringKeyword()),
|
|
359
359
|
optionalProp('source_schema_name', t.tsStringKeyword()),
|
|
360
360
|
optionalProp('target_schema_name', t.tsStringKeyword()),
|
|
361
|
-
]
|
|
361
|
+
];
|
|
362
|
+
// RelationSpatial is the only relation type that references *existing*
|
|
363
|
+
// columns rather than creating FK/junction fields. Its blueprint JSON
|
|
364
|
+
// therefore needs source_field / target_field (column *names* resolved
|
|
365
|
+
// server-side by resolve_blueprint_field), which have no ID-space
|
|
366
|
+
// equivalent in parameter_schema. Surface them here so blueprint authors
|
|
367
|
+
// get autocomplete without a cast.
|
|
368
|
+
if (nt.name === 'RelationSpatial') {
|
|
369
|
+
baseMembers.push(addJSDoc(requiredProp('source_field', t.tsStringKeyword()), 'Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag.'), addJSDoc(requiredProp('target_field', t.tsStringKeyword()), 'Name of the geometry/geography column on target_table that the predicate is evaluated against.'));
|
|
370
|
+
}
|
|
371
|
+
const baseType = t.tsTypeLiteral(baseMembers);
|
|
362
372
|
return t.tsIntersectionType([
|
|
363
373
|
baseType,
|
|
364
374
|
partialOf(t.tsTypeReference(t.identifier(`${nt.name}Params`))),
|
|
@@ -781,6 +781,8 @@ export type BlueprintRelation = {
|
|
|
781
781
|
target_table: string;
|
|
782
782
|
source_schema_name?: string;
|
|
783
783
|
target_schema_name?: string;
|
|
784
|
+
/** Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag. */ source_field: string;
|
|
785
|
+
/** Name of the geometry/geography column on target_table that the predicate is evaluated against. */ target_field: string;
|
|
784
786
|
} & Partial<RelationSpatialParams>;
|
|
785
787
|
/** A table definition within a blueprint. */
|
|
786
788
|
export interface BlueprintTable {
|
|
@@ -317,13 +317,23 @@ function buildNodeTypes(dataNodes) {
|
|
|
317
317
|
// ---------------------------------------------------------------------------
|
|
318
318
|
function buildRelationTypes(relationNodes) {
|
|
319
319
|
const relationMembers = relationNodes.map((nt) => {
|
|
320
|
-
const
|
|
320
|
+
const baseMembers = [
|
|
321
321
|
requiredProp('$type', strLit(nt.name)),
|
|
322
322
|
requiredProp('source_table', t.tsStringKeyword()),
|
|
323
323
|
requiredProp('target_table', t.tsStringKeyword()),
|
|
324
324
|
optionalProp('source_schema_name', t.tsStringKeyword()),
|
|
325
325
|
optionalProp('target_schema_name', t.tsStringKeyword()),
|
|
326
|
-
]
|
|
326
|
+
];
|
|
327
|
+
// RelationSpatial is the only relation type that references *existing*
|
|
328
|
+
// columns rather than creating FK/junction fields. Its blueprint JSON
|
|
329
|
+
// therefore needs source_field / target_field (column *names* resolved
|
|
330
|
+
// server-side by resolve_blueprint_field), which have no ID-space
|
|
331
|
+
// equivalent in parameter_schema. Surface them here so blueprint authors
|
|
332
|
+
// get autocomplete without a cast.
|
|
333
|
+
if (nt.name === 'RelationSpatial') {
|
|
334
|
+
baseMembers.push(addJSDoc(requiredProp('source_field', t.tsStringKeyword()), 'Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag.'), addJSDoc(requiredProp('target_field', t.tsStringKeyword()), 'Name of the geometry/geography column on target_table that the predicate is evaluated against.'));
|
|
335
|
+
}
|
|
336
|
+
const baseType = t.tsTypeLiteral(baseMembers);
|
|
327
337
|
return t.tsIntersectionType([
|
|
328
338
|
baseType,
|
|
329
339
|
partialOf(t.tsTypeReference(t.identifier(`${nt.name}Params`))),
|
package/esm/index.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export * from './data';
|
|
|
4
4
|
export * from './relation';
|
|
5
5
|
export * from './view';
|
|
6
6
|
export * from './blueprint-types.generated';
|
|
7
|
+
export * from './module-presets';
|
|
7
8
|
import type { NodeTypeDefinition } from './types';
|
|
8
9
|
export declare const allNodeTypes: NodeTypeDefinition[];
|
package/esm/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from './data';
|
|
|
3
3
|
export * from './relation';
|
|
4
4
|
export * from './view';
|
|
5
5
|
export * from './blueprint-types.generated';
|
|
6
|
+
export * from './module-presets';
|
|
6
7
|
import * as authz from './authz';
|
|
7
8
|
import * as data from './data';
|
|
8
9
|
import * as relation from './relation';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ModulePreset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `auth:email+magic` — `auth:email` plus passwordless email flows.
|
|
4
|
+
*
|
|
5
|
+
* Adds `session_secrets_module`, which is where one-time nonces for magic
|
|
6
|
+
* links and email OTPs are stored. Once installed, the `user_auth_module`
|
|
7
|
+
* emits `sign_up_magic_link`, `sign_in_magic_link`, and `sign_in_email_otp`
|
|
8
|
+
* procedures (gated on the equivalent `allow_*` toggles in
|
|
9
|
+
* `app_settings_auth`).
|
|
10
|
+
*
|
|
11
|
+
* Choose this over `auth:email` when you want users to be able to log in
|
|
12
|
+
* without ever setting a password — but still only over email (no SMS, no
|
|
13
|
+
* SSO).
|
|
14
|
+
*/
|
|
15
|
+
export declare const PresetAuthEmailMagic: ModulePreset;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `auth:email+magic` — `auth:email` plus passwordless email flows.
|
|
3
|
+
*
|
|
4
|
+
* Adds `session_secrets_module`, which is where one-time nonces for magic
|
|
5
|
+
* links and email OTPs are stored. Once installed, the `user_auth_module`
|
|
6
|
+
* emits `sign_up_magic_link`, `sign_in_magic_link`, and `sign_in_email_otp`
|
|
7
|
+
* procedures (gated on the equivalent `allow_*` toggles in
|
|
8
|
+
* `app_settings_auth`).
|
|
9
|
+
*
|
|
10
|
+
* Choose this over `auth:email` when you want users to be able to log in
|
|
11
|
+
* without ever setting a password — but still only over email (no SMS, no
|
|
12
|
+
* SSO).
|
|
13
|
+
*/
|
|
14
|
+
export const PresetAuthEmailMagic = {
|
|
15
|
+
name: 'auth:email+magic',
|
|
16
|
+
display_name: 'Email + Magic Link / OTP',
|
|
17
|
+
summary: 'Everything in `auth:email` plus magic-link and email-OTP passwordless flows.',
|
|
18
|
+
description: 'Same password-based auth as `auth:email`, with `session_secrets_module` added so the ' +
|
|
19
|
+
'generator emits the passwordless procedures: `sign_up_magic_link`, `sign_in_magic_link`, ' +
|
|
20
|
+
'`sign_in_email_otp`. Password flows still exist — you opt into passwordless-only by ' +
|
|
21
|
+
'flipping the `allow_password_sign_*` toggles off in `app_settings_auth` after install. ' +
|
|
22
|
+
"This is the right step up from `auth:email` when you want to ship magic links without yet " +
|
|
23
|
+
"taking on SSO or passkeys.",
|
|
24
|
+
good_for: [
|
|
25
|
+
'Consumer apps that want passwordless from day one',
|
|
26
|
+
'Apps targeting users who forget passwords (newsletters, one-off tools)',
|
|
27
|
+
'Hardening path from `auth:email` without jumping all the way to `auth:hardened`'
|
|
28
|
+
],
|
|
29
|
+
not_for: [
|
|
30
|
+
'Apps that need SSO or passkeys — use `auth:sso` or `auth:passkey`',
|
|
31
|
+
'Production at scale — use `auth:hardened` for rate limiting'
|
|
32
|
+
],
|
|
33
|
+
modules: [
|
|
34
|
+
'users_module',
|
|
35
|
+
'membership_types_module',
|
|
36
|
+
'memberships_module:app',
|
|
37
|
+
'sessions_module',
|
|
38
|
+
'secrets_module',
|
|
39
|
+
'encrypted_secrets_module',
|
|
40
|
+
'emails_module',
|
|
41
|
+
'rls_module',
|
|
42
|
+
'user_auth_module',
|
|
43
|
+
'session_secrets_module'
|
|
44
|
+
],
|
|
45
|
+
includes_notes: {
|
|
46
|
+
session_secrets_module: 'Stores nonces for magic-link and email-OTP flows. Without it those procedures are not emitted.'
|
|
47
|
+
},
|
|
48
|
+
omits_notes: {
|
|
49
|
+
rate_limits_module: 'Same reasoning as `auth:email` — add later via `auth:hardened`.',
|
|
50
|
+
connected_accounts_module: 'No OAuth / SSO in this preset.',
|
|
51
|
+
webauthn_credentials_module: 'No passkeys — add `auth:passkey`.',
|
|
52
|
+
phone_numbers_module: 'No SMS — add `auth:hardened`.'
|
|
53
|
+
},
|
|
54
|
+
extends: ['auth:email']
|
|
55
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ModulePreset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `auth:email` — email + password sign_up/sign_in. No orgs, no SSO, no SMS,
|
|
4
|
+
* no passkeys, no rate limits.
|
|
5
|
+
*
|
|
6
|
+
* This is the "working consumer login in one step" preset. It installs the
|
|
7
|
+
* `user_auth_module` and all the tables its insert trigger hard-requires,
|
|
8
|
+
* giving you the standard procedures: `sign_up`, `sign_in`, `sign_out`,
|
|
9
|
+
* `set_password`, `reset_password`, `forgot_password`, `verify_email`,
|
|
10
|
+
* `delete_account`, `my_sessions`, API-key CRUD. Nothing more.
|
|
11
|
+
*
|
|
12
|
+
* It deliberately excludes rate limits, connected accounts / identity
|
|
13
|
+
* providers (OAuth), WebAuthn (passkeys), phone numbers (SMS), invites,
|
|
14
|
+
* permissions, and org-scoped memberships. Bolt those on by moving to a
|
|
15
|
+
* richer preset (`auth:hardened`, `b2b`) when you actually need them.
|
|
16
|
+
*/
|
|
17
|
+
export declare const PresetAuthEmail: ModulePreset;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `auth:email` — email + password sign_up/sign_in. No orgs, no SSO, no SMS,
|
|
3
|
+
* no passkeys, no rate limits.
|
|
4
|
+
*
|
|
5
|
+
* This is the "working consumer login in one step" preset. It installs the
|
|
6
|
+
* `user_auth_module` and all the tables its insert trigger hard-requires,
|
|
7
|
+
* giving you the standard procedures: `sign_up`, `sign_in`, `sign_out`,
|
|
8
|
+
* `set_password`, `reset_password`, `forgot_password`, `verify_email`,
|
|
9
|
+
* `delete_account`, `my_sessions`, API-key CRUD. Nothing more.
|
|
10
|
+
*
|
|
11
|
+
* It deliberately excludes rate limits, connected accounts / identity
|
|
12
|
+
* providers (OAuth), WebAuthn (passkeys), phone numbers (SMS), invites,
|
|
13
|
+
* permissions, and org-scoped memberships. Bolt those on by moving to a
|
|
14
|
+
* richer preset (`auth:hardened`, `b2b`) when you actually need them.
|
|
15
|
+
*/
|
|
16
|
+
export const PresetAuthEmail = {
|
|
17
|
+
name: 'auth:email',
|
|
18
|
+
display_name: 'Email + Password',
|
|
19
|
+
summary: 'Standard email/password auth flow. No orgs, no SSO, no MFA, no rate limits.',
|
|
20
|
+
description: "Installs `user_auth_module` with exactly the table dependencies its insert trigger " +
|
|
21
|
+
"hard-requires: users, app-scoped memberships, emails, secrets, encrypted secrets, " +
|
|
22
|
+
"sessions, plus RLS. You get the standard password-based auth procedures (sign_up, " +
|
|
23
|
+
"sign_in, reset_password, verify_email, delete_account, ...) and that's it. " +
|
|
24
|
+
"Everything else in the module catalog — SSO, passkeys, SMS, rate limits, orgs, " +
|
|
25
|
+
"invites, permissions — is deliberately omitted. This is the right shape for single-tenant " +
|
|
26
|
+
"consumer apps in the first weeks, internal tools that need a real login, or anything " +
|
|
27
|
+
"where you want the lightest possible working auth and will add complexity only when " +
|
|
28
|
+
"forced to.",
|
|
29
|
+
good_for: [
|
|
30
|
+
'Single-tenant consumer apps in the first week of development',
|
|
31
|
+
'Internal tools where one simple login is enough',
|
|
32
|
+
'Demos and hobby projects that need real password auth',
|
|
33
|
+
'B2C SaaS before org/team features are needed'
|
|
34
|
+
],
|
|
35
|
+
not_for: [
|
|
36
|
+
'Apps with org/team/workspace structure — use `b2b`',
|
|
37
|
+
'Apps that need SSO or passkeys from day one — use `auth:sso` or `auth:passkey`',
|
|
38
|
+
'Production apps at scale — use `auth:hardened` (adds rate limits, SSO, passkeys, SMS)'
|
|
39
|
+
],
|
|
40
|
+
modules: [
|
|
41
|
+
'users_module',
|
|
42
|
+
'membership_types_module',
|
|
43
|
+
'memberships_module:app',
|
|
44
|
+
'sessions_module',
|
|
45
|
+
'secrets_module',
|
|
46
|
+
'encrypted_secrets_module',
|
|
47
|
+
'emails_module',
|
|
48
|
+
'rls_module',
|
|
49
|
+
'user_auth_module'
|
|
50
|
+
],
|
|
51
|
+
includes_notes: {
|
|
52
|
+
'memberships_module:app': 'Required by `user_auth_module`: every user gets an app-level membership row at sign-up.',
|
|
53
|
+
membership_types_module: "Required by `memberships_module:app`; defines the 'app' scope.",
|
|
54
|
+
emails_module: 'Required by the `user_auth_module` insert trigger (`RAISE EXCEPTION REQUIRES emails_module`).',
|
|
55
|
+
encrypted_secrets_module: 'Required for password hashing; referenced by `set_password`, `verify_password`, and reset flows.',
|
|
56
|
+
secrets_module: 'API-key storage (`create_api_key`, `revoke_api_key`, `my_api_keys`).'
|
|
57
|
+
},
|
|
58
|
+
omits_notes: {
|
|
59
|
+
rate_limits_module: 'Omitted intentionally; throttle_* helpers are null-safe and the auth procs compile without it. Add later via `auth:hardened`.',
|
|
60
|
+
connected_accounts_module: 'No OAuth / SSO in this preset — add `auth:sso`.',
|
|
61
|
+
identity_providers_module: 'No OAuth provider configs without connected_accounts.',
|
|
62
|
+
webauthn_credentials_module: 'No passkeys — add `auth:passkey`.',
|
|
63
|
+
phone_numbers_module: 'No SMS login — add `auth:hardened` or the SMS-only refactor path.',
|
|
64
|
+
'memberships_module:org': 'No org/team structure — move to `b2b` when you need one.',
|
|
65
|
+
'permissions_module:app': 'No fine-grained RBAC; the `is_admin` flag on users is the only gate.',
|
|
66
|
+
invites_module: 'Self-serve signup only.',
|
|
67
|
+
session_secrets_module: 'No magic-link / email-OTP nonces; add `auth:email+magic`.'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ModulePreset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `auth:hardened` — `auth:email` with rate limiting, SSO, passkeys, SMS,
|
|
4
|
+
* and magic-link / OTP infrastructure all installed. Production-ready
|
|
5
|
+
* consumer auth with the full identifier matrix.
|
|
6
|
+
*
|
|
7
|
+
* Still single-tenant (no orgs / teams / invites / permissions). For
|
|
8
|
+
* multi-tenant B2B, step up to `b2b`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const PresetAuthHardened: ModulePreset;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `auth:hardened` — `auth:email` with rate limiting, SSO, passkeys, SMS,
|
|
3
|
+
* and magic-link / OTP infrastructure all installed. Production-ready
|
|
4
|
+
* consumer auth with the full identifier matrix.
|
|
5
|
+
*
|
|
6
|
+
* Still single-tenant (no orgs / teams / invites / permissions). For
|
|
7
|
+
* multi-tenant B2B, step up to `b2b`.
|
|
8
|
+
*/
|
|
9
|
+
export const PresetAuthHardened = {
|
|
10
|
+
name: 'auth:hardened',
|
|
11
|
+
display_name: 'Hardened (all auth surfaces)',
|
|
12
|
+
summary: 'Rate limits + SSO + passkeys + SMS + magic links. Production-grade consumer auth.',
|
|
13
|
+
description: 'All of `auth:email`, plus every optional auth module that fits inside the single-tenant ' +
|
|
14
|
+
'model: `rate_limits_module` for throttling (protects sign-in, password reset, and ' +
|
|
15
|
+
'signup flows), `connected_accounts_module` + `identity_providers_module` for SSO, ' +
|
|
16
|
+
'`webauthn_credentials_module` + `webauthn_auth_module` for passkeys, ' +
|
|
17
|
+
'`session_secrets_module` for magic-link / email-OTP nonces, and ' +
|
|
18
|
+
'`phone_numbers_module` for SMS flows. Every login identifier is available; ' +
|
|
19
|
+
'toggle whichever ones you want off via `app_settings_auth.allow_*` columns. ' +
|
|
20
|
+
'Choose this for any production consumer app; step up to `b2b` once you need orgs.',
|
|
21
|
+
good_for: [
|
|
22
|
+
'Production consumer apps with a serious user base',
|
|
23
|
+
'Apps that need every identifier available (email, SSO, passkey, SMS) with throttling',
|
|
24
|
+
'Apps doing a progressive rollout of auth methods — everything is installed, you toggle per method'
|
|
25
|
+
],
|
|
26
|
+
not_for: [
|
|
27
|
+
'Hobby projects / demos — way too much infrastructure; use `auth:email`',
|
|
28
|
+
'Multi-tenant B2B apps — use `b2b`, which layers orgs + invites + permissions on top'
|
|
29
|
+
],
|
|
30
|
+
modules: [
|
|
31
|
+
'users_module',
|
|
32
|
+
'membership_types_module',
|
|
33
|
+
'memberships_module:app',
|
|
34
|
+
'sessions_module',
|
|
35
|
+
'secrets_module',
|
|
36
|
+
'encrypted_secrets_module',
|
|
37
|
+
'emails_module',
|
|
38
|
+
'rls_module',
|
|
39
|
+
'user_auth_module',
|
|
40
|
+
'session_secrets_module',
|
|
41
|
+
'rate_limits_module',
|
|
42
|
+
'connected_accounts_module',
|
|
43
|
+
'identity_providers_module',
|
|
44
|
+
'webauthn_credentials_module',
|
|
45
|
+
'webauthn_auth_module',
|
|
46
|
+
'phone_numbers_module'
|
|
47
|
+
],
|
|
48
|
+
includes_notes: {
|
|
49
|
+
rate_limits_module: 'Throttling for sign-in, password reset, sign-up, and IP-based gates.',
|
|
50
|
+
connected_accounts_module: 'OAuth / SSO linkage.',
|
|
51
|
+
identity_providers_module: 'OAuth provider configs (required for `connected_accounts_module`).',
|
|
52
|
+
webauthn_credentials_module: 'Per-user passkey storage.',
|
|
53
|
+
webauthn_auth_module: 'Passkey challenge + assertion runtime.',
|
|
54
|
+
session_secrets_module: 'Nonces for magic links, email OTP, and WebAuthn challenges.',
|
|
55
|
+
phone_numbers_module: 'SMS sign-in / MFA support.'
|
|
56
|
+
},
|
|
57
|
+
omits_notes: {
|
|
58
|
+
'memberships_module:org': 'No orgs / teams — use `b2b` when you need multi-tenancy.',
|
|
59
|
+
'permissions_module:app': 'No RBAC beyond the `is_admin` flag — add via `b2b`.',
|
|
60
|
+
invites_module: 'No invite flow — add via `b2b`.',
|
|
61
|
+
storage_module: 'Add separately if you need file uploads.',
|
|
62
|
+
crypto_addresses_module: 'Not a web3 preset; omit unless doing wallet sign-in.'
|
|
63
|
+
},
|
|
64
|
+
extends: ['auth:email', 'auth:email+magic', 'auth:sso', 'auth:passkey']
|
|
65
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModulePreset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `auth:passkey` — `auth:email` plus WebAuthn / passkeys.
|
|
4
|
+
*
|
|
5
|
+
* Adds `webauthn_credentials_module` (stores each user's registered public
|
|
6
|
+
* keys and credential IDs), `webauthn_auth_module` (the auth-time challenge
|
|
7
|
+
* storage + flow), and `session_secrets_module` (where the one-time
|
|
8
|
+
* challenge nonces live). The generator then emits WebAuthn registration
|
|
9
|
+
* and assertion procedures.
|
|
10
|
+
*
|
|
11
|
+
* Password flows stay on by default as a recovery path; toggle them off in
|
|
12
|
+
* `app_settings_auth` if you want strictly-passkey.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PresetAuthPasskey: ModulePreset;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `auth:passkey` — `auth:email` plus WebAuthn / passkeys.
|
|
3
|
+
*
|
|
4
|
+
* Adds `webauthn_credentials_module` (stores each user's registered public
|
|
5
|
+
* keys and credential IDs), `webauthn_auth_module` (the auth-time challenge
|
|
6
|
+
* storage + flow), and `session_secrets_module` (where the one-time
|
|
7
|
+
* challenge nonces live). The generator then emits WebAuthn registration
|
|
8
|
+
* and assertion procedures.
|
|
9
|
+
*
|
|
10
|
+
* Password flows stay on by default as a recovery path; toggle them off in
|
|
11
|
+
* `app_settings_auth` if you want strictly-passkey.
|
|
12
|
+
*/
|
|
13
|
+
export const PresetAuthPasskey = {
|
|
14
|
+
name: 'auth:passkey',
|
|
15
|
+
display_name: 'Passkeys (WebAuthn)',
|
|
16
|
+
summary: '`auth:email` plus WebAuthn passkey registration and assertion.',
|
|
17
|
+
description: "Installs the three modules WebAuthn needs: `webauthn_credentials_module` for each user's " +
|
|
18
|
+
"registered public keys, `webauthn_auth_module` for the runtime challenge/assertion flow, " +
|
|
19
|
+
"and `session_secrets_module` for the one-time challenge nonces. With these installed, " +
|
|
20
|
+
"the generator emits WebAuthn registration/login procs. Keep password flows as a recovery " +
|
|
21
|
+
"path, or disable them in `app_settings_auth` for passkey-only deployments.",
|
|
22
|
+
good_for: [
|
|
23
|
+
'Apps where you want users to adopt phishing-resistant auth',
|
|
24
|
+
'Consumer apps with a tech-forward audience',
|
|
25
|
+
'Internal tools protecting sensitive data where FIDO2 is a requirement'
|
|
26
|
+
],
|
|
27
|
+
not_for: [
|
|
28
|
+
'Apps that also need SSO or SMS — use `auth:hardened` for everything',
|
|
29
|
+
'Apps where the end-user device mix is heavy on old browsers that lack WebAuthn'
|
|
30
|
+
],
|
|
31
|
+
modules: [
|
|
32
|
+
'users_module',
|
|
33
|
+
'membership_types_module',
|
|
34
|
+
'memberships_module:app',
|
|
35
|
+
'sessions_module',
|
|
36
|
+
'secrets_module',
|
|
37
|
+
'encrypted_secrets_module',
|
|
38
|
+
'emails_module',
|
|
39
|
+
'rls_module',
|
|
40
|
+
'user_auth_module',
|
|
41
|
+
'session_secrets_module',
|
|
42
|
+
'webauthn_credentials_module',
|
|
43
|
+
'webauthn_auth_module'
|
|
44
|
+
],
|
|
45
|
+
includes_notes: {
|
|
46
|
+
webauthn_credentials_module: 'Per-user WebAuthn credential storage. Without it, passkey registration does not compile.',
|
|
47
|
+
webauthn_auth_module: 'Runtime challenge + assertion flow.',
|
|
48
|
+
session_secrets_module: 'Challenge nonces for registration and assertion.'
|
|
49
|
+
},
|
|
50
|
+
omits_notes: {
|
|
51
|
+
rate_limits_module: 'Add via `auth:hardened` for production.',
|
|
52
|
+
connected_accounts_module: 'No OAuth / SSO — add via `auth:hardened`.',
|
|
53
|
+
phone_numbers_module: 'No SMS.'
|
|
54
|
+
},
|
|
55
|
+
extends: ['auth:email']
|
|
56
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ModulePreset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `auth:sso` — `auth:email` plus OAuth / OpenID Connect sign-in.
|
|
4
|
+
*
|
|
5
|
+
* Adds `connected_accounts_module` (the junction table mapping a user to
|
|
6
|
+
* `(provider, external_id)`) and `identity_providers_module` (the provider
|
|
7
|
+
* config: URLs, client_id, encrypted client_secret, scopes, PKCE/nonce
|
|
8
|
+
* knobs). The generator then emits `sign_in_identity` / `sign_up_identity`
|
|
9
|
+
* procedures which rely on `encrypted_secrets_module` to decrypt the client
|
|
10
|
+
* secret at auth time.
|
|
11
|
+
*
|
|
12
|
+
* Password fallback stays on by default (break-glass for admins); flip the
|
|
13
|
+
* `allow_password_sign_*` toggles off in `app_settings_auth` for strictly
|
|
14
|
+
* SSO-only.
|
|
15
|
+
*
|
|
16
|
+
* Note: `emails_module` is still required — the `user_auth_module` insert
|
|
17
|
+
* trigger hard-requires it today. A pure SSO-only install without emails
|
|
18
|
+
* is a separate refactor (see `docs/architecture/module-presets.md` in
|
|
19
|
+
* constructive-db).
|
|
20
|
+
*/
|
|
21
|
+
export declare const PresetAuthSso: ModulePreset;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `auth:sso` — `auth:email` plus OAuth / OpenID Connect sign-in.
|
|
3
|
+
*
|
|
4
|
+
* Adds `connected_accounts_module` (the junction table mapping a user to
|
|
5
|
+
* `(provider, external_id)`) and `identity_providers_module` (the provider
|
|
6
|
+
* config: URLs, client_id, encrypted client_secret, scopes, PKCE/nonce
|
|
7
|
+
* knobs). The generator then emits `sign_in_identity` / `sign_up_identity`
|
|
8
|
+
* procedures which rely on `encrypted_secrets_module` to decrypt the client
|
|
9
|
+
* secret at auth time.
|
|
10
|
+
*
|
|
11
|
+
* Password fallback stays on by default (break-glass for admins); flip the
|
|
12
|
+
* `allow_password_sign_*` toggles off in `app_settings_auth` for strictly
|
|
13
|
+
* SSO-only.
|
|
14
|
+
*
|
|
15
|
+
* Note: `emails_module` is still required — the `user_auth_module` insert
|
|
16
|
+
* trigger hard-requires it today. A pure SSO-only install without emails
|
|
17
|
+
* is a separate refactor (see `docs/architecture/module-presets.md` in
|
|
18
|
+
* constructive-db).
|
|
19
|
+
*/
|
|
20
|
+
export const PresetAuthSso = {
|
|
21
|
+
name: 'auth:sso',
|
|
22
|
+
display_name: 'OAuth / OpenID Connect',
|
|
23
|
+
summary: '`auth:email` plus OAuth providers and connected-account linkage.',
|
|
24
|
+
description: "Adds the two modules that make SSO work: `identity_providers_module` (where provider " +
|
|
25
|
+
"definitions live — Google, GitHub, Okta, etc., with their URLs, client IDs, and " +
|
|
26
|
+
"encrypted client secrets) and `connected_accounts_module` (the junction mapping a " +
|
|
27
|
+
"Constructive user to a `(provider, external_id)` pair). The generator emits " +
|
|
28
|
+
"`sign_in_identity` and `sign_up_identity` procedures which decrypt the client secret " +
|
|
29
|
+
"through `encrypted_secrets_module` at auth time. Keep password flows as break-glass, or " +
|
|
30
|
+
"disable them via `app_settings_auth` toggles for strictly-SSO deployments.",
|
|
31
|
+
good_for: [
|
|
32
|
+
'B2B apps where end users sign in via their employer IdP',
|
|
33
|
+
'Consumer apps that want "Sign in with Google / GitHub"',
|
|
34
|
+
'Apps that need to federate identity with a specific provider ecosystem'
|
|
35
|
+
],
|
|
36
|
+
not_for: [
|
|
37
|
+
'Apps that also need passkeys and rate limits — use `auth:hardened`',
|
|
38
|
+
'Strictly-SSO apps that want NO email storage — needs the emails-optional refactor; not supported by a preset today'
|
|
39
|
+
],
|
|
40
|
+
modules: [
|
|
41
|
+
'users_module',
|
|
42
|
+
'membership_types_module',
|
|
43
|
+
'memberships_module:app',
|
|
44
|
+
'sessions_module',
|
|
45
|
+
'secrets_module',
|
|
46
|
+
'encrypted_secrets_module',
|
|
47
|
+
'emails_module',
|
|
48
|
+
'rls_module',
|
|
49
|
+
'user_auth_module',
|
|
50
|
+
'connected_accounts_module',
|
|
51
|
+
'identity_providers_module'
|
|
52
|
+
],
|
|
53
|
+
includes_notes: {
|
|
54
|
+
connected_accounts_module: 'Junction table for (user, provider, external_id). Without it, `sign_in_identity` does not compile.',
|
|
55
|
+
identity_providers_module: 'Provider config table (URLs, client_id, encrypted client_secret, scopes, PKCE knobs).',
|
|
56
|
+
encrypted_secrets_module: 'Required by `auth:email` already; also used by SSO to decrypt the provider client_secret at auth time.'
|
|
57
|
+
},
|
|
58
|
+
omits_notes: {
|
|
59
|
+
webauthn_credentials_module: 'No passkeys — add `auth:passkey` or move to `auth:hardened`.',
|
|
60
|
+
rate_limits_module: 'Omitted; add via `auth:hardened` for production.',
|
|
61
|
+
session_secrets_module: "Not required for authorization-code OAuth; add if you also want magic-link flows. PKCE doesn't require it for stateless OAuth flows today.",
|
|
62
|
+
phone_numbers_module: 'No SMS in this preset.'
|
|
63
|
+
},
|
|
64
|
+
extends: ['auth:email']
|
|
65
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModulePreset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `b2b` — `auth:hardened` plus orgs, invites, permissions, levels,
|
|
4
|
+
* profiles, and hierarchy. The full multi-tenant / B2B SaaS shape.
|
|
5
|
+
*
|
|
6
|
+
* Installs both app-scoped AND org-scoped instances of the membership,
|
|
7
|
+
* permission, limit, level, profile, and invite modules. `hierarchy_module`
|
|
8
|
+
* at the org scope enables nested org/team structures.
|
|
9
|
+
*
|
|
10
|
+
* This is a large install — every B2B concept Constructive ships. Don't
|
|
11
|
+
* reach for it until you actually need orgs; moving from `auth:hardened`
|
|
12
|
+
* to `b2b` later is a provisioning step, not a schema rewrite.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PresetB2b: ModulePreset;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `b2b` — `auth:hardened` plus orgs, invites, permissions, levels,
|
|
3
|
+
* profiles, and hierarchy. The full multi-tenant / B2B SaaS shape.
|
|
4
|
+
*
|
|
5
|
+
* Installs both app-scoped AND org-scoped instances of the membership,
|
|
6
|
+
* permission, limit, level, profile, and invite modules. `hierarchy_module`
|
|
7
|
+
* at the org scope enables nested org/team structures.
|
|
8
|
+
*
|
|
9
|
+
* This is a large install — every B2B concept Constructive ships. Don't
|
|
10
|
+
* reach for it until you actually need orgs; moving from `auth:hardened`
|
|
11
|
+
* to `b2b` later is a provisioning step, not a schema rewrite.
|
|
12
|
+
*/
|
|
13
|
+
export const PresetB2b = {
|
|
14
|
+
name: 'b2b',
|
|
15
|
+
display_name: 'B2B SaaS (orgs + invites + permissions)',
|
|
16
|
+
summary: '`auth:hardened` + orgs, invites, fine-grained permissions, levels, profiles, hierarchy.',
|
|
17
|
+
description: 'Everything in `auth:hardened`, plus the full org/team/permission stack at both app and ' +
|
|
18
|
+
'org membership scopes. You get: `memberships_module:org` for org-scoped memberships, ' +
|
|
19
|
+
'`permissions_module:app/:org` for fine-grained RBAC, `limits_module:app/:org` for per-scope ' +
|
|
20
|
+
'quota enforcement, `levels_module:app/:org` for role bundles, `profiles_module:app/:org` ' +
|
|
21
|
+
'for per-scope user display info, `hierarchy_module:org` for nested org structures, and ' +
|
|
22
|
+
'`invites_module:app/:org` for invite flows at either scope. Choose this when the app has ' +
|
|
23
|
+
'the concept of a "workspace" / "team" / "tenant" that users belong to and act within.',
|
|
24
|
+
good_for: [
|
|
25
|
+
'B2B SaaS with multi-tenant workspaces / teams',
|
|
26
|
+
'Apps where permissions scope to an organization, not globally',
|
|
27
|
+
'Apps with an invite-based onboarding flow (admins invite members)',
|
|
28
|
+
'Apps that need nested org hierarchies (parent org / sub-org / team)'
|
|
29
|
+
],
|
|
30
|
+
not_for: [
|
|
31
|
+
'Single-tenant consumer apps — use `auth:hardened` or `auth:email`',
|
|
32
|
+
'Apps where all users see the same global dataset — orgs would add overhead with no benefit'
|
|
33
|
+
],
|
|
34
|
+
modules: [
|
|
35
|
+
'users_module',
|
|
36
|
+
'membership_types_module',
|
|
37
|
+
'memberships_module:app',
|
|
38
|
+
'memberships_module:org',
|
|
39
|
+
'sessions_module',
|
|
40
|
+
'secrets_module',
|
|
41
|
+
'encrypted_secrets_module',
|
|
42
|
+
'emails_module',
|
|
43
|
+
'rls_module',
|
|
44
|
+
'user_auth_module',
|
|
45
|
+
'session_secrets_module',
|
|
46
|
+
'rate_limits_module',
|
|
47
|
+
'connected_accounts_module',
|
|
48
|
+
'identity_providers_module',
|
|
49
|
+
'webauthn_credentials_module',
|
|
50
|
+
'webauthn_auth_module',
|
|
51
|
+
'phone_numbers_module',
|
|
52
|
+
'permissions_module:app',
|
|
53
|
+
'permissions_module:org',
|
|
54
|
+
'limits_module:app',
|
|
55
|
+
'limits_module:org',
|
|
56
|
+
'levels_module:app',
|
|
57
|
+
'levels_module:org',
|
|
58
|
+
'profiles_module:app',
|
|
59
|
+
'profiles_module:org',
|
|
60
|
+
'hierarchy_module:org',
|
|
61
|
+
'invites_module:app',
|
|
62
|
+
'invites_module:org'
|
|
63
|
+
],
|
|
64
|
+
includes_notes: {
|
|
65
|
+
'memberships_module:org': 'Org-scoped membership rows — every user in an org gets one.',
|
|
66
|
+
'permissions_module:app': 'App-wide permission grants (e.g. platform admins).',
|
|
67
|
+
'permissions_module:org': 'Org-scoped permission grants (per-workspace admins, members, viewers, ...).',
|
|
68
|
+
'limits_module:app': 'App-level quotas (e.g. max users per plan).',
|
|
69
|
+
'limits_module:org': 'Org-level quotas (e.g. per-workspace API call caps).',
|
|
70
|
+
'levels_module:app': 'Role/level bundles at the app scope.',
|
|
71
|
+
'levels_module:org': 'Role/level bundles at the org scope (admin / member / viewer, etc.).',
|
|
72
|
+
'profiles_module:app': 'App-scoped user profile (one per user).',
|
|
73
|
+
'profiles_module:org': 'Org-scoped user profile (per org a user belongs to).',
|
|
74
|
+
'hierarchy_module:org': 'Nested org structures (parent / child orgs).',
|
|
75
|
+
'invites_module:app': 'App-level invites (rare — usually platform admin adds another admin).',
|
|
76
|
+
'invites_module:org': 'Org-level invites (the common case — invite a teammate into a workspace).'
|
|
77
|
+
},
|
|
78
|
+
omits_notes: {
|
|
79
|
+
storage_module: 'Add separately if you need file uploads tied to orgs.',
|
|
80
|
+
crypto_addresses_module: 'Not a web3 preset.'
|
|
81
|
+
},
|
|
82
|
+
extends: ['auth:hardened']
|
|
83
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ModulePreset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `full` — install everything. Equivalent to the default
|
|
4
|
+
* `provision_database_modules(v_modules => ARRAY['all'])` behavior.
|
|
5
|
+
*
|
|
6
|
+
* This is the maximalist preset: every module Constructive ships, including
|
|
7
|
+
* `storage_module` for file uploads and `crypto_addresses_module` for
|
|
8
|
+
* wallet-based sign-in. Use it for greenfield apps where you'd rather
|
|
9
|
+
* disable features via `app_settings_auth` toggles than uninstall modules,
|
|
10
|
+
* or for the "kitchen sink" example / demo databases.
|
|
11
|
+
*
|
|
12
|
+
* Prefer a more targeted preset for anything production-bound — installing
|
|
13
|
+
* a module you'll never use still costs tables, triggers, and grants.
|
|
14
|
+
*/
|
|
15
|
+
export declare const PresetFull: ModulePreset;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `full` — install everything. Equivalent to the default
|
|
3
|
+
* `provision_database_modules(v_modules => ARRAY['all'])` behavior.
|
|
4
|
+
*
|
|
5
|
+
* This is the maximalist preset: every module Constructive ships, including
|
|
6
|
+
* `storage_module` for file uploads and `crypto_addresses_module` for
|
|
7
|
+
* wallet-based sign-in. Use it for greenfield apps where you'd rather
|
|
8
|
+
* disable features via `app_settings_auth` toggles than uninstall modules,
|
|
9
|
+
* or for the "kitchen sink" example / demo databases.
|
|
10
|
+
*
|
|
11
|
+
* Prefer a more targeted preset for anything production-bound — installing
|
|
12
|
+
* a module you'll never use still costs tables, triggers, and grants.
|
|
13
|
+
*/
|
|
14
|
+
export const PresetFull = {
|
|
15
|
+
name: 'full',
|
|
16
|
+
display_name: 'Full (every module)',
|
|
17
|
+
summary: "Install every Constructive module. Equivalent to v_modules => ARRAY['all'].",
|
|
18
|
+
description: 'Installs every module in the catalog: everything in `b2b` plus `storage_module` ' +
|
|
19
|
+
'for file uploads and `crypto_addresses_module` / `crypto_auth_module` for ' +
|
|
20
|
+
'wallet-based sign-in. This matches the current default when `provision_database_modules` ' +
|
|
21
|
+
"is called without an explicit `v_modules` argument. Use it for fully-featured " +
|
|
22
|
+
'demo/example databases, kitchen-sink reference deployments, or greenfield apps that ' +
|
|
23
|
+
'would rather feature-flag at the app_settings level than uninstall modules.',
|
|
24
|
+
good_for: [
|
|
25
|
+
'Reference / demo databases that showcase every Constructive feature',
|
|
26
|
+
'Greenfield apps where the product scope is still open-ended',
|
|
27
|
+
'Keeping the provisioning call identical to the pre-preset default'
|
|
28
|
+
],
|
|
29
|
+
not_for: [
|
|
30
|
+
'Production apps with a defined feature set — pick the narrowest preset that fits',
|
|
31
|
+
'Resource-constrained environments — every module costs schema bloat, RLS policies, and grants'
|
|
32
|
+
],
|
|
33
|
+
modules: ['all'],
|
|
34
|
+
includes_notes: {
|
|
35
|
+
all: "Sentinel — the provisioner installs every known module when `modules = ['all']`."
|
|
36
|
+
},
|
|
37
|
+
extends: ['b2b']
|
|
38
|
+
};
|