node-type-registry 0.43.1 → 0.46.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.
Files changed (36) hide show
  1. package/README.md +4 -0
  2. package/data/data-i18n.d.ts +2 -0
  3. package/data/data-i18n.js +47 -0
  4. package/data/index.d.ts +1 -0
  5. package/data/index.js +3 -1
  6. package/esm/data/data-i18n.d.ts +2 -0
  7. package/esm/data/data-i18n.js +44 -0
  8. package/esm/data/index.d.ts +1 -0
  9. package/esm/data/index.js +1 -0
  10. package/esm/module-presets/auth-email-magic.js +6 -14
  11. package/esm/module-presets/auth-email.d.ts +3 -3
  12. package/esm/module-presets/auth-email.js +10 -29
  13. package/esm/module-presets/auth-hardened.js +6 -21
  14. package/esm/module-presets/auth-passkey.js +6 -15
  15. package/esm/module-presets/auth-sso.d.ts +1 -1
  16. package/esm/module-presets/auth-sso.js +8 -18
  17. package/esm/module-presets/b2b-storage.js +16 -22
  18. package/esm/module-presets/b2b.js +20 -38
  19. package/esm/module-presets/full.d.ts +1 -1
  20. package/esm/module-presets/full.js +25 -28
  21. package/esm/module-presets/minimal.js +1 -12
  22. package/esm/module-presets/types.d.ts +9 -15
  23. package/module-presets/auth-email-magic.js +6 -14
  24. package/module-presets/auth-email.d.ts +3 -3
  25. package/module-presets/auth-email.js +10 -29
  26. package/module-presets/auth-hardened.js +6 -21
  27. package/module-presets/auth-passkey.js +6 -15
  28. package/module-presets/auth-sso.d.ts +1 -1
  29. package/module-presets/auth-sso.js +8 -18
  30. package/module-presets/b2b-storage.js +16 -22
  31. package/module-presets/b2b.js +20 -38
  32. package/module-presets/full.d.ts +1 -1
  33. package/module-presets/full.js +25 -28
  34. package/module-presets/minimal.js +1 -12
  35. package/module-presets/types.d.ts +9 -15
  36. package/package.json +2 -2
package/README.md CHANGED
@@ -131,6 +131,10 @@ Common issues and solutions for pgpm, PostgreSQL, and testing.
131
131
  * [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **📝 Type definitions** for PostgreSQL AST nodes in TypeScript.
132
132
  * [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **🛠️ AST utilities** for constructing and transforming PostgreSQL syntax trees.
133
133
 
134
+ ### 📚 Documentation & Skills
135
+
136
+ * [constructive-skills](https://github.com/constructive-io/constructive-skills): **📖 Platform documentation and AI agent skills** — feature catalog, blueprint reference, SDK guides (i18n, billing, limits, events, uploads, security, entities, search, AI), and deployment guides.
137
+
134
138
  ## Credits
135
139
 
136
140
  **🛠 Built by the [Constructive](https://constructive.io) team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**
@@ -0,0 +1,2 @@
1
+ import type { NodeTypeDefinition } from '../types';
2
+ export declare const DataI18n: NodeTypeDefinition;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DataI18n = void 0;
4
+ exports.DataI18n = {
5
+ name: 'DataI18n',
6
+ slug: 'data_i18n',
7
+ category: 'data',
8
+ display_name: 'Internationalization',
9
+ description: 'Creates a companion _translations table with lang_code + translatable ' +
10
+ 'fields. Copies SELECT policies and column-ref fields from the base ' +
11
+ 'table. Adds @i18n smart comment so the Graphile i18n plugin discovers ' +
12
+ 'it. Requires i18n_module to be provisioned for the database.',
13
+ parameter_schema: {
14
+ type: 'object',
15
+ properties: {
16
+ fields: {
17
+ type: 'array',
18
+ items: {
19
+ type: 'string',
20
+ format: 'column-ref'
21
+ },
22
+ description: 'Field names on the base table to make translatable. Each field ' +
23
+ 'is duplicated on the translation table with the same type.'
24
+ },
25
+ table_suffix: {
26
+ type: 'string',
27
+ description: 'Suffix for the translation table name',
28
+ default: '_translations'
29
+ },
30
+ lang_code_type: {
31
+ type: 'string',
32
+ enum: ['citext', 'text'],
33
+ description: 'Type for the lang_code column',
34
+ default: 'citext'
35
+ },
36
+ copy_mutation_policies: {
37
+ type: 'boolean',
38
+ description: 'Whether to also copy INSERT/UPDATE/DELETE policies (not just ' +
39
+ 'SELECT). Default true — translations should be editable by the ' +
40
+ 'same users who can edit the base row.',
41
+ default: true
42
+ }
43
+ },
44
+ required: ['fields']
45
+ },
46
+ tags: ['i18n', 'translation', 'schema']
47
+ };
package/data/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export { DataCompositeField } from './data-composite-field';
7
7
  export { DataDirectOwner } from './data-direct-owner';
8
8
  export { DataEntityMembership } from './data-entity-membership';
9
9
  export { DataForceCurrentUser } from './data-force-current-user';
10
+ export { DataI18n } from './data-i18n';
10
11
  export { DataId } from './data-id';
11
12
  export { DataImmutableFields } from './data-immutable-fields';
12
13
  export { DataInflection } from './data-inflection';
package/data/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TableUserSettings = exports.TableUserProfiles = exports.TableOrganizationSettings = exports.SearchVector = exports.SearchUnified = exports.SearchTrgm = exports.SearchSpatialAggregate = exports.SearchSpatial = exports.SearchFullText = exports.SearchBm25 = exports.DataTimestamps = exports.DataTags = exports.DataStatusField = exports.DataSoftDelete = exports.DataSlug = exports.DataRealtime = exports.DataPublishable = exports.DataPeoplestamps = exports.DataOwnershipInEntity = exports.DataOwnedFields = exports.DataMemberOwner = exports.DataJsonb = exports.DataInheritFromParent = exports.DataInflection = exports.DataImmutableFields = exports.DataId = exports.DataForceCurrentUser = exports.DataEntityMembership = exports.DataDirectOwner = exports.DataCompositeField = exports.DataBulk = exports.CheckOneOf = exports.CheckNotEqual = exports.CheckLessThan = exports.CheckGreaterThan = void 0;
3
+ exports.TableUserSettings = exports.TableUserProfiles = exports.TableOrganizationSettings = exports.SearchVector = exports.SearchUnified = exports.SearchTrgm = exports.SearchSpatialAggregate = exports.SearchSpatial = exports.SearchFullText = exports.SearchBm25 = exports.DataTimestamps = exports.DataTags = exports.DataStatusField = exports.DataSoftDelete = exports.DataSlug = exports.DataRealtime = exports.DataPublishable = exports.DataPeoplestamps = exports.DataOwnershipInEntity = exports.DataOwnedFields = exports.DataMemberOwner = exports.DataJsonb = exports.DataInheritFromParent = exports.DataInflection = exports.DataImmutableFields = exports.DataId = exports.DataI18n = exports.DataForceCurrentUser = exports.DataEntityMembership = exports.DataDirectOwner = exports.DataCompositeField = exports.DataBulk = exports.CheckOneOf = exports.CheckNotEqual = exports.CheckLessThan = exports.CheckGreaterThan = void 0;
4
4
  var check_greater_than_1 = require("./check-greater-than");
5
5
  Object.defineProperty(exports, "CheckGreaterThan", { enumerable: true, get: function () { return check_greater_than_1.CheckGreaterThan; } });
6
6
  var check_less_than_1 = require("./check-less-than");
@@ -19,6 +19,8 @@ var data_entity_membership_1 = require("./data-entity-membership");
19
19
  Object.defineProperty(exports, "DataEntityMembership", { enumerable: true, get: function () { return data_entity_membership_1.DataEntityMembership; } });
20
20
  var data_force_current_user_1 = require("./data-force-current-user");
21
21
  Object.defineProperty(exports, "DataForceCurrentUser", { enumerable: true, get: function () { return data_force_current_user_1.DataForceCurrentUser; } });
22
+ var data_i18n_1 = require("./data-i18n");
23
+ Object.defineProperty(exports, "DataI18n", { enumerable: true, get: function () { return data_i18n_1.DataI18n; } });
22
24
  var data_id_1 = require("./data-id");
23
25
  Object.defineProperty(exports, "DataId", { enumerable: true, get: function () { return data_id_1.DataId; } });
24
26
  var data_immutable_fields_1 = require("./data-immutable-fields");
@@ -0,0 +1,2 @@
1
+ import type { NodeTypeDefinition } from '../types';
2
+ export declare const DataI18n: NodeTypeDefinition;
@@ -0,0 +1,44 @@
1
+ export const DataI18n = {
2
+ name: 'DataI18n',
3
+ slug: 'data_i18n',
4
+ category: 'data',
5
+ display_name: 'Internationalization',
6
+ description: 'Creates a companion _translations table with lang_code + translatable ' +
7
+ 'fields. Copies SELECT policies and column-ref fields from the base ' +
8
+ 'table. Adds @i18n smart comment so the Graphile i18n plugin discovers ' +
9
+ 'it. Requires i18n_module to be provisioned for the database.',
10
+ parameter_schema: {
11
+ type: 'object',
12
+ properties: {
13
+ fields: {
14
+ type: 'array',
15
+ items: {
16
+ type: 'string',
17
+ format: 'column-ref'
18
+ },
19
+ description: 'Field names on the base table to make translatable. Each field ' +
20
+ 'is duplicated on the translation table with the same type.'
21
+ },
22
+ table_suffix: {
23
+ type: 'string',
24
+ description: 'Suffix for the translation table name',
25
+ default: '_translations'
26
+ },
27
+ lang_code_type: {
28
+ type: 'string',
29
+ enum: ['citext', 'text'],
30
+ description: 'Type for the lang_code column',
31
+ default: 'citext'
32
+ },
33
+ copy_mutation_policies: {
34
+ type: 'boolean',
35
+ description: 'Whether to also copy INSERT/UPDATE/DELETE policies (not just ' +
36
+ 'SELECT). Default true — translations should be editable by the ' +
37
+ 'same users who can edit the base row.',
38
+ default: true
39
+ }
40
+ },
41
+ required: ['fields']
42
+ },
43
+ tags: ['i18n', 'translation', 'schema']
44
+ };
@@ -7,6 +7,7 @@ export { DataCompositeField } from './data-composite-field';
7
7
  export { DataDirectOwner } from './data-direct-owner';
8
8
  export { DataEntityMembership } from './data-entity-membership';
9
9
  export { DataForceCurrentUser } from './data-force-current-user';
10
+ export { DataI18n } from './data-i18n';
10
11
  export { DataId } from './data-id';
11
12
  export { DataImmutableFields } from './data-immutable-fields';
12
13
  export { DataInflection } from './data-inflection';
package/esm/data/index.js CHANGED
@@ -7,6 +7,7 @@ export { DataCompositeField } from './data-composite-field';
7
7
  export { DataDirectOwner } from './data-direct-owner';
8
8
  export { DataEntityMembership } from './data-entity-membership';
9
9
  export { DataForceCurrentUser } from './data-force-current-user';
10
+ export { DataI18n } from './data-i18n';
10
11
  export { DataId } from './data-id';
11
12
  export { DataImmutableFields } from './data-immutable-fields';
12
13
  export { DataInflection } from './data-inflection';
@@ -33,26 +33,18 @@ export const PresetAuthEmailMagic = {
33
33
  modules: [
34
34
  'users_module',
35
35
  'membership_types_module',
36
- 'permissions_module:app',
37
- 'limits_module:app',
38
- 'levels_module:app',
39
- 'memberships_module:app',
36
+ ['permissions_module', { scope: 'app' }],
37
+ ['limits_module', { scope: 'app' }],
38
+ ['levels_module', { scope: 'app' }],
39
+ ['memberships_module', { scope: 'app' }],
40
40
  'sessions_module',
41
41
  'user_state_module',
42
- 'config_secrets_user_module',
42
+ 'user_credentials_module',
43
+ 'config_secrets_module',
43
44
  'emails_module',
44
45
  'rls_module',
45
46
  'user_auth_module',
46
47
  'session_secrets_module'
47
48
  ],
48
- includes_notes: {
49
- session_secrets_module: 'Stores nonces for magic-link and email-OTP flows. Without it those procedures are not emitted.'
50
- },
51
- omits_notes: {
52
- rate_limits_module: 'Same reasoning as `auth:email` — add later via `auth:hardened`.',
53
- connected_accounts_module: 'No OAuth / SSO in this preset.',
54
- webauthn_credentials_module: 'No passkeys — add `auth:passkey`.',
55
- phone_numbers_module: 'No SMS — add `auth:hardened`.'
56
- },
57
49
  extends: ['auth:email']
58
50
  };
@@ -9,9 +9,9 @@ import type { ModulePreset } from './types';
9
9
  * `set_password`, `reset_password`, `forgot_password`, `verify_email`,
10
10
  * `delete_account`, `my_sessions`, API-key CRUD. Nothing more.
11
11
  *
12
- * Includes `permissions_module:app`, `limits_module:app`, and
13
- * `levels_module:app` because `memberships_module:app` has NOT NULL
14
- * foreign keys to the tables they create (grants, caps, levels).
12
+ * Includes permissions, limits, and levels modules (app scope) because
13
+ * the app-scoped memberships module has NOT NULL foreign keys to the
14
+ * tables they create (grants, caps, levels).
15
15
  *
16
16
  * It deliberately excludes rate limits, connected accounts / identity
17
17
  * providers (OAuth), WebAuthn (passkeys), phone numbers (SMS), invites,
@@ -8,9 +8,9 @@
8
8
  * `set_password`, `reset_password`, `forgot_password`, `verify_email`,
9
9
  * `delete_account`, `my_sessions`, API-key CRUD. Nothing more.
10
10
  *
11
- * Includes `permissions_module:app`, `limits_module:app`, and
12
- * `levels_module:app` because `memberships_module:app` has NOT NULL
13
- * foreign keys to the tables they create (grants, caps, levels).
11
+ * Includes permissions, limits, and levels modules (app scope) because
12
+ * the app-scoped memberships module has NOT NULL foreign keys to the
13
+ * tables they create (grants, caps, levels).
14
14
  *
15
15
  * It deliberately excludes rate limits, connected accounts / identity
16
16
  * providers (OAuth), WebAuthn (passkeys), phone numbers (SMS), invites,
@@ -44,35 +44,16 @@ export const PresetAuthEmail = {
44
44
  modules: [
45
45
  'users_module',
46
46
  'membership_types_module',
47
- 'permissions_module:app',
48
- 'limits_module:app',
49
- 'levels_module:app',
50
- 'memberships_module:app',
47
+ ['permissions_module', { scope: 'app' }],
48
+ ['limits_module', { scope: 'app' }],
49
+ ['levels_module', { scope: 'app' }],
50
+ ['memberships_module', { scope: 'app' }],
51
51
  'sessions_module',
52
52
  'user_state_module',
53
- 'config_secrets_user_module',
53
+ 'user_credentials_module',
54
+ 'config_secrets_module',
54
55
  'emails_module',
55
56
  'rls_module',
56
57
  'user_auth_module'
57
- ],
58
- includes_notes: {
59
- 'memberships_module:app': 'Required by `user_auth_module`: every user gets an app-level membership row at sign-up.',
60
- membership_types_module: "Required by `memberships_module:app`; defines the 'app' scope.",
61
- 'permissions_module:app': 'Required by `memberships_module:app`: NOT NULL FK to grants table.',
62
- 'limits_module:app': 'Required by `memberships_module:app`: NOT NULL FK to caps table.',
63
- 'levels_module:app': 'Required by `memberships_module:app`: NOT NULL FK to levels table.',
64
- emails_module: 'Required by the `user_auth_module` insert trigger (`RAISE EXCEPTION REQUIRES emails_module`).',
65
- config_secrets_user_module: 'Required for password hashing; referenced by `set_password`, `verify_password`, and reset flows.',
66
- user_state_module: 'API-key storage (`create_api_key`, `revoke_api_key`, `my_api_keys`).'
67
- },
68
- omits_notes: {
69
- rate_limits_module: 'Omitted intentionally; throttle_* helpers are null-safe and the auth procs compile without it. Add later via `auth:hardened`.',
70
- connected_accounts_module: 'No OAuth / SSO in this preset — add `auth:sso`.',
71
- identity_providers_module: 'No OAuth provider configs without connected_accounts.',
72
- webauthn_credentials_module: 'No passkeys — add `auth:passkey`.',
73
- phone_numbers_module: 'No SMS login — add `auth:hardened` or the SMS-only refactor path.',
74
- 'memberships_module:org': 'No org/team structure — move to `b2b` when you need one.',
75
- invites_module: 'Self-serve signup only.',
76
- session_secrets_module: 'No magic-link / email-OTP nonces; add `auth:email+magic`.'
77
- }
58
+ ]
78
59
  };
@@ -30,13 +30,14 @@ export const PresetAuthHardened = {
30
30
  modules: [
31
31
  'users_module',
32
32
  'membership_types_module',
33
- 'permissions_module:app',
34
- 'limits_module:app',
35
- 'levels_module:app',
36
- 'memberships_module:app',
33
+ ['permissions_module', { scope: 'app' }],
34
+ ['limits_module', { scope: 'app' }],
35
+ ['levels_module', { scope: 'app' }],
36
+ ['memberships_module', { scope: 'app' }],
37
37
  'sessions_module',
38
38
  'user_state_module',
39
- 'config_secrets_user_module',
39
+ 'user_credentials_module',
40
+ 'config_secrets_module',
40
41
  'emails_module',
41
42
  'rls_module',
42
43
  'user_auth_module',
@@ -49,21 +50,5 @@ export const PresetAuthHardened = {
49
50
  'phone_numbers_module',
50
51
  'devices_module'
51
52
  ],
52
- includes_notes: {
53
- rate_limits_module: 'Throttling for sign-in, password reset, sign-up, and IP-based gates.',
54
- connected_accounts_module: 'OAuth / SSO linkage.',
55
- identity_providers_module: 'OAuth provider configs (required for `connected_accounts_module`).',
56
- webauthn_credentials_module: 'Per-user passkey storage.',
57
- webauthn_auth_module: 'Passkey challenge + assertion runtime.',
58
- session_secrets_module: 'Nonces for magic links, email OTP, and WebAuthn challenges.',
59
- phone_numbers_module: 'SMS sign-in / MFA support.',
60
- devices_module: 'Device tracking and trusted-device MFA bypass.'
61
- },
62
- omits_notes: {
63
- 'memberships_module:org': 'No orgs / teams — use `b2b` when you need multi-tenancy.',
64
- invites_module: 'No invite flow — add via `b2b`.',
65
- storage_module: 'Add separately if you need file uploads.',
66
- crypto_addresses_module: 'Not a web3 preset; omit unless doing wallet sign-in.'
67
- },
68
53
  extends: ['auth:email', 'auth:email+magic', 'auth:sso', 'auth:passkey']
69
54
  };
@@ -31,13 +31,14 @@ export const PresetAuthPasskey = {
31
31
  modules: [
32
32
  'users_module',
33
33
  'membership_types_module',
34
- 'permissions_module:app',
35
- 'limits_module:app',
36
- 'levels_module:app',
37
- 'memberships_module:app',
34
+ ['permissions_module', { scope: 'app' }],
35
+ ['limits_module', { scope: 'app' }],
36
+ ['levels_module', { scope: 'app' }],
37
+ ['memberships_module', { scope: 'app' }],
38
38
  'sessions_module',
39
39
  'user_state_module',
40
- 'config_secrets_user_module',
40
+ 'user_credentials_module',
41
+ 'config_secrets_module',
41
42
  'emails_module',
42
43
  'rls_module',
43
44
  'user_auth_module',
@@ -45,15 +46,5 @@ export const PresetAuthPasskey = {
45
46
  'webauthn_credentials_module',
46
47
  'webauthn_auth_module'
47
48
  ],
48
- includes_notes: {
49
- webauthn_credentials_module: 'Per-user WebAuthn credential storage. Without it, passkey registration does not compile.',
50
- webauthn_auth_module: 'Runtime challenge + assertion flow.',
51
- session_secrets_module: 'Challenge nonces for registration and assertion.'
52
- },
53
- omits_notes: {
54
- rate_limits_module: 'Add via `auth:hardened` for production.',
55
- connected_accounts_module: 'No OAuth / SSO — add via `auth:hardened`.',
56
- phone_numbers_module: 'No SMS.'
57
- },
58
49
  extends: ['auth:email']
59
50
  };
@@ -6,7 +6,7 @@ import type { ModulePreset } from './types';
6
6
  * `(provider, external_id)`) and `identity_providers_module` (the provider
7
7
  * config: URLs, client_id, encrypted client_secret, scopes, PKCE/nonce
8
8
  * knobs). The generator then emits `sign_in_identity` / `sign_up_identity`
9
- * procedures which rely on `config_secrets_user_module` to decrypt the client
9
+ * procedures which rely on `config_secrets_module` to decrypt the client
10
10
  * secret at auth time.
11
11
  *
12
12
  * Password fallback stays on by default (break-glass for admins); flip the
@@ -5,7 +5,7 @@
5
5
  * `(provider, external_id)`) and `identity_providers_module` (the provider
6
6
  * config: URLs, client_id, encrypted client_secret, scopes, PKCE/nonce
7
7
  * knobs). The generator then emits `sign_in_identity` / `sign_up_identity`
8
- * procedures which rely on `config_secrets_user_module` to decrypt the client
8
+ * procedures which rely on `config_secrets_module` to decrypt the client
9
9
  * secret at auth time.
10
10
  *
11
11
  * Password fallback stays on by default (break-glass for admins); flip the
@@ -26,7 +26,7 @@ export const PresetAuthSso = {
26
26
  'encrypted client secrets) and `connected_accounts_module` (the junction mapping a ' +
27
27
  'Constructive user to a `(provider, external_id)` pair). The generator emits ' +
28
28
  '`sign_in_identity` and `sign_up_identity` procedures which decrypt the client secret ' +
29
- 'through `config_secrets_user_module` at auth time. Keep password flows as break-glass, or ' +
29
+ 'through `config_secrets_module` at auth time. Keep password flows as break-glass, or ' +
30
30
  'disable them via `app_settings_auth` toggles for strictly-SSO deployments.',
31
31
  good_for: [
32
32
  'B2B apps where end users sign in via their employer IdP',
@@ -40,29 +40,19 @@ export const PresetAuthSso = {
40
40
  modules: [
41
41
  'users_module',
42
42
  'membership_types_module',
43
- 'permissions_module:app',
44
- 'limits_module:app',
45
- 'levels_module:app',
46
- 'memberships_module:app',
43
+ ['permissions_module', { scope: 'app' }],
44
+ ['limits_module', { scope: 'app' }],
45
+ ['levels_module', { scope: 'app' }],
46
+ ['memberships_module', { scope: 'app' }],
47
47
  'sessions_module',
48
48
  'user_state_module',
49
- 'config_secrets_user_module',
49
+ 'user_credentials_module',
50
+ 'config_secrets_module',
50
51
  'emails_module',
51
52
  'rls_module',
52
53
  'user_auth_module',
53
54
  'connected_accounts_module',
54
55
  'identity_providers_module'
55
56
  ],
56
- includes_notes: {
57
- connected_accounts_module: 'Junction table for (user, provider, external_id). Without it, `sign_in_identity` does not compile.',
58
- identity_providers_module: 'Provider config table (URLs, client_id, encrypted client_secret, scopes, PKCE knobs).',
59
- config_secrets_user_module: 'Required by `auth:email` already; also used by SSO to decrypt the provider client_secret at auth time.'
60
- },
61
- omits_notes: {
62
- webauthn_credentials_module: 'No passkeys — add `auth:passkey` or move to `auth:hardened`.',
63
- rate_limits_module: 'Omitted; add via `auth:hardened` for production.',
64
- 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.",
65
- phone_numbers_module: 'No SMS in this preset.'
66
- },
67
57
  extends: ['auth:email']
68
58
  };
@@ -18,7 +18,7 @@ export const PresetB2bStorage = {
18
18
  '`app_buckets` and `app_files` tables with full RLS: AuthzPublishable for public reads, ' +
19
19
  'AuthzAppMembership for member access, AuthzDirectOwner for uploader-only modify/delete. ' +
20
20
  'Entity-type provisioning with a non-empty `storage` array adds per-scope storage tables ' +
21
- 'automatically (multiple modules per entity via storage_key). Choose this when your B2B ' +
21
+ 'automatically (multiple modules per entity via key). Choose this when your B2B ' +
22
22
  'app needs file uploads, avatars, attachments, or any object storage tied to workspaces.',
23
23
  good_for: [
24
24
  'B2B SaaS with file uploads (documents, avatars, attachments)',
@@ -32,17 +32,18 @@ export const PresetB2bStorage = {
32
32
  modules: [
33
33
  'users_module',
34
34
  'membership_types_module',
35
- 'permissions_module:app',
36
- 'permissions_module:org',
37
- 'limits_module:app',
38
- 'limits_module:org',
39
- 'levels_module:app',
40
- 'levels_module:org',
41
- 'memberships_module:app',
42
- 'memberships_module:org',
35
+ ['permissions_module', { scope: 'app' }],
36
+ ['permissions_module', { scope: 'org' }],
37
+ ['limits_module', { scope: 'app' }],
38
+ ['limits_module', { scope: 'org' }],
39
+ ['levels_module', { scope: 'app' }],
40
+ ['levels_module', { scope: 'org' }],
41
+ ['memberships_module', { scope: 'app' }],
42
+ ['memberships_module', { scope: 'org' }],
43
43
  'sessions_module',
44
44
  'user_state_module',
45
- 'config_secrets_user_module',
45
+ 'user_credentials_module',
46
+ 'config_secrets_module',
46
47
  'emails_module',
47
48
  'rls_module',
48
49
  'user_auth_module',
@@ -53,20 +54,13 @@ export const PresetB2bStorage = {
53
54
  'webauthn_credentials_module',
54
55
  'webauthn_auth_module',
55
56
  'phone_numbers_module',
56
- 'profiles_module:app',
57
- 'profiles_module:org',
58
- 'hierarchy_module:org',
59
- 'invites_module:app',
60
- 'invites_module:org',
57
+ ['profiles_module', { scope: 'app' }],
58
+ ['profiles_module', { scope: 'org' }],
59
+ ['hierarchy_module', { scope: 'org' }],
60
+ ['invites_module', { scope: 'app' }],
61
+ ['invites_module', { scope: 'org' }],
61
62
  'storage_module',
62
63
  'devices_module'
63
64
  ],
64
- includes_notes: {
65
- storage_module: 'File upload infrastructure: app_buckets + app_files tables with RLS. Entity-type storage scopes layered on top via the `storage` array (array-only format, supports multiple modules per entity via storage_key).',
66
- devices_module: 'Device tracking and trusted-device MFA bypass.'
67
- },
68
- omits_notes: {
69
- crypto_addresses_module: 'Not a web3 preset.'
70
- },
71
65
  extends: ['b2b']
72
66
  };
@@ -15,11 +15,11 @@ export const PresetB2b = {
15
15
  display_name: 'B2B SaaS (orgs + invites + permissions)',
16
16
  summary: '`auth:hardened` + orgs, invites, fine-grained permissions, levels, profiles, hierarchy.',
17
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 ' +
18
+ 'org membership scopes. You get: memberships at org scope, permissions at app and org ' +
19
+ 'scopes for fine-grained RBAC, limits at app and org scopes for per-scope quota ' +
20
+ 'enforcement, levels at app and org scopes for role bundles, profiles at app and org ' +
21
+ 'scopes for per-scope user display info, hierarchy at org scope for nested org structures, ' +
22
+ 'and invites at app and org scopes for invite flows. Choose this when the app has ' +
23
23
  'the concept of a "workspace" / "team" / "tenant" that users belong to and act within.',
24
24
  good_for: [
25
25
  'B2B SaaS with multi-tenant workspaces / teams',
@@ -34,11 +34,12 @@ export const PresetB2b = {
34
34
  modules: [
35
35
  'users_module',
36
36
  'membership_types_module',
37
- 'memberships_module:app',
38
- 'memberships_module:org',
37
+ ['memberships_module', { scope: 'app' }],
38
+ ['memberships_module', { scope: 'org' }],
39
39
  'sessions_module',
40
40
  'user_state_module',
41
- 'config_secrets_user_module',
41
+ 'user_credentials_module',
42
+ 'config_secrets_module',
42
43
  'emails_module',
43
44
  'rls_module',
44
45
  'user_auth_module',
@@ -49,37 +50,18 @@ export const PresetB2b = {
49
50
  'webauthn_credentials_module',
50
51
  'webauthn_auth_module',
51
52
  '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',
53
+ ['permissions_module', { scope: 'app' }],
54
+ ['permissions_module', { scope: 'org' }],
55
+ ['limits_module', { scope: 'app' }],
56
+ ['limits_module', { scope: 'org' }],
57
+ ['levels_module', { scope: 'app' }],
58
+ ['levels_module', { scope: 'org' }],
59
+ ['profiles_module', { scope: 'app' }],
60
+ ['profiles_module', { scope: 'org' }],
61
+ ['hierarchy_module', { scope: 'org' }],
62
+ ['invites_module', { scope: 'app' }],
63
+ ['invites_module', { scope: 'org' }],
63
64
  'devices_module'
64
65
  ],
65
- includes_notes: {
66
- 'memberships_module:org': 'Org-scoped membership rows — every user in an org gets one.',
67
- 'permissions_module:app': 'App-wide permission grants (e.g. platform admins).',
68
- 'permissions_module:org': 'Org-scoped permission grants (per-workspace admins, members, viewers, ...).',
69
- 'limits_module:app': 'App-level quotas (e.g. max users per plan).',
70
- 'limits_module:org': 'Org-level quotas (e.g. per-workspace API call caps).',
71
- 'levels_module:app': 'Role/level bundles at the app scope.',
72
- 'levels_module:org': 'Role/level bundles at the org scope (admin / member / viewer, etc.).',
73
- 'profiles_module:app': 'App-scoped user profile (one per user).',
74
- 'profiles_module:org': 'Org-scoped user profile (per org a user belongs to).',
75
- 'hierarchy_module:org': 'Nested org structures (parent / child orgs).',
76
- 'invites_module:app': 'App-level invites (rare — usually platform admin adds another admin).',
77
- 'invites_module:org': 'Org-level invites (the common case — invite a teammate into a workspace).',
78
- devices_module: 'Device tracking and trusted-device MFA bypass.'
79
- },
80
- omits_notes: {
81
- storage_module: 'Add separately if you need file uploads tied to orgs.',
82
- crypto_addresses_module: 'Not a web3 preset.'
83
- },
84
66
  extends: ['auth:hardened']
85
67
  };
@@ -3,7 +3,7 @@ import type { ModulePreset } from './types';
3
3
  * `full` — install every standard module.
4
4
  *
5
5
  * This is the maximalist preset: every module Constructive ships, including
6
- * `storage_module:full` for file uploads with all feature flags and
6
+ * `storage_module` with all feature flags enabled and
7
7
  * `crypto_addresses_module` for wallet-based sign-in.
8
8
  *
9
9
  * Usage logging modules (compute_log, inference_log, transfer_log,