aios-core 2.2.1 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.aios-core/.session/current-session.json +14 -14
  2. package/.aios-core/cli/commands/migrate/validate.js +1 -1
  3. package/.aios-core/core/docs/session-update-pattern.md +17 -10
  4. package/.aios-core/core/elicitation/elicitation-engine.js +11 -6
  5. package/.aios-core/core/elicitation/session-manager.js +2 -1
  6. package/.aios-core/core/registry/registry-schema.json +166 -166
  7. package/.aios-core/core/registry/service-registry.json +6585 -6585
  8. package/.aios-core/core-config.yaml +12 -1
  9. package/.aios-core/data/agent-config-requirements.yaml +5 -5
  10. package/.aios-core/development/agents/devops.md +12 -0
  11. package/.aios-core/development/scripts/squad/README.md +112 -0
  12. package/.aios-core/development/scripts/squad/index.js +41 -0
  13. package/.aios-core/development/scripts/squad/squad-loader.js +359 -0
  14. package/.aios-core/development/scripts/squad/squad-validator.js +685 -0
  15. package/.aios-core/development/tasks/add-mcp.md +11 -5
  16. package/.aios-core/development/tasks/search-mcp.md +309 -0
  17. package/.aios-core/development/tasks/setup-mcp-docker.md +11 -8
  18. package/.aios-core/development/tasks/squad-creator-validate.md +151 -0
  19. package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +3 -3
  20. package/.aios-core/index.d.ts +7 -7
  21. package/.aios-core/index.js +1 -1
  22. package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
  23. package/.aios-core/infrastructure/scripts/component-generator.js +1 -1
  24. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  25. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  26. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  27. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  28. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  29. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  30. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  31. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  32. package/.aios-core/infrastructure/tests/utilities-audit-results.json +500 -500
  33. package/.aios-core/infrastructure/tools/README.md +1 -1
  34. package/.aios-core/install-manifest.yaml +4 -1
  35. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  36. package/.aios-core/manifests/workers.csv +203 -203
  37. package/.aios-core/package.json +102 -102
  38. package/.aios-core/product/templates/activation-instructions-template.md +7 -7
  39. package/.aios-core/product/templates/adr.hbs +125 -125
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/dbdr.hbs +241 -241
  42. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  43. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  44. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  46. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  47. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  48. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  49. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  50. package/.aios-core/product/templates/epic.hbs +212 -212
  51. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  52. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  53. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  54. package/.aios-core/product/templates/pmdr.hbs +186 -186
  55. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  56. package/.aios-core/product/templates/prd.hbs +201 -201
  57. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  58. package/.aios-core/product/templates/story.hbs +263 -263
  59. package/.aios-core/product/templates/task.hbs +170 -170
  60. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  61. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  62. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  63. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  64. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  65. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  66. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  67. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  68. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  69. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  70. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  71. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  72. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  73. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  74. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  75. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  76. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  77. package/.aios-core/schemas/squad-schema.json +185 -0
  78. package/.aios-core/scripts/README.md +90 -322
  79. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  80. package/.claude/rules/mcp-usage.md +116 -100
  81. package/LICENSE +48 -48
  82. package/README.md +3 -4
  83. package/bin/aios-init.js +11 -6
  84. package/bin/aios.js +2 -1
  85. package/package.json +2 -3
  86. package/packages/installer/package.json +39 -39
  87. package/packages/installer/tests/integration/environment-configuration.test.js +2 -2
  88. package/packages/installer/tests/unit/env-template.test.js +4 -3
  89. package/templates/squad/LICENSE +21 -21
  90. package/templates/squad/README.md +37 -37
  91. package/templates/squad/agents/example-agent.yaml +36 -36
  92. package/templates/squad/package.json +19 -19
  93. package/templates/squad/squad.yaml +25 -25
  94. package/templates/squad/tasks/example-task.yaml +46 -46
  95. package/templates/squad/templates/example-template.md +24 -24
  96. package/templates/squad/tests/example-agent.test.js +53 -53
  97. package/templates/squad/workflows/example-workflow.yaml +54 -54
  98. package/tools/diagnose-npx-issue.ps1 +96 -96
  99. package/tools/quick-diagnose.cmd +85 -85
  100. package/tools/quick-diagnose.ps1 +117 -117
  101. package/.aios-core/core/data/agent-config-requirements.yaml +0 -368
  102. package/.aios-core/core/data/aios-kb.md +0 -924
  103. package/.aios-core/core/data/workflow-patterns.yaml +0 -267
  104. package/.aios-core/product/templates/1mcp-config.yaml +0 -225
  105. package/.aios-core/scripts/context-detector.js +0 -226
  106. package/.aios-core/scripts/elicitation-engine.js +0 -385
  107. package/.aios-core/scripts/elicitation-session-manager.js +0 -300
  108. package/.claude/CLAUDE.md +0 -221
@@ -1,133 +1,133 @@
1
- -- Materialized View Template
2
- -- View: :view_name
3
- -- Created: :created_date
4
- -- Author: :author
5
- -- Description: :description
6
- --
7
- -- Materialized views cache query results for performance
8
- -- IMPORTANT: Data is not live - must be refreshed periodically
9
-
10
- -- =============================================================================
11
- -- BASIC MATERIALIZED VIEW
12
- -- =============================================================================
13
-
14
- -- Drop existing view if exists
15
- DROP MATERIALIZED VIEW IF EXISTS :view_name CASCADE;
16
-
17
- -- Create materialized view
18
- CREATE MATERIALIZED VIEW :view_name AS
19
- SELECT
20
- t.id,
21
- t.name,
22
- t.category,
23
- t.created_at,
24
- -- Aggregations
25
- COUNT(r.id) AS related_count,
26
- SUM(r.amount) AS total_amount,
27
- AVG(r.rating) AS avg_rating,
28
- -- Computed columns
29
- CASE
30
- WHEN t.status = 'active' THEN true
31
- ELSE false
32
- END AS is_active
33
- FROM :base_table t
34
- LEFT JOIN :related_table r ON r.parent_id = t.id
35
- WHERE t.deleted_at IS NULL
36
- GROUP BY t.id, t.name, t.category, t.created_at, t.status
37
- -- Store data sorted for efficient access
38
- WITH DATA;
39
-
40
- -- =============================================================================
41
- -- INDEXES ON MATERIALIZED VIEW
42
- -- =============================================================================
43
-
44
- -- Unique index (required for CONCURRENT refresh)
45
- CREATE UNIQUE INDEX IF NOT EXISTS idx_:view_name_id
46
- ON :view_name (id);
47
-
48
- -- Additional indexes for common queries
49
- CREATE INDEX IF NOT EXISTS idx_:view_name_category
50
- ON :view_name (category);
51
-
52
- CREATE INDEX IF NOT EXISTS idx_:view_name_created_at
53
- ON :view_name (created_at DESC);
54
-
55
- -- =============================================================================
56
- -- REFRESH FUNCTION
57
- -- =============================================================================
58
-
59
- -- Function to refresh the materialized view
60
- CREATE OR REPLACE FUNCTION refresh_:view_name()
61
- RETURNS void
62
- LANGUAGE plpgsql
63
- SECURITY DEFINER
64
- AS $$
65
- BEGIN
66
- -- CONCURRENTLY allows queries during refresh (requires unique index)
67
- REFRESH MATERIALIZED VIEW CONCURRENTLY :view_name;
68
-
69
- -- Log refresh
70
- RAISE NOTICE 'Materialized view :view_name refreshed at %', NOW();
71
- END;
72
- $$;
73
-
74
- -- Grant execute to service role (for scheduled refresh)
75
- GRANT EXECUTE ON FUNCTION refresh_:view_name() TO service_role;
76
-
77
- -- =============================================================================
78
- -- SCHEDULED REFRESH (pg_cron)
79
- -- =============================================================================
80
- --
81
- -- If using pg_cron extension (available in Supabase):
82
- --
83
- -- Enable extension (one-time, requires superuser)
84
- -- CREATE EXTENSION IF NOT EXISTS pg_cron;
85
- --
86
- -- Schedule refresh every hour
87
- -- SELECT cron.schedule(
88
- -- 'refresh-:view_name',
89
- -- '0 * * * *', -- Every hour at minute 0
90
- -- $$SELECT refresh_:view_name()$$
91
- -- );
92
- --
93
- -- To remove schedule:
94
- -- SELECT cron.unschedule('refresh-:view_name');
95
-
96
- -- =============================================================================
97
- -- TRIGGER-BASED REFRESH (Alternative)
98
- -- =============================================================================
99
-
100
- -- Refresh when base table changes (use with caution - can be slow)
101
- -- CREATE OR REPLACE FUNCTION refresh_:view_name_trigger()
102
- -- RETURNS TRIGGER AS $$
103
- -- BEGIN
104
- -- REFRESH MATERIALIZED VIEW CONCURRENTLY :view_name;
105
- -- RETURN NULL;
106
- -- END;
107
- -- $$ LANGUAGE plpgsql;
108
- --
109
- -- CREATE TRIGGER trigger_refresh_:view_name
110
- -- AFTER INSERT OR UPDATE OR DELETE ON :base_table
111
- -- FOR EACH STATEMENT
112
- -- EXECUTE FUNCTION refresh_:view_name_trigger();
113
-
114
- -- =============================================================================
115
- -- VIEW METADATA
116
- -- =============================================================================
117
-
118
- COMMENT ON MATERIALIZED VIEW :view_name IS ':description. Refresh: hourly or on-demand.';
119
-
120
- -- =============================================================================
121
- -- USAGE
122
- -- =============================================================================
123
- --
124
- -- Query the view (cached data, very fast):
125
- -- SELECT * FROM :view_name WHERE category = 'example';
126
- --
127
- -- Manual refresh (use during low-traffic periods):
128
- -- REFRESH MATERIALIZED VIEW CONCURRENTLY :view_name;
129
- -- OR
130
- -- SELECT refresh_:view_name();
131
- --
132
- -- Check last refresh time (approximate):
133
- -- SELECT pg_stat_get_last_analyze_time(':view_name'::regclass);
1
+ -- Materialized View Template
2
+ -- View: :view_name
3
+ -- Created: :created_date
4
+ -- Author: :author
5
+ -- Description: :description
6
+ --
7
+ -- Materialized views cache query results for performance
8
+ -- IMPORTANT: Data is not live - must be refreshed periodically
9
+
10
+ -- =============================================================================
11
+ -- BASIC MATERIALIZED VIEW
12
+ -- =============================================================================
13
+
14
+ -- Drop existing view if exists
15
+ DROP MATERIALIZED VIEW IF EXISTS :view_name CASCADE;
16
+
17
+ -- Create materialized view
18
+ CREATE MATERIALIZED VIEW :view_name AS
19
+ SELECT
20
+ t.id,
21
+ t.name,
22
+ t.category,
23
+ t.created_at,
24
+ -- Aggregations
25
+ COUNT(r.id) AS related_count,
26
+ SUM(r.amount) AS total_amount,
27
+ AVG(r.rating) AS avg_rating,
28
+ -- Computed columns
29
+ CASE
30
+ WHEN t.status = 'active' THEN true
31
+ ELSE false
32
+ END AS is_active
33
+ FROM :base_table t
34
+ LEFT JOIN :related_table r ON r.parent_id = t.id
35
+ WHERE t.deleted_at IS NULL
36
+ GROUP BY t.id, t.name, t.category, t.created_at, t.status
37
+ -- Store data sorted for efficient access
38
+ WITH DATA;
39
+
40
+ -- =============================================================================
41
+ -- INDEXES ON MATERIALIZED VIEW
42
+ -- =============================================================================
43
+
44
+ -- Unique index (required for CONCURRENT refresh)
45
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_:view_name_id
46
+ ON :view_name (id);
47
+
48
+ -- Additional indexes for common queries
49
+ CREATE INDEX IF NOT EXISTS idx_:view_name_category
50
+ ON :view_name (category);
51
+
52
+ CREATE INDEX IF NOT EXISTS idx_:view_name_created_at
53
+ ON :view_name (created_at DESC);
54
+
55
+ -- =============================================================================
56
+ -- REFRESH FUNCTION
57
+ -- =============================================================================
58
+
59
+ -- Function to refresh the materialized view
60
+ CREATE OR REPLACE FUNCTION refresh_:view_name()
61
+ RETURNS void
62
+ LANGUAGE plpgsql
63
+ SECURITY DEFINER
64
+ AS $$
65
+ BEGIN
66
+ -- CONCURRENTLY allows queries during refresh (requires unique index)
67
+ REFRESH MATERIALIZED VIEW CONCURRENTLY :view_name;
68
+
69
+ -- Log refresh
70
+ RAISE NOTICE 'Materialized view :view_name refreshed at %', NOW();
71
+ END;
72
+ $$;
73
+
74
+ -- Grant execute to service role (for scheduled refresh)
75
+ GRANT EXECUTE ON FUNCTION refresh_:view_name() TO service_role;
76
+
77
+ -- =============================================================================
78
+ -- SCHEDULED REFRESH (pg_cron)
79
+ -- =============================================================================
80
+ --
81
+ -- If using pg_cron extension (available in Supabase):
82
+ --
83
+ -- Enable extension (one-time, requires superuser)
84
+ -- CREATE EXTENSION IF NOT EXISTS pg_cron;
85
+ --
86
+ -- Schedule refresh every hour
87
+ -- SELECT cron.schedule(
88
+ -- 'refresh-:view_name',
89
+ -- '0 * * * *', -- Every hour at minute 0
90
+ -- $$SELECT refresh_:view_name()$$
91
+ -- );
92
+ --
93
+ -- To remove schedule:
94
+ -- SELECT cron.unschedule('refresh-:view_name');
95
+
96
+ -- =============================================================================
97
+ -- TRIGGER-BASED REFRESH (Alternative)
98
+ -- =============================================================================
99
+
100
+ -- Refresh when base table changes (use with caution - can be slow)
101
+ -- CREATE OR REPLACE FUNCTION refresh_:view_name_trigger()
102
+ -- RETURNS TRIGGER AS $$
103
+ -- BEGIN
104
+ -- REFRESH MATERIALIZED VIEW CONCURRENTLY :view_name;
105
+ -- RETURN NULL;
106
+ -- END;
107
+ -- $$ LANGUAGE plpgsql;
108
+ --
109
+ -- CREATE TRIGGER trigger_refresh_:view_name
110
+ -- AFTER INSERT OR UPDATE OR DELETE ON :base_table
111
+ -- FOR EACH STATEMENT
112
+ -- EXECUTE FUNCTION refresh_:view_name_trigger();
113
+
114
+ -- =============================================================================
115
+ -- VIEW METADATA
116
+ -- =============================================================================
117
+
118
+ COMMENT ON MATERIALIZED VIEW :view_name IS ':description. Refresh: hourly or on-demand.';
119
+
120
+ -- =============================================================================
121
+ -- USAGE
122
+ -- =============================================================================
123
+ --
124
+ -- Query the view (cached data, very fast):
125
+ -- SELECT * FROM :view_name WHERE category = 'example';
126
+ --
127
+ -- Manual refresh (use during low-traffic periods):
128
+ -- REFRESH MATERIALIZED VIEW CONCURRENTLY :view_name;
129
+ -- OR
130
+ -- SELECT refresh_:view_name();
131
+ --
132
+ -- Check last refresh time (approximate):
133
+ -- SELECT pg_stat_get_last_analyze_time(':view_name'::regclass);
@@ -1,177 +1,177 @@
1
- -- View Template
2
- -- View: :view_name
3
- -- Created: :created_date
4
- -- Author: :author
5
- -- Description: :description
6
- --
7
- -- Views are virtual tables that provide a simplified interface
8
- -- Data is always live (not cached like materialized views)
9
-
10
- -- =============================================================================
11
- -- BASIC VIEW
12
- -- =============================================================================
13
-
14
- -- Drop existing view if exists
15
- DROP VIEW IF EXISTS :view_name CASCADE;
16
-
17
- -- Create view
18
- CREATE OR REPLACE VIEW :view_name AS
19
- SELECT
20
- t.id,
21
- t.name,
22
- t.description,
23
- t.status,
24
- t.created_at,
25
- t.updated_at,
26
- -- Related data
27
- u.email AS owner_email,
28
- u.raw_user_meta_data->>'full_name' AS owner_name,
29
- -- Computed columns
30
- CASE
31
- WHEN t.status = 'active' THEN 'Active'
32
- WHEN t.status = 'pending' THEN 'Pending'
33
- WHEN t.status = 'archived' THEN 'Archived'
34
- ELSE 'Unknown'
35
- END AS status_label,
36
- -- Age calculation
37
- EXTRACT(DAY FROM NOW() - t.created_at) AS days_old
38
- FROM :base_table t
39
- LEFT JOIN auth.users u ON u.id = t.user_id
40
- WHERE t.deleted_at IS NULL;
41
-
42
- -- =============================================================================
43
- -- VIEW WITH AGGREGATIONS
44
- -- =============================================================================
45
-
46
- CREATE OR REPLACE VIEW :view_name_summary AS
47
- SELECT
48
- t.category,
49
- COUNT(*) AS total_count,
50
- COUNT(*) FILTER (WHERE t.status = 'active') AS active_count,
51
- COUNT(*) FILTER (WHERE t.status = 'pending') AS pending_count,
52
- MIN(t.created_at) AS earliest,
53
- MAX(t.created_at) AS latest,
54
- AVG(t.value)::NUMERIC(10,2) AS avg_value
55
- FROM :base_table t
56
- WHERE t.deleted_at IS NULL
57
- GROUP BY t.category;
58
-
59
- -- =============================================================================
60
- -- VIEW WITH RLS CONSIDERATION
61
- -- =============================================================================
62
-
63
- -- Views inherit RLS from underlying tables
64
- -- If base table has RLS, view will respect it
65
- --
66
- -- For views that need custom RLS-like behavior,
67
- -- wrap in a SECURITY DEFINER function:
68
-
69
- CREATE OR REPLACE FUNCTION get_:view_name_for_user()
70
- RETURNS TABLE (
71
- id UUID,
72
- name TEXT,
73
- status TEXT,
74
- created_at TIMESTAMPTZ
75
- )
76
- LANGUAGE plpgsql
77
- SECURITY INVOKER
78
- STABLE
79
- AS $$
80
- BEGIN
81
- RETURN QUERY
82
- SELECT
83
- v.id,
84
- v.name,
85
- v.status,
86
- v.created_at
87
- FROM :view_name v
88
- WHERE v.user_id = auth.uid()
89
- ORDER BY v.created_at DESC;
90
- END;
91
- $$;
92
-
93
- -- =============================================================================
94
- -- VIEW WITH JOINED DATA
95
- -- =============================================================================
96
-
97
- CREATE OR REPLACE VIEW :view_name_detailed AS
98
- SELECT
99
- t.id,
100
- t.name,
101
- t.status,
102
- -- One-to-one relationship
103
- d.detail_field,
104
- -- One-to-many aggregation
105
- (
106
- SELECT json_agg(json_build_object(
107
- 'id', c.id,
108
- 'name', c.name,
109
- 'created_at', c.created_at
110
- ))
111
- FROM :child_table c
112
- WHERE c.parent_id = t.id
113
- ) AS children,
114
- -- Count
115
- (
116
- SELECT COUNT(*)
117
- FROM :child_table c
118
- WHERE c.parent_id = t.id
119
- ) AS children_count,
120
- t.created_at,
121
- t.updated_at
122
- FROM :base_table t
123
- LEFT JOIN :detail_table d ON d.id = t.detail_id
124
- WHERE t.deleted_at IS NULL;
125
-
126
- -- =============================================================================
127
- -- UPDATEABLE VIEW (with INSTEAD OF triggers)
128
- -- =============================================================================
129
-
130
- -- Simple updateable view (for basic cases)
131
- CREATE OR REPLACE VIEW :view_name_editable AS
132
- SELECT
133
- id,
134
- name,
135
- description,
136
- status,
137
- updated_at
138
- FROM :base_table
139
- WHERE deleted_at IS NULL;
140
-
141
- -- Make it updateable via trigger
142
- CREATE OR REPLACE FUNCTION :view_name_editable_update()
143
- RETURNS TRIGGER AS $$
144
- BEGIN
145
- UPDATE :base_table
146
- SET
147
- name = NEW.name,
148
- description = NEW.description,
149
- status = NEW.status,
150
- updated_at = NOW()
151
- WHERE id = NEW.id
152
- AND user_id = auth.uid(); -- RLS check
153
-
154
- RETURN NEW;
155
- END;
156
- $$ LANGUAGE plpgsql SECURITY DEFINER;
157
-
158
- CREATE TRIGGER trigger_:view_name_editable_update
159
- INSTEAD OF UPDATE ON :view_name_editable
160
- FOR EACH ROW
161
- EXECUTE FUNCTION :view_name_editable_update();
162
-
163
- -- =============================================================================
164
- -- VIEW METADATA
165
- -- =============================================================================
166
-
167
- COMMENT ON VIEW :view_name IS ':description';
168
-
169
- -- =============================================================================
170
- -- PERMISSIONS
171
- -- =============================================================================
172
-
173
- -- Grant SELECT on view (inherits base table RLS)
174
- GRANT SELECT ON :view_name TO authenticated;
175
-
176
- -- For updateable views, also grant UPDATE
177
- -- GRANT UPDATE ON :view_name_editable TO authenticated;
1
+ -- View Template
2
+ -- View: :view_name
3
+ -- Created: :created_date
4
+ -- Author: :author
5
+ -- Description: :description
6
+ --
7
+ -- Views are virtual tables that provide a simplified interface
8
+ -- Data is always live (not cached like materialized views)
9
+
10
+ -- =============================================================================
11
+ -- BASIC VIEW
12
+ -- =============================================================================
13
+
14
+ -- Drop existing view if exists
15
+ DROP VIEW IF EXISTS :view_name CASCADE;
16
+
17
+ -- Create view
18
+ CREATE OR REPLACE VIEW :view_name AS
19
+ SELECT
20
+ t.id,
21
+ t.name,
22
+ t.description,
23
+ t.status,
24
+ t.created_at,
25
+ t.updated_at,
26
+ -- Related data
27
+ u.email AS owner_email,
28
+ u.raw_user_meta_data->>'full_name' AS owner_name,
29
+ -- Computed columns
30
+ CASE
31
+ WHEN t.status = 'active' THEN 'Active'
32
+ WHEN t.status = 'pending' THEN 'Pending'
33
+ WHEN t.status = 'archived' THEN 'Archived'
34
+ ELSE 'Unknown'
35
+ END AS status_label,
36
+ -- Age calculation
37
+ EXTRACT(DAY FROM NOW() - t.created_at) AS days_old
38
+ FROM :base_table t
39
+ LEFT JOIN auth.users u ON u.id = t.user_id
40
+ WHERE t.deleted_at IS NULL;
41
+
42
+ -- =============================================================================
43
+ -- VIEW WITH AGGREGATIONS
44
+ -- =============================================================================
45
+
46
+ CREATE OR REPLACE VIEW :view_name_summary AS
47
+ SELECT
48
+ t.category,
49
+ COUNT(*) AS total_count,
50
+ COUNT(*) FILTER (WHERE t.status = 'active') AS active_count,
51
+ COUNT(*) FILTER (WHERE t.status = 'pending') AS pending_count,
52
+ MIN(t.created_at) AS earliest,
53
+ MAX(t.created_at) AS latest,
54
+ AVG(t.value)::NUMERIC(10,2) AS avg_value
55
+ FROM :base_table t
56
+ WHERE t.deleted_at IS NULL
57
+ GROUP BY t.category;
58
+
59
+ -- =============================================================================
60
+ -- VIEW WITH RLS CONSIDERATION
61
+ -- =============================================================================
62
+
63
+ -- Views inherit RLS from underlying tables
64
+ -- If base table has RLS, view will respect it
65
+ --
66
+ -- For views that need custom RLS-like behavior,
67
+ -- wrap in a SECURITY DEFINER function:
68
+
69
+ CREATE OR REPLACE FUNCTION get_:view_name_for_user()
70
+ RETURNS TABLE (
71
+ id UUID,
72
+ name TEXT,
73
+ status TEXT,
74
+ created_at TIMESTAMPTZ
75
+ )
76
+ LANGUAGE plpgsql
77
+ SECURITY INVOKER
78
+ STABLE
79
+ AS $$
80
+ BEGIN
81
+ RETURN QUERY
82
+ SELECT
83
+ v.id,
84
+ v.name,
85
+ v.status,
86
+ v.created_at
87
+ FROM :view_name v
88
+ WHERE v.user_id = auth.uid()
89
+ ORDER BY v.created_at DESC;
90
+ END;
91
+ $$;
92
+
93
+ -- =============================================================================
94
+ -- VIEW WITH JOINED DATA
95
+ -- =============================================================================
96
+
97
+ CREATE OR REPLACE VIEW :view_name_detailed AS
98
+ SELECT
99
+ t.id,
100
+ t.name,
101
+ t.status,
102
+ -- One-to-one relationship
103
+ d.detail_field,
104
+ -- One-to-many aggregation
105
+ (
106
+ SELECT json_agg(json_build_object(
107
+ 'id', c.id,
108
+ 'name', c.name,
109
+ 'created_at', c.created_at
110
+ ))
111
+ FROM :child_table c
112
+ WHERE c.parent_id = t.id
113
+ ) AS children,
114
+ -- Count
115
+ (
116
+ SELECT COUNT(*)
117
+ FROM :child_table c
118
+ WHERE c.parent_id = t.id
119
+ ) AS children_count,
120
+ t.created_at,
121
+ t.updated_at
122
+ FROM :base_table t
123
+ LEFT JOIN :detail_table d ON d.id = t.detail_id
124
+ WHERE t.deleted_at IS NULL;
125
+
126
+ -- =============================================================================
127
+ -- UPDATEABLE VIEW (with INSTEAD OF triggers)
128
+ -- =============================================================================
129
+
130
+ -- Simple updateable view (for basic cases)
131
+ CREATE OR REPLACE VIEW :view_name_editable AS
132
+ SELECT
133
+ id,
134
+ name,
135
+ description,
136
+ status,
137
+ updated_at
138
+ FROM :base_table
139
+ WHERE deleted_at IS NULL;
140
+
141
+ -- Make it updateable via trigger
142
+ CREATE OR REPLACE FUNCTION :view_name_editable_update()
143
+ RETURNS TRIGGER AS $$
144
+ BEGIN
145
+ UPDATE :base_table
146
+ SET
147
+ name = NEW.name,
148
+ description = NEW.description,
149
+ status = NEW.status,
150
+ updated_at = NOW()
151
+ WHERE id = NEW.id
152
+ AND user_id = auth.uid(); -- RLS check
153
+
154
+ RETURN NEW;
155
+ END;
156
+ $$ LANGUAGE plpgsql SECURITY DEFINER;
157
+
158
+ CREATE TRIGGER trigger_:view_name_editable_update
159
+ INSTEAD OF UPDATE ON :view_name_editable
160
+ FOR EACH ROW
161
+ EXECUTE FUNCTION :view_name_editable_update();
162
+
163
+ -- =============================================================================
164
+ -- VIEW METADATA
165
+ -- =============================================================================
166
+
167
+ COMMENT ON VIEW :view_name IS ':description';
168
+
169
+ -- =============================================================================
170
+ -- PERMISSIONS
171
+ -- =============================================================================
172
+
173
+ -- Grant SELECT on view (inherits base table RLS)
174
+ GRANT SELECT ON :view_name TO authenticated;
175
+
176
+ -- For updateable views, also grant UPDATE
177
+ -- GRANT UPDATE ON :view_name_editable TO authenticated;