@soulbatical/tetra-core 0.1.13 → 0.1.15

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.

Potentially problematic release.


This version of @soulbatical/tetra-core might be problematic. Click here for more details.

Files changed (77) hide show
  1. package/dist/generators/rls-auditor.d.ts +39 -0
  2. package/dist/generators/rls-auditor.d.ts.map +1 -0
  3. package/dist/generators/rls-auditor.js +505 -0
  4. package/dist/generators/rls-auditor.js.map +1 -0
  5. package/dist/generators/rls-checker.d.ts +94 -0
  6. package/dist/generators/rls-checker.d.ts.map +1 -0
  7. package/dist/generators/rls-checker.js +215 -0
  8. package/dist/generators/rls-checker.js.map +1 -0
  9. package/dist/generators/rls-generator.d.ts +77 -0
  10. package/dist/generators/rls-generator.d.ts.map +1 -0
  11. package/dist/generators/rls-generator.js +402 -0
  12. package/dist/generators/rls-generator.js.map +1 -0
  13. package/dist/generators/rpc/detail-rpc-generator.d.ts +58 -0
  14. package/dist/generators/rpc/detail-rpc-generator.d.ts.map +1 -0
  15. package/dist/generators/rpc/detail-rpc-generator.js +163 -0
  16. package/dist/generators/rpc/detail-rpc-generator.js.map +1 -0
  17. package/dist/generators/rpc/index.d.ts +24 -0
  18. package/dist/generators/rpc/index.d.ts.map +1 -0
  19. package/dist/generators/rpc/index.js +20 -0
  20. package/dist/generators/rpc/index.js.map +1 -0
  21. package/dist/generators/rpc/rpc-generator.d.ts +150 -0
  22. package/dist/generators/rpc/rpc-generator.d.ts.map +1 -0
  23. package/dist/generators/rpc/rpc-generator.js +743 -0
  24. package/dist/generators/rpc/rpc-generator.js.map +1 -0
  25. package/dist/generators/rpc/templates/array.d.ts +29 -0
  26. package/dist/generators/rpc/templates/array.d.ts.map +1 -0
  27. package/dist/generators/rpc/templates/array.js +40 -0
  28. package/dist/generators/rpc/templates/array.js.map +1 -0
  29. package/dist/generators/rpc/templates/auth.d.ts +85 -0
  30. package/dist/generators/rpc/templates/auth.d.ts.map +1 -0
  31. package/dist/generators/rpc/templates/auth.js +233 -0
  32. package/dist/generators/rpc/templates/auth.js.map +1 -0
  33. package/dist/generators/rpc/templates/column.d.ts +39 -0
  34. package/dist/generators/rpc/templates/column.d.ts.map +1 -0
  35. package/dist/generators/rpc/templates/column.js +97 -0
  36. package/dist/generators/rpc/templates/column.js.map +1 -0
  37. package/dist/generators/rpc/templates/enum.d.ts +33 -0
  38. package/dist/generators/rpc/templates/enum.d.ts.map +1 -0
  39. package/dist/generators/rpc/templates/enum.js +93 -0
  40. package/dist/generators/rpc/templates/enum.js.map +1 -0
  41. package/dist/generators/rpc/templates/nullable.d.ts +31 -0
  42. package/dist/generators/rpc/templates/nullable.d.ts.map +1 -0
  43. package/dist/generators/rpc/templates/nullable.js +50 -0
  44. package/dist/generators/rpc/templates/nullable.js.map +1 -0
  45. package/dist/generators/rpc/templates/related.d.ts +47 -0
  46. package/dist/generators/rpc/templates/related.d.ts.map +1 -0
  47. package/dist/generators/rpc/templates/related.js +182 -0
  48. package/dist/generators/rpc/templates/related.js.map +1 -0
  49. package/dist/generators/rpc/templates/search.d.ts +42 -0
  50. package/dist/generators/rpc/templates/search.d.ts.map +1 -0
  51. package/dist/generators/rpc/templates/search.js +81 -0
  52. package/dist/generators/rpc/templates/search.js.map +1 -0
  53. package/dist/generators/rpc/templates/time.d.ts +44 -0
  54. package/dist/generators/rpc/templates/time.d.ts.map +1 -0
  55. package/dist/generators/rpc/templates/time.js +143 -0
  56. package/dist/generators/rpc/templates/time.js.map +1 -0
  57. package/dist/generators/rpc/utils.d.ts +58 -0
  58. package/dist/generators/rpc/utils.d.ts.map +1 -0
  59. package/dist/generators/rpc/utils.js +92 -0
  60. package/dist/generators/rpc/utils.js.map +1 -0
  61. package/dist/generators/rpc/validator.d.ts +21 -0
  62. package/dist/generators/rpc/validator.d.ts.map +1 -0
  63. package/dist/generators/rpc/validator.js +398 -0
  64. package/dist/generators/rpc/validator.js.map +1 -0
  65. package/dist/index.d.ts +9 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +6 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/shared/auth/index.d.ts +1 -1
  70. package/dist/shared/auth/index.d.ts.map +1 -1
  71. package/dist/shared/auth/routes.d.ts +4 -1
  72. package/dist/shared/auth/routes.d.ts.map +1 -1
  73. package/dist/shared/auth/routes.js +83 -1
  74. package/dist/shared/auth/routes.js.map +1 -1
  75. package/dist/shared/auth/types.d.ts +24 -0
  76. package/dist/shared/auth/types.d.ts.map +1 -1
  77. package/package.json +1 -1
@@ -0,0 +1,402 @@
1
+ /**
2
+ * Tetra RLS Generator v1.0
3
+ *
4
+ * Generates Row Level Security policies based on SparkBuddy production patterns.
5
+ * Every Tetra project MUST use these patterns for consistent security.
6
+ *
7
+ * NAMING STANDARD (no exceptions):
8
+ * - Organization column: organization_id
9
+ * - User column: user_id
10
+ * - All columns: snake_case
11
+ * - All roles: authenticated (never public)
12
+ *
13
+ * Table types:
14
+ * admin — Only admin/owner can CRUD. Most common. (default)
15
+ * user — Admins see all, users see only own data (user_id = auth.uid())
16
+ * creator — Creator role can INSERT/SELECT own + shared, admin sees all
17
+ * public — Anyone can SELECT, only admin can mutate
18
+ * public-active — Anyone can SELECT where is_active = true, only admin can mutate
19
+ * service — All client access blocked, only service_role can access
20
+ * superadmin — Only superadmins can manage (e.g., organizations table)
21
+ *
22
+ * Special tables (hardcoded, always the same in every project):
23
+ * organizations → superadmin type, org column = 'id'
24
+ * users_public → custom: self-insert, admin+self select/update
25
+ * organization_members → service type
26
+ * organization_invites → service type
27
+ *
28
+ * Prerequisites:
29
+ * - Auth functions from rls-auth-functions.sql must be installed
30
+ * - organization_members table with (user_id, organization_id, role, status)
31
+ * - users_public table with (id, is_superadmin)
32
+ */
33
+ // Standard column names — no overrides, no legacy
34
+ const ORG_COL = 'organization_id';
35
+ const USER_COL = 'user_id';
36
+ /**
37
+ * Generate RLS policies for a table.
38
+ *
39
+ * Usage:
40
+ * const result = generateRLS({ tableName: 'appointments', tableType: 'admin' });
41
+ * // Apply result.sql as a Supabase migration
42
+ */
43
+ export function generateRLS(config) {
44
+ const { tableName, tableType, visibilityColumn = 'visibility_level', publicVisibilityValues = ['shared', 'public'], allowAnonInsert = false, extraPolicies = [], } = config;
45
+ // Special tables get hardcoded treatment
46
+ const isSpecialTable = SPECIAL_TABLES[tableName];
47
+ if (isSpecialTable) {
48
+ return generateSpecialTable(tableName, isSpecialTable);
49
+ }
50
+ const policies = [];
51
+ const policyNames = [];
52
+ const prefix = tableName;
53
+ const header = generateHeader(tableName, tableType);
54
+ const drops = generateDrops(tableName, prefix);
55
+ switch (tableType) {
56
+ case 'admin':
57
+ policies.push(...generateAdminPolicies(tableName, prefix));
58
+ break;
59
+ case 'user':
60
+ policies.push(...generateUserPolicies(tableName, prefix));
61
+ break;
62
+ case 'creator':
63
+ policies.push(...generateCreatorPolicies(tableName, prefix, visibilityColumn, publicVisibilityValues));
64
+ break;
65
+ case 'public':
66
+ policies.push(...generatePublicPolicies(tableName, prefix, false, allowAnonInsert));
67
+ break;
68
+ case 'public-active':
69
+ policies.push(...generatePublicPolicies(tableName, prefix, true, allowAnonInsert));
70
+ break;
71
+ case 'service':
72
+ policies.push(...generateServicePolicies(tableName, prefix));
73
+ break;
74
+ case 'superadmin':
75
+ policies.push(...generateSuperadminPolicies(tableName, prefix));
76
+ break;
77
+ }
78
+ for (const policy of policies) {
79
+ const match = policy.match(/CREATE POLICY "([^"]+)"/);
80
+ if (match)
81
+ policyNames.push(match[1]);
82
+ }
83
+ if (extraPolicies.length > 0) {
84
+ policies.push('\n-- Custom policies');
85
+ policies.push(...extraPolicies);
86
+ }
87
+ const sql = [header, drops, '\n-- Policies', ...policies].join('\n');
88
+ return { sql, policyCount: policyNames.length + extraPolicies.length, tableType, policyNames };
89
+ }
90
+ // ─── Special Tables ────────────────────────────────────────────
91
+ // These tables ALWAYS have the same RLS in every Tetra project.
92
+ const SPECIAL_TABLES = {
93
+ organizations: 'organizations',
94
+ users_public: 'users_public',
95
+ organization_members: 'organization_members',
96
+ organization_invites: 'organization_invites',
97
+ };
98
+ function generateSpecialTable(tableName, type) {
99
+ const header = generateHeader(tableName, type);
100
+ const drops = generateDrops(tableName, tableName);
101
+ let policies;
102
+ switch (type) {
103
+ case 'organizations':
104
+ policies = [
105
+ `
106
+ -- Members can read their own org
107
+ CREATE POLICY "organizations_select" ON public.organizations
108
+ FOR SELECT TO authenticated
109
+ USING (id IN (SELECT public.auth_user_organizations()));`,
110
+ `
111
+ -- Only superadmins can insert
112
+ CREATE POLICY "organizations_insert" ON public.organizations
113
+ FOR INSERT TO authenticated
114
+ WITH CHECK (public.auth_is_superadmin());`,
115
+ `
116
+ -- Only superadmins can update
117
+ CREATE POLICY "organizations_update" ON public.organizations
118
+ FOR UPDATE TO authenticated
119
+ USING (public.auth_is_superadmin())
120
+ WITH CHECK (public.auth_is_superadmin());`,
121
+ `
122
+ -- Only superadmins can delete
123
+ CREATE POLICY "organizations_delete" ON public.organizations
124
+ FOR DELETE TO authenticated
125
+ USING (public.auth_is_superadmin());`,
126
+ ];
127
+ break;
128
+ case 'users_public':
129
+ policies = [
130
+ `
131
+ -- Users can see themselves + admins can see org members
132
+ CREATE POLICY "users_public_select" ON public.users_public
133
+ FOR SELECT TO authenticated
134
+ USING (
135
+ id = auth.uid()
136
+ OR active_organization_id IN (SELECT public.auth_admin_organizations())
137
+ );`,
138
+ `
139
+ -- Users can only insert their own record
140
+ CREATE POLICY "users_public_insert" ON public.users_public
141
+ FOR INSERT TO authenticated
142
+ WITH CHECK (id = auth.uid());`,
143
+ `
144
+ -- Users can update own profile, admins can update org members
145
+ CREATE POLICY "users_public_update" ON public.users_public
146
+ FOR UPDATE TO authenticated
147
+ USING (
148
+ id = auth.uid()
149
+ OR active_organization_id IN (SELECT public.auth_admin_organizations())
150
+ )
151
+ WITH CHECK (
152
+ id = auth.uid()
153
+ OR active_organization_id IN (SELECT public.auth_admin_organizations())
154
+ );`,
155
+ `
156
+ -- Only admins can delete org members
157
+ CREATE POLICY "users_public_delete" ON public.users_public
158
+ FOR DELETE TO authenticated
159
+ USING (active_organization_id IN (SELECT public.auth_admin_organizations()));`,
160
+ ];
161
+ break;
162
+ case 'organization_members':
163
+ case 'organization_invites':
164
+ policies = [
165
+ `
166
+ -- Block ALL client access. Only service_role can manage ${tableName}.
167
+ CREATE POLICY "${tableName}_service_only" ON public.${tableName}
168
+ FOR ALL
169
+ USING (false);`,
170
+ ];
171
+ break;
172
+ default:
173
+ policies = [];
174
+ }
175
+ const policyNames = [];
176
+ for (const p of policies) {
177
+ const match = p.match(/CREATE POLICY "([^"]+)"/);
178
+ if (match)
179
+ policyNames.push(match[1]);
180
+ }
181
+ const sql = [header, drops, '\n-- Policies', ...policies].join('\n');
182
+ return { sql, policyCount: policyNames.length, tableType: 'service', policyNames };
183
+ }
184
+ // ─── Helpers ───────────────────────────────────────────────────
185
+ function generateHeader(tableName, tableType) {
186
+ return `-- ============================================================================
187
+ -- RLS Policies for ${tableName}
188
+ -- ============================================================================
189
+ -- Table type: ${tableType}
190
+ -- Org column: ${ORG_COL} (Tetra standard, no exceptions)
191
+ -- User column: ${USER_COL} (Tetra standard, no exceptions)
192
+ -- Generated by: Tetra RLS Generator v1.0
193
+ -- Generated at: ${new Date().toISOString()}
194
+ -- ============================================================================
195
+
196
+ -- Enable RLS
197
+ ALTER TABLE public.${tableName} ENABLE ROW LEVEL SECURITY;
198
+ ALTER TABLE public.${tableName} FORCE ROW LEVEL SECURITY;
199
+
200
+ -- Drop existing policies (idempotent)`;
201
+ }
202
+ function generateDrops(tableName, prefix) {
203
+ const names = [
204
+ `${prefix}_select`, `${prefix}_insert`, `${prefix}_update`, `${prefix}_delete`,
205
+ `${prefix}_public_select`, `${prefix}_active_select`,
206
+ `${prefix}_anon_insert`, `${prefix}_service_only`,
207
+ ];
208
+ return names.map(p => `DROP POLICY IF EXISTS "${p}" ON public.${tableName};`).join('\n');
209
+ }
210
+ // ─── Admin Policies ────────────────────────────────────────────
211
+ function generateAdminPolicies(table, prefix) {
212
+ return [
213
+ `
214
+ CREATE POLICY "${prefix}_select" ON public.${table}
215
+ FOR SELECT TO authenticated
216
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
217
+ `
218
+ CREATE POLICY "${prefix}_insert" ON public.${table}
219
+ FOR INSERT TO authenticated
220
+ WITH CHECK (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
221
+ `
222
+ CREATE POLICY "${prefix}_update" ON public.${table}
223
+ FOR UPDATE TO authenticated
224
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()))
225
+ WITH CHECK (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
226
+ `
227
+ CREATE POLICY "${prefix}_delete" ON public.${table}
228
+ FOR DELETE TO authenticated
229
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
230
+ ];
231
+ }
232
+ // ─── User Policies ─────────────────────────────────────────────
233
+ function generateUserPolicies(table, prefix) {
234
+ return [
235
+ `
236
+ -- Admins see all org data, users see only their own records
237
+ CREATE POLICY "${prefix}_select" ON public.${table}
238
+ FOR SELECT TO authenticated
239
+ USING (
240
+ ${ORG_COL} IN (SELECT public.auth_admin_organizations())
241
+ OR (${ORG_COL} IN (SELECT public.auth_user_organizations()) AND ${USER_COL} = auth.uid())
242
+ );`,
243
+ `
244
+ -- Users can insert for their own org, tied to their user ID
245
+ CREATE POLICY "${prefix}_insert" ON public.${table}
246
+ FOR INSERT TO authenticated
247
+ WITH CHECK (
248
+ ${ORG_COL} IN (SELECT public.auth_user_organizations())
249
+ AND ${USER_COL} = auth.uid()
250
+ );`,
251
+ `
252
+ -- Admins can update all org records, users only their own
253
+ CREATE POLICY "${prefix}_update" ON public.${table}
254
+ FOR UPDATE TO authenticated
255
+ USING (
256
+ ${ORG_COL} IN (SELECT public.auth_admin_organizations())
257
+ OR (${ORG_COL} IN (SELECT public.auth_user_organizations()) AND ${USER_COL} = auth.uid())
258
+ )
259
+ WITH CHECK (
260
+ ${ORG_COL} IN (SELECT public.auth_admin_organizations())
261
+ OR (${ORG_COL} IN (SELECT public.auth_user_organizations()) AND ${USER_COL} = auth.uid())
262
+ );`,
263
+ `
264
+ -- Only admins can delete
265
+ CREATE POLICY "${prefix}_delete" ON public.${table}
266
+ FOR DELETE TO authenticated
267
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
268
+ ];
269
+ }
270
+ // ─── Creator Policies ──────────────────────────────────────────
271
+ function generateCreatorPolicies(table, prefix, visCol, publicValues) {
272
+ const valuesList = publicValues.map(v => `'${v}'`).join(', ');
273
+ return [
274
+ `
275
+ -- Admins see all, creators see own org content if shared/public
276
+ CREATE POLICY "${prefix}_select" ON public.${table}
277
+ FOR SELECT TO authenticated
278
+ USING (
279
+ ${ORG_COL} IN (SELECT public.auth_admin_organizations())
280
+ OR (${ORG_COL} IN (SELECT public.auth_creator_organizations()) AND ${visCol} IN (${valuesList}))
281
+ );`,
282
+ `
283
+ -- Creators can insert in their creator orgs
284
+ CREATE POLICY "${prefix}_insert" ON public.${table}
285
+ FOR INSERT TO authenticated
286
+ WITH CHECK (${ORG_COL} IN (SELECT public.auth_creator_organizations()));`,
287
+ `
288
+ -- Only admins can update
289
+ CREATE POLICY "${prefix}_update" ON public.${table}
290
+ FOR UPDATE TO authenticated
291
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()))
292
+ WITH CHECK (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
293
+ `
294
+ -- Only admins can delete
295
+ CREATE POLICY "${prefix}_delete" ON public.${table}
296
+ FOR DELETE TO authenticated
297
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
298
+ ];
299
+ }
300
+ // ─── Public Policies ───────────────────────────────────────────
301
+ function generatePublicPolicies(table, prefix, activeOnly, allowAnonInsert) {
302
+ const selectPolicy = activeOnly
303
+ ? `
304
+ -- Anyone can read active records
305
+ CREATE POLICY "${prefix}_active_select" ON public.${table}
306
+ FOR SELECT
307
+ USING (is_active = true);`
308
+ : `
309
+ -- Anyone can read
310
+ CREATE POLICY "${prefix}_public_select" ON public.${table}
311
+ FOR SELECT
312
+ USING (true);`;
313
+ const policies = [
314
+ selectPolicy,
315
+ `
316
+ -- Only admins can insert
317
+ CREATE POLICY "${prefix}_insert" ON public.${table}
318
+ FOR INSERT TO authenticated
319
+ WITH CHECK (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
320
+ `
321
+ -- Only admins can update
322
+ CREATE POLICY "${prefix}_update" ON public.${table}
323
+ FOR UPDATE TO authenticated
324
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()))
325
+ WITH CHECK (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
326
+ `
327
+ -- Only admins can delete
328
+ CREATE POLICY "${prefix}_delete" ON public.${table}
329
+ FOR DELETE TO authenticated
330
+ USING (${ORG_COL} IN (SELECT public.auth_admin_organizations()));`,
331
+ ];
332
+ if (allowAnonInsert) {
333
+ policies.push(`
334
+ -- Anonymous users can insert (signups, contact forms)
335
+ CREATE POLICY "${prefix}_anon_insert" ON public.${table}
336
+ FOR INSERT TO anon
337
+ WITH CHECK (true);`);
338
+ }
339
+ return policies;
340
+ }
341
+ // ─── Service Policies ──────────────────────────────────────────
342
+ function generateServicePolicies(table, prefix) {
343
+ return [
344
+ `
345
+ -- Block ALL client access. Only service_role (which bypasses RLS) can access.
346
+ CREATE POLICY "${prefix}_service_only" ON public.${table}
347
+ FOR ALL
348
+ USING (false);`,
349
+ ];
350
+ }
351
+ // ─── Superadmin Policies ───────────────────────────────────────
352
+ function generateSuperadminPolicies(table, prefix) {
353
+ return [
354
+ `
355
+ -- Members can read their own org records
356
+ CREATE POLICY "${prefix}_select" ON public.${table}
357
+ FOR SELECT TO authenticated
358
+ USING (${ORG_COL} IN (SELECT public.auth_user_organizations()));`,
359
+ `
360
+ -- Only superadmins can insert
361
+ CREATE POLICY "${prefix}_insert" ON public.${table}
362
+ FOR INSERT TO authenticated
363
+ WITH CHECK (public.auth_is_superadmin());`,
364
+ `
365
+ -- Only superadmins can update
366
+ CREATE POLICY "${prefix}_update" ON public.${table}
367
+ FOR UPDATE TO authenticated
368
+ USING (public.auth_is_superadmin())
369
+ WITH CHECK (public.auth_is_superadmin());`,
370
+ `
371
+ -- Only superadmins can delete
372
+ CREATE POLICY "${prefix}_delete" ON public.${table}
373
+ FOR DELETE TO authenticated
374
+ USING (public.auth_is_superadmin());`,
375
+ ];
376
+ }
377
+ // ─── Batch Generator ───────────────────────────────────────────
378
+ /**
379
+ * Generate RLS for multiple tables at once.
380
+ *
381
+ * Usage:
382
+ * const sql = generateRLSBatch([
383
+ * { tableName: 'appointments', tableType: 'admin' },
384
+ * { tableName: 'organizations', tableType: 'superadmin' },
385
+ * { tableName: 'organization_invites', tableType: 'service' },
386
+ * ]);
387
+ */
388
+ export function generateRLSBatch(configs) {
389
+ const results = configs.map(c => generateRLS(c));
390
+ const totalPolicies = results.reduce((sum, r) => sum + r.policyCount, 0);
391
+ const header = `-- ============================================================================
392
+ -- Tetra RLS Migration (Batch)
393
+ -- ============================================================================
394
+ -- Tables: ${configs.length}
395
+ -- Policies: ${totalPolicies}
396
+ -- Generated by: Tetra RLS Generator v1.0
397
+ -- Generated at: ${new Date().toISOString()}
398
+ -- ============================================================================
399
+ `;
400
+ return header + results.map(r => r.sql).join('\n\n');
401
+ }
402
+ //# sourceMappingURL=rls-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rls-generator.js","sourceRoot":"","sources":["../../src/generators/rls-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Detail RPC Generator
3
+ * Auto-generates SQL RPC functions for fetching single records with nested includes
4
+ *
5
+ * This generator creates get_<table>_detail(p_id uuid) RPC functions that:
6
+ * - Efficiently join nested relations using LATERAL joins
7
+ * - Apply computed fields at each nesting level
8
+ * - Return single JSONB object with all nested data
9
+ *
10
+ * Usage:
11
+ * const generator = new DetailRPCGenerator(productionsFeatureConfig);
12
+ * const detailSQL = generator.generate();
13
+ * generator.writeMigration();
14
+ */
15
+ import { FeatureConfig } from '../../shared/types/feature-config.js';
16
+ export interface DetailRPCGeneratorOptions {
17
+ /** Custom table alias map */
18
+ aliasMap?: Record<string, string>;
19
+ /** Base fields to include in detail response (overrides auto-detection) */
20
+ baseFields?: string[];
21
+ }
22
+ export declare class DetailRPCGenerator {
23
+ private config;
24
+ private tableName;
25
+ private tableAlias;
26
+ private aliasMap?;
27
+ private baseFieldsOverride?;
28
+ private readonly GENERATOR_VERSION;
29
+ constructor(config: FeatureConfig<any>, options?: DetailRPCGeneratorOptions);
30
+ /**
31
+ * Generate the detail RPC SQL
32
+ */
33
+ generate(): string;
34
+ /**
35
+ * Generate base table fields for SELECT
36
+ */
37
+ private generateBaseFields;
38
+ /**
39
+ * Generate subquery for an include
40
+ */
41
+ private generateIncludeSubquery;
42
+ /**
43
+ * Get JOIN condition based on foreign key
44
+ */
45
+ private getJoinCondition;
46
+ /**
47
+ * Extract column name from foreign key name
48
+ * Example: 'productionitems_user_id_fkey' -> 'user_id'
49
+ */
50
+ private extractColumnFromForeignKey;
51
+ /**
52
+ * Write migration file
53
+ */
54
+ writeMigration(options?: {
55
+ outputDir?: string;
56
+ }): string;
57
+ }
58
+ //# sourceMappingURL=detail-rpc-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail-rpc-generator.d.ts","sourceRoot":"","sources":["../../../src/generators/rpc/detail-rpc-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAIrE,MAAM,WAAW,yBAAyB;IACxC,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAyB;IAC1C,OAAO,CAAC,kBAAkB,CAAC,CAAW;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAE/B,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,yBAAyB;IAQ3E;;OAEG;IACH,QAAQ,IAAI,MAAM;IAiClB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAkB1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA+C/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IASnC;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;CAkBzD"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Detail RPC Generator
3
+ * Auto-generates SQL RPC functions for fetching single records with nested includes
4
+ *
5
+ * This generator creates get_<table>_detail(p_id uuid) RPC functions that:
6
+ * - Efficiently join nested relations using LATERAL joins
7
+ * - Apply computed fields at each nesting level
8
+ * - Return single JSONB object with all nested data
9
+ *
10
+ * Usage:
11
+ * const generator = new DetailRPCGenerator(productionsFeatureConfig);
12
+ * const detailSQL = generator.generate();
13
+ * generator.writeMigration();
14
+ */
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import { generateTimestamp, getTableAlias } from './utils.js';
18
+ export class DetailRPCGenerator {
19
+ config;
20
+ tableName;
21
+ tableAlias;
22
+ aliasMap;
23
+ baseFieldsOverride;
24
+ GENERATOR_VERSION = '1.0';
25
+ constructor(config, options) {
26
+ this.config = config;
27
+ this.tableName = config.tableName;
28
+ this.aliasMap = options?.aliasMap;
29
+ this.tableAlias = getTableAlias(this.tableName, this.aliasMap);
30
+ this.baseFieldsOverride = options?.baseFields;
31
+ }
32
+ /**
33
+ * Generate the detail RPC SQL
34
+ */
35
+ generate() {
36
+ const rpcName = `get_${this.tableName}_detail`;
37
+ const includeMapping = this.config.includeMapping || {};
38
+ // Build SELECT for base table fields
39
+ const baseFields = this.generateBaseFields();
40
+ // Build nested includes
41
+ const nestedIncludes = Object.entries(includeMapping)
42
+ .map(([key, config]) => this.generateIncludeSubquery(key, config, this.tableAlias))
43
+ .join(',\n ');
44
+ const sql = `-- ============================================
45
+ -- Detail RPC for ${this.tableName}
46
+ -- Generated by DetailRPCGenerator v${this.GENERATOR_VERSION}
47
+ -- ============================================
48
+
49
+ CREATE OR REPLACE FUNCTION ${rpcName}(p_id uuid)
50
+ RETURNS jsonb AS $$
51
+ SELECT jsonb_build_object(
52
+ ${baseFields}${nestedIncludes ? ',\n ' + nestedIncludes : ''}
53
+ )
54
+ FROM ${this.tableName} ${this.tableAlias}
55
+ WHERE ${this.tableAlias}.id = p_id;
56
+ $$ LANGUAGE sql STABLE;
57
+
58
+ -- Usage:
59
+ -- SELECT * FROM ${rpcName}('uuid-here');
60
+ `;
61
+ return sql;
62
+ }
63
+ /**
64
+ * Generate base table fields for SELECT
65
+ */
66
+ generateBaseFields() {
67
+ if (this.baseFieldsOverride) {
68
+ return this.baseFieldsOverride
69
+ .map(f => `'${f}', ${this.tableAlias}.${f}`)
70
+ .join(',\n ');
71
+ }
72
+ // Default base fields - projects should override via options.baseFields
73
+ return `'id', ${this.tableAlias}.id,
74
+ 'created_at', ${this.tableAlias}.created_at,
75
+ 'title', ${this.tableAlias}.title,
76
+ 'status', ${this.tableAlias}.status,
77
+ 'notes', ${this.tableAlias}.notes,
78
+ 'user_id', ${this.tableAlias}.user_id,
79
+ 'organization_id', ${this.tableAlias}.organization_id,
80
+ 'current_phase', ${this.tableAlias}.current_phase`;
81
+ }
82
+ /**
83
+ * Generate subquery for an include
84
+ */
85
+ generateIncludeSubquery(includeKey, includeConfig, parentAlias, depth = 0) {
86
+ const indent = ' '.repeat(depth + 2);
87
+ const childTableName = includeConfig.tableName || includeKey;
88
+ const childAlias = `${getTableAlias(childTableName, this.aliasMap)}${depth}`;
89
+ // Determine join condition
90
+ const joinCondition = this.getJoinCondition(includeConfig, parentAlias, childAlias);
91
+ // Build fields for this level
92
+ const fields = includeConfig.fields
93
+ .map(field => `'${field}', ${childAlias}.${field}`)
94
+ .join(',\n' + indent + ' ');
95
+ // Build computed fields for this level
96
+ const computedFields = includeConfig.computedFields
97
+ ? Object.entries(includeConfig.computedFields)
98
+ .map(([key, expr]) => `'${key}', ${expr.replace(/\b(\w+)\b/g, `${childAlias}.$1`)}`)
99
+ .join(',\n' + indent + ' ')
100
+ : '';
101
+ // Build nested includes for this level
102
+ const nestedIncludes = includeConfig.nested
103
+ ? Object.entries(includeConfig.nested)
104
+ .map(([nestedKey, nestedConfig]) => this.generateIncludeSubquery(nestedKey, nestedConfig, childAlias, depth + 1))
105
+ .join(',\n' + indent + ' ')
106
+ : '';
107
+ const allFields = [fields, computedFields, nestedIncludes]
108
+ .filter(Boolean)
109
+ .join(',\n' + indent + ' ');
110
+ return `'${includeKey}', (
111
+ ${indent} SELECT jsonb_agg(jsonb_build_object(
112
+ ${indent} ${allFields}
113
+ ${indent} ))
114
+ ${indent} FROM ${childTableName} ${childAlias}
115
+ ${indent} ${joinCondition}
116
+ ${indent})`;
117
+ }
118
+ /**
119
+ * Get JOIN condition based on foreign key
120
+ */
121
+ getJoinCondition(includeConfig, parentAlias, childAlias) {
122
+ if (includeConfig.isReverseRelation) {
123
+ // Reverse relation: child points to parent
124
+ const foreignKeyColumn = includeConfig.column || this.extractColumnFromForeignKey(includeConfig.foreignKey);
125
+ return `WHERE ${childAlias}.${foreignKeyColumn} = ${parentAlias}.id`;
126
+ }
127
+ else {
128
+ // Forward relation: parent points to child
129
+ const foreignKeyColumn = includeConfig.column || this.extractColumnFromForeignKey(includeConfig.foreignKey);
130
+ return `WHERE ${childAlias}.id = ${parentAlias}.${foreignKeyColumn}`;
131
+ }
132
+ }
133
+ /**
134
+ * Extract column name from foreign key name
135
+ * Example: 'productionitems_user_id_fkey' -> 'user_id'
136
+ */
137
+ extractColumnFromForeignKey(foreignKey) {
138
+ if (!foreignKey)
139
+ return 'id';
140
+ // Pattern: tablename_columnname_fkey
141
+ const parts = foreignKey.split('_');
142
+ // Remove first part (table name) and last part ('fkey')
143
+ return parts.slice(1, -1).join('_');
144
+ }
145
+ /**
146
+ * Write migration file
147
+ */
148
+ writeMigration(options) {
149
+ const timestamp = generateTimestamp();
150
+ const fileName = `${timestamp}_create_get_${this.tableName}_detail.sql`;
151
+ const migrationsDir = options?.outputDir || path.join(process.cwd(), 'supabase', 'migrations');
152
+ const filePath = path.join(migrationsDir, fileName);
153
+ const sql = this.generate();
154
+ // Ensure migrations directory exists
155
+ if (!fs.existsSync(migrationsDir)) {
156
+ fs.mkdirSync(migrationsDir, { recursive: true });
157
+ }
158
+ fs.writeFileSync(filePath, sql);
159
+ console.log(`Created migration: ${fileName}`);
160
+ return filePath;
161
+ }
162
+ }
163
+ //# sourceMappingURL=detail-rpc-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail-rpc-generator.js","sourceRoot":"","sources":["../../../src/generators/rpc/detail-rpc-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAS9D,MAAM,OAAO,kBAAkB;IACrB,MAAM,CAAqB;IAC3B,SAAS,CAAS;IAClB,UAAU,CAAS;IACnB,QAAQ,CAA0B;IAClC,kBAAkB,CAAY;IACrB,iBAAiB,GAAG,KAAK,CAAC;IAE3C,YAAY,MAA0B,EAAE,OAAmC;QACzE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC;QAClC,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,kBAAkB,GAAG,OAAO,EAAE,UAAU,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,SAAS,SAAS,CAAC;QAC/C,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;QAExD,qCAAqC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE7C,wBAAwB;QACxB,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;aAClD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,uBAAuB,CAAC,GAAG,EAAE,MAAuB,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;aACnG,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnB,MAAM,GAAG,GAAG;oBACI,IAAI,CAAC,SAAS;sCACI,IAAI,CAAC,iBAAiB;;;6BAG/B,OAAO;;;MAG9B,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE;;SAE1D,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,UAAU;UAChC,IAAI,CAAC,UAAU;;;;mBAIN,OAAO;CACzB,CAAC;QAEE,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,kBAAkB;iBAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;iBAC3C,IAAI,CAAC,SAAS,CAAC,CAAC;QACrB,CAAC;QAED,wEAAwE;QACxE,OAAO,SAAS,IAAI,CAAC,UAAU;oBACf,IAAI,CAAC,UAAU;eACpB,IAAI,CAAC,UAAU;gBACd,IAAI,CAAC,UAAU;eAChB,IAAI,CAAC,UAAU;iBACb,IAAI,CAAC,UAAU;yBACP,IAAI,CAAC,UAAU;uBACjB,IAAI,CAAC,UAAU,gBAAgB,CAAC;IACrD,CAAC;IAED;;OAEG;IACK,uBAAuB,CAC7B,UAAkB,EAClB,aAA4B,EAC5B,WAAmB,EACnB,QAAgB,CAAC;QAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACtC,MAAM,cAAc,GAAG,aAAa,CAAC,SAAS,IAAI,UAAU,CAAC;QAC7D,MAAM,UAAU,GAAG,GAAG,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,KAAK,EAAE,CAAC;QAE7E,2BAA2B;QAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;QAEpF,8BAA8B;QAC9B,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM;aAChC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM,UAAU,IAAI,KAAK,EAAE,CAAC;aAClD,IAAI,CAAC,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;QAErC,uCAAuC;QACvC,MAAM,cAAc,GAAG,aAAa,CAAC,cAAc;YACjD,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC;iBACzC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,UAAU,KAAK,CAAC,EAAE,CAAC;iBACnF,IAAI,CAAC,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;YACtC,CAAC,CAAC,EAAE,CAAC;QAEP,uCAAuC;QACvC,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM;YACzC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC;iBACjC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,EAAE,CACjC,IAAI,CAAC,uBAAuB,CAAC,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,GAAG,CAAC,CAAC,CAC7E;iBACA,IAAI,CAAC,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;YACtC,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,cAAc,CAAC;aACvD,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;QAErC,OAAO,IAAI,UAAU;EACvB,MAAM;EACN,MAAM,WAAW,SAAS;EAC1B,MAAM;EACN,MAAM,UAAU,cAAc,IAAI,UAAU;EAC5C,MAAM,KAAK,aAAa;EACxB,MAAM,GAAG,CAAC;IACV,CAAC;IAED;;OAEG;IACK,gBAAgB,CACtB,aAA4B,EAC5B,WAAmB,EACnB,UAAkB;QAElB,IAAI,aAAa,CAAC,iBAAiB,EAAE,CAAC;YACpC,2CAA2C;YAC3C,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC,2BAA2B,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC5G,OAAO,SAAS,UAAU,IAAI,gBAAgB,MAAM,WAAW,KAAK,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,2CAA2C;YAC3C,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC,2BAA2B,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC5G,OAAO,SAAS,UAAU,SAAS,WAAW,IAAI,gBAAgB,EAAE,CAAC;QACvE,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,2BAA2B,CAAC,UAAyB;QAC3D,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAE7B,qCAAqC;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,wDAAwD;QACxD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,OAAgC;QAC7C,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,GAAG,SAAS,eAAe,IAAI,CAAC,SAAS,aAAa,CAAC;QACxE,MAAM,aAAa,GAAG,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;QAC/F,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QAEpD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAE5B,qCAAqC;QACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAEhC,OAAO,CAAC,GAAG,CAAC,sBAAsB,QAAQ,EAAE,CAAC,CAAC;QAC9C,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * RPC Generator — Auto-generates SQL RPC functions from FeatureConfig
3
+ *
4
+ * Usage:
5
+ * ```typescript
6
+ * import { RPCGenerator, DetailRPCGenerator, validateConfig } from '@soulbatical/tetra-core';
7
+ *
8
+ * const generator = new RPCGenerator(ordersFeatureConfig, {
9
+ * aliasMap: { orders: 'o', orderitems: 'oi' },
10
+ * });
11
+ * const { resultsSQL, countsSQL } = generator.generate();
12
+ * generator.writeMigrations({ force: true });
13
+ * ```
14
+ */
15
+ export { RPCGenerator } from './rpc-generator.js';
16
+ export type { GeneratedSQL, FilterDefinition, RPCGeneratorOptions } from './rpc-generator.js';
17
+ export { DetailRPCGenerator } from './detail-rpc-generator.js';
18
+ export type { DetailRPCGeneratorOptions } from './detail-rpc-generator.js';
19
+ export { validateConfig } from './validator.js';
20
+ export type { ValidationResult, ValidationError } from './validator.js';
21
+ export { generateAuthCheck, generateAuthWhereClause, generateAuthDeclarations } from './templates/auth.js';
22
+ export type { AccessLevel, CreatorVisibilityConfig } from './templates/auth.js';
23
+ export { generateTimestamp, getTableAlias, escapeIdentifier, capitalize, kebabToCamelCase, snakeToCamelCase, filterNameToCountsKey, indent, } from './utils.js';
24
+ //# sourceMappingURL=index.d.ts.map