@pgpm/achievements 0.28.3 → 0.28.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -275,7 +275,7 @@ pnpm test
275
275
 
276
276
  ## Related Tooling
277
277
 
278
- * [pgpm](https://github.com/constructive-io/constructive/tree/main/pgpm/pgpm): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
278
+ * [pgpm](https://github.com/constructive-io/constructive/tree/main/pgpm/cli): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
279
279
  * [pgsql-test](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-test): **📊 Isolated testing environments** with per-test transaction rollbacks—ideal for integration tests, complex migrations, and RLS simulation.
280
280
  * [supabase-test](https://github.com/constructive-io/constructive/tree/main/postgres/supabase-test): **🧪 Supabase-native test harness** preconfigured for the local Supabase stack—per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
281
281
  * [graphile-test](https://github.com/constructive-io/constructive/tree/main/graphile/graphile-test): **🔐 Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
@@ -283,21 +283,6 @@ pnpm test
283
283
  * [libpg-query-node](https://github.com/constructive-io/libpg-query-node): **🌉 Node.js bindings** for `libpg_query`, converting SQL into parse trees.
284
284
  * [pg-proto-parser](https://github.com/constructive-io/pg-proto-parser): **📦 Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
285
285
 
286
- ### 📚 Documentation & Skills
287
-
288
- * [constructive-skills](https://github.com/constructive-io/constructive-skills): **📖 Platform documentation and AI agent skills** — feature catalog, blueprint reference, SDK guides, and deployment guides.
289
-
290
- Install skills for AI coding agents:
291
-
292
- ```bash
293
- # All platform skills (security, blueprints, codegen, billing, etc.)
294
- npx skills add constructive-io/constructive-skills
295
-
296
- # Individual repo skills (pgpm, testing, CLI, search, etc.)
297
- npx skills add https://github.com/constructive-io/constructive --skill pgpm
298
- npx skills add https://github.com/constructive-io/constructive --skill constructive-testing
299
- ```
300
-
301
286
  ## Disclaimer
302
287
 
303
288
  AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgpm/achievements",
3
- "version": "0.28.3",
3
+ "version": "0.28.4",
4
4
  "description": "Achievement system for tracking user progress and milestones",
5
5
  "author": "Dan Lynch <pyramation@gmail.com>",
6
6
  "contributors": [
@@ -21,11 +21,11 @@
21
21
  "test:watch": "jest --watch"
22
22
  },
23
23
  "dependencies": {
24
- "@pgpm/jwt-claims": "0.28.3",
25
- "@pgpm/verify": "0.28.3"
24
+ "@pgpm/jwt-claims": "0.28.4",
25
+ "@pgpm/verify": "0.28.4"
26
26
  },
27
27
  "devDependencies": {
28
- "pgpm": "^4.23.2"
28
+ "pgpm": "^4.28.7"
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
@@ -35,5 +35,5 @@
35
35
  "bugs": {
36
36
  "url": "https://github.com/constructive-io/pgpm-modules/issues"
37
37
  },
38
- "gitHead": "ebab5300b911578a615bf8d267fe7c5c244caea3"
38
+ "gitHead": "08979b82f1f68af396b540c6ce0cda4b438d34f0"
39
39
  }
@@ -1,280 +0,0 @@
1
- \echo Use "CREATE EXTENSION pgpm-achievements" to load this file. \quit
2
- CREATE SCHEMA IF NOT EXISTS status_private;
3
-
4
- GRANT USAGE ON SCHEMA status_private TO authenticated, anonymous;
5
-
6
- ALTER DEFAULT PRIVILEGES IN SCHEMA status_private
7
- GRANT EXECUTE ON FUNCTIONS TO authenticated;
8
-
9
- CREATE SCHEMA IF NOT EXISTS status_public;
10
-
11
- GRANT USAGE ON SCHEMA status_public TO authenticated, anonymous;
12
-
13
- ALTER DEFAULT PRIVILEGES IN SCHEMA status_public
14
- GRANT EXECUTE ON FUNCTIONS TO authenticated;
15
-
16
- CREATE TABLE status_public.user_steps (
17
- id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
18
- user_id uuid NOT NULL,
19
- name text NOT NULL,
20
- count int NOT NULL DEFAULT 1,
21
- created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
22
- );
23
-
24
- COMMENT ON TABLE status_public.user_steps IS 'The user achieving a requirement for a level. Log table that has every single step ever taken.';
25
- COMMENT ON COLUMN status_public.user_steps.id IS 'Unique identifier for this step record';
26
- COMMENT ON COLUMN status_public.user_steps.user_id IS 'User who performed this step';
27
- COMMENT ON COLUMN status_public.user_steps.name IS 'Name of the level requirement this step counts toward';
28
- COMMENT ON COLUMN status_public.user_steps.count IS 'Number of units this step contributes (default 1)';
29
- COMMENT ON COLUMN status_public.user_steps.created_at IS 'Timestamp when this step was recorded';
30
-
31
- CREATE INDEX ON status_public.user_steps (user_id, name);
32
-
33
- CREATE FUNCTION status_private.user_completed_step(step text, user_id uuid DEFAULT jwt_public.current_user_id()) RETURNS void AS $EOFCODE$
34
- INSERT INTO status_public.user_steps ( name, user_id, count )
35
- VALUES ( step, user_id, 1 );
36
- $EOFCODE$ LANGUAGE sql VOLATILE SECURITY DEFINER;
37
-
38
- CREATE FUNCTION status_private.user_incompleted_step(step text, user_id uuid DEFAULT jwt_public.current_user_id()) RETURNS void AS $EOFCODE$
39
- BEGIN
40
- DELETE FROM status_public.user_steps s
41
- WHERE s.user_id = user_incompleted_step.user_id
42
- AND s.name = step;
43
- DELETE FROM status_public.user_achievements a
44
- WHERE a.user_id = user_incompleted_step.user_id
45
- AND a.name = step;
46
- END;
47
- $EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
48
-
49
- CREATE FUNCTION status_private.tg_achievement() RETURNS trigger AS $EOFCODE$
50
- DECLARE
51
- is_null boolean;
52
- task_name text;
53
- BEGIN
54
- IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
55
- task_name = TG_ARGV[1]::text;
56
- EXECUTE format('SELECT ($1).%s IS NULL', TG_ARGV[0])
57
- USING NEW INTO is_null;
58
- IF (is_null IS FALSE) THEN
59
- PERFORM status_private.user_completed_step(task_name);
60
- END IF;
61
- RETURN NEW;
62
- END IF;
63
- END;
64
- $EOFCODE$ LANGUAGE plpgsql VOLATILE;
65
-
66
- CREATE FUNCTION status_private.tg_achievement_toggle() RETURNS trigger AS $EOFCODE$
67
- DECLARE
68
- is_null boolean;
69
- task_name text;
70
- BEGIN
71
- IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
72
- task_name = TG_ARGV[1]::text;
73
- EXECUTE format('SELECT ($1).%s IS NULL', TG_ARGV[0])
74
- USING NEW INTO is_null;
75
- IF (is_null IS TRUE) THEN
76
- PERFORM status_private.user_incompleted_step(task_name);
77
- ELSE
78
- PERFORM status_private.user_completed_step(task_name);
79
- END IF;
80
- RETURN NEW;
81
- END IF;
82
- END;
83
- $EOFCODE$ LANGUAGE plpgsql VOLATILE;
84
-
85
- CREATE FUNCTION status_private.tg_achievement_boolean() RETURNS trigger AS $EOFCODE$
86
- DECLARE
87
- is_true boolean;
88
- task_name text;
89
- BEGIN
90
- IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
91
- task_name = TG_ARGV[1]::text;
92
- EXECUTE format('SELECT ($1).%s IS TRUE', TG_ARGV[0])
93
- USING NEW INTO is_true;
94
- IF (is_true IS TRUE) THEN
95
- PERFORM status_private.user_completed_step(task_name);
96
- END IF;
97
- RETURN NEW;
98
- END IF;
99
- END;
100
- $EOFCODE$ LANGUAGE plpgsql VOLATILE;
101
-
102
- CREATE FUNCTION status_private.tg_achievement_toggle_boolean() RETURNS trigger AS $EOFCODE$
103
- DECLARE
104
- is_true boolean;
105
- task_name text;
106
- BEGIN
107
- IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
108
- task_name = TG_ARGV[1]::text;
109
- EXECUTE format('SELECT ($1).%s IS TRUE', TG_ARGV[0])
110
- USING NEW INTO is_true;
111
- IF (is_true IS TRUE) THEN
112
- PERFORM status_private.user_completed_step(task_name);
113
- ELSE
114
- PERFORM status_private.user_incompleted_step(task_name);
115
- END IF;
116
- RETURN NEW;
117
- END IF;
118
- END;
119
- $EOFCODE$ LANGUAGE plpgsql VOLATILE;
120
-
121
- CREATE TABLE status_public.user_achievements (
122
- id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
123
- user_id uuid NOT NULL,
124
- name text NOT NULL,
125
- count int NOT NULL DEFAULT 0,
126
- created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
127
- CONSTRAINT user_achievements_unique_key
128
- UNIQUE (user_id, name)
129
- );
130
-
131
- COMMENT ON TABLE status_public.user_achievements IS 'This table represents the users progress for particular level requirements, tallying the total count. This table is updated via triggers and should not be updated maually.';
132
- COMMENT ON COLUMN status_public.user_achievements.id IS 'Unique identifier for this achievement progress record';
133
- COMMENT ON COLUMN status_public.user_achievements.user_id IS 'User whose progress is being tracked';
134
- COMMENT ON COLUMN status_public.user_achievements.name IS 'Name of the level requirement this progress relates to';
135
- COMMENT ON COLUMN status_public.user_achievements.count IS 'Accumulated count toward the requirement (updated by triggers)';
136
- COMMENT ON COLUMN status_public.user_achievements.created_at IS 'Timestamp when this progress record was first created';
137
-
138
- CREATE INDEX ON status_public.user_achievements (user_id, name);
139
-
140
- CREATE FUNCTION status_private.upsert_achievement(vuser_id uuid, vname text, vcount int) RETURNS void AS $EOFCODE$
141
- BEGIN
142
- INSERT INTO status_public.user_achievements (user_id, name, count)
143
- VALUES
144
- (vuser_id, vname, GREATEST(vcount, 0))
145
- ON CONFLICT ON CONSTRAINT user_achievements_unique_key
146
- DO UPDATE SET
147
- -- look ma! you can actually do aliases inside on conflict
148
- count = user_achievements.count + EXCLUDED.count
149
- ;
150
- END;
151
- $EOFCODE$ LANGUAGE plpgsql VOLATILE;
152
-
153
- CREATE TABLE status_public.levels (
154
- name text NOT NULL PRIMARY KEY
155
- );
156
-
157
- COMMENT ON TABLE status_public.levels IS 'Levels for achievement';
158
- COMMENT ON COLUMN status_public.levels.name IS 'Unique level name used as the primary key (e.g. bronze, silver, gold)';
159
-
160
- GRANT SELECT ON status_public.levels TO PUBLIC;
161
-
162
- CREATE TABLE status_public.level_requirements (
163
- id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
164
- name text NOT NULL,
165
- level text NOT NULL,
166
- required_count int DEFAULT 1,
167
- priority int DEFAULT 100,
168
- UNIQUE (name, level)
169
- );
170
-
171
- COMMENT ON TABLE status_public.level_requirements IS 'Requirements to achieve a level';
172
- COMMENT ON COLUMN status_public.level_requirements.id IS 'Unique identifier for this requirement';
173
- COMMENT ON COLUMN status_public.level_requirements.name IS 'Requirement name (e.g. posts_created, logins); matches user_steps.name';
174
- COMMENT ON COLUMN status_public.level_requirements.level IS 'Level this requirement belongs to (references levels.name)';
175
- COMMENT ON COLUMN status_public.level_requirements.required_count IS 'Number of steps needed to satisfy this requirement (default 1)';
176
- COMMENT ON COLUMN status_public.level_requirements.priority IS 'Display/evaluation order; lower numbers are checked first (default 100)';
177
-
178
- CREATE INDEX ON status_public.level_requirements (name, level, priority);
179
-
180
- GRANT SELECT ON status_public.levels TO authenticated;
181
-
182
- CREATE FUNCTION status_public.steps_required(vlevel text, vrole_id uuid DEFAULT jwt_public.current_user_id()) RETURNS SETOF status_public.level_requirements AS $EOFCODE$
183
- BEGIN
184
- RETURN QUERY
185
- SELECT
186
- level_requirements.id,
187
- level_requirements.name,
188
- level_requirements.level,
189
- -1*(coalesce(user_achievements.count,0)-level_requirements.required_count) as required_count,
190
- level_requirements.priority
191
- FROM
192
- status_public.level_requirements
193
- FULL OUTER JOIN status_public.user_achievements ON (
194
- user_achievements.name = level_requirements.name
195
- AND user_achievements.user_id =vrole_id
196
- )
197
- JOIN status_public.levels ON (level_requirements.level = levels.name)
198
- WHERE
199
- level_requirements.level = vlevel
200
- AND -1*(coalesce(user_achievements.count,0)-level_requirements.required_count) > 0
201
- ORDER BY priority ASC
202
- ;
203
- END;
204
- $EOFCODE$ LANGUAGE plpgsql STABLE;
205
-
206
- CREATE FUNCTION status_public.user_achieved(vlevel text, vrole_id uuid DEFAULT jwt_public.current_user_id()) RETURNS boolean AS $EOFCODE$
207
- DECLARE
208
- c int;
209
- BEGIN
210
- SELECT COUNT(*) FROM
211
- status_public.steps_required(
212
- vlevel,
213
- vrole_id
214
- )
215
- INTO c;
216
-
217
- RETURN c <= 0;
218
- END;
219
- $EOFCODE$ LANGUAGE plpgsql STABLE;
220
-
221
- ALTER TABLE status_public.user_achievements
222
- ENABLE ROW LEVEL SECURITY;
223
-
224
- CREATE POLICY can_select_user_achievements
225
- ON status_public.user_achievements
226
- AS PERMISSIVE
227
- FOR SELECT
228
- TO PUBLIC
229
- USING (
230
- jwt_public.current_user_id() = user_id
231
- );
232
-
233
- CREATE POLICY can_insert_user_achievements
234
- ON status_public.user_achievements
235
- AS PERMISSIVE
236
- FOR INSERT
237
- TO PUBLIC
238
- WITH CHECK (
239
- false
240
- );
241
-
242
- CREATE POLICY can_update_user_achievements
243
- ON status_public.user_achievements
244
- AS PERMISSIVE
245
- FOR UPDATE
246
- TO PUBLIC
247
- USING (
248
- false
249
- );
250
-
251
- CREATE POLICY can_delete_user_achievements
252
- ON status_public.user_achievements
253
- AS PERMISSIVE
254
- FOR DELETE
255
- TO PUBLIC
256
- USING (
257
- false
258
- );
259
-
260
- GRANT INSERT ON status_public.user_achievements TO authenticated;
261
-
262
- GRANT SELECT ON status_public.user_achievements TO authenticated;
263
-
264
- GRANT UPDATE ON status_public.user_achievements TO authenticated;
265
-
266
- GRANT DELETE ON status_public.user_achievements TO authenticated;
267
-
268
- CREATE FUNCTION status_private.tg_update_achievements_tg() RETURNS trigger AS $EOFCODE$
269
- DECLARE
270
- BEGIN
271
- PERFORM status_private.upsert_achievement(NEW.user_id, NEW.name, NEW.count);
272
- RETURN NEW;
273
- END;
274
- $EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
275
-
276
- CREATE TRIGGER update_achievements_tg
277
- AFTER INSERT
278
- ON status_public.user_steps
279
- FOR EACH ROW
280
- EXECUTE PROCEDURE status_private.tg_update_achievements_tg();