@guard-rail/core 0.1.1 → 0.2.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.
@@ -0,0 +1,48 @@
1
+ // Plain, already-final JS — deliberately NOT part of the tsup build. A
2
+ // postinstall script has to exist the instant npm/pnpm/yarn extracts the
3
+ // package, with no dependency on a build step having run yet, which would
4
+ // otherwise race tsup during this very monorepo's own installs (lifecycle
5
+ // script ordering between "postinstall" and "prepare" isn't a guarantee
6
+ // worth relying on).
7
+ //
8
+ // Runs after `npm install @guard-rail/core` in a consuming project — just
9
+ // onboarding guidance, easy to miss otherwise among the rest of an install's
10
+ // output.
11
+ //
12
+ // Deliberately silent in two cases:
13
+ // - CI: this is guidance for a human at a keyboard, not something that
14
+ // belongs in build logs.
15
+ // - Running inside this very monorepo: pnpm runs a workspace package's own
16
+ // lifecycle scripts too (already visible via the "prepare" build step on
17
+ // every `pnpm install`), which would otherwise print this on every
18
+ // contributor's install of this repo, not just a real consumer's. Resolved
19
+ // through the pnpm workspace symlink, this file's own real path sits under
20
+ // packages/guardrail/ — never inside a node_modules/ — which a real
21
+ // consumer's install always does.
22
+ import { fileURLToPath } from "node:url";
23
+
24
+ const isRealInstall = fileURLToPath(import.meta.url).includes("node_modules");
25
+
26
+ if (isRealInstall && !process.env.CI) {
27
+ console.log(`
28
+ Guardrail installed. Next steps:
29
+
30
+ 1. Set up the database (skip both if this Supabase project already has
31
+ Guardrail applied for another site — see "Many sites, one Supabase
32
+ project" in the README):
33
+ - Copy node_modules/@guard-rail/core/supabase/migrations/*.sql into
34
+ your own supabase/migrations/, then \`supabase db push\`.
35
+ - Add "guardrail" under Settings > API > Exposed schemas in the
36
+ Supabase dashboard.
37
+
38
+ 2. Wrap your root layout with withGuardrail and mark fields with <GRail>.
39
+ Full guide: https://www.npmjs.com/package/@guard-rail/core
40
+
41
+ 3. Once SUPABASE_URL / SUPABASE_ANON_KEY are set, seed yourself as the
42
+ first admin on a site (needs the service_role key, not the anon key —
43
+ Settings > API in the dashboard; pass it inline, don't store it):
44
+
45
+ SERVICE_KEY=... npx guardrail-seed-user \\
46
+ --email=you@example.com --password=... --site-id=my-site
47
+ `);
48
+ }
@@ -17,7 +17,12 @@
17
17
  -- sufficient — keep both.
18
18
 
19
19
  create schema if not exists guardrail;
20
- grant usage on schema guardrail to anon, authenticated;
20
+ -- service_role bypasses RLS (a Postgres role attribute Supabase configures
21
+ -- automatically), but that doesn't waive ordinary schema/table grants —
22
+ -- without this, any service_role-authenticated call (guardrail-seed-user,
23
+ -- say) fails with "permission denied for schema guardrail" despite using
24
+ -- the right key entirely.
25
+ grant usage on schema guardrail to anon, authenticated, service_role;
21
26
 
22
27
  /* ------------------------------------------------------------- members --- */
23
28
 
@@ -38,7 +43,7 @@ alter table guardrail.gr_members enable row level security;
38
43
  -- Anon has no legitimate reason to read team membership, so it gets no grant
39
44
  -- at all here (unlike gr_content, which anonymous visitors must be able to
40
45
  -- read to render the site).
41
- grant select, insert, update, delete on guardrail.gr_members to authenticated;
46
+ grant select, insert, update, delete on guardrail.gr_members to authenticated, service_role;
42
47
 
43
48
  -- Does the current user hold one of these roles on this site? SECURITY
44
49
  -- DEFINER so the membership policies below can call it without recursing
@@ -121,8 +126,8 @@ create index gr_content_site_path_idx on guardrail.gr_content (site_id, path_key
121
126
 
122
127
  alter table guardrail.gr_content enable row level security;
123
128
 
124
- grant select on guardrail.gr_content to anon, authenticated;
125
- grant insert, update, delete on guardrail.gr_content to authenticated;
129
+ grant select on guardrail.gr_content to anon, authenticated, service_role;
130
+ grant insert, update, delete on guardrail.gr_content to authenticated, service_role;
126
131
 
127
132
  -- Anyone may read published content: this is what anonymous visitors render.
128
133
  create policy "content is publicly readable"
@@ -179,8 +184,8 @@ create index gr_repeater_items_order_idx
179
184
 
180
185
  alter table guardrail.gr_repeater_items enable row level security;
181
186
 
182
- grant select on guardrail.gr_repeater_items to anon, authenticated;
183
- grant insert, update, delete on guardrail.gr_repeater_items to authenticated;
187
+ grant select on guardrail.gr_repeater_items to anon, authenticated, service_role;
188
+ grant insert, update, delete on guardrail.gr_repeater_items to authenticated, service_role;
184
189
 
185
190
  -- Anyone may enumerate items: this is what renders the page for anonymous
186
191
  -- visitors, same reasoning as gr_content's public read policy.
@@ -289,8 +294,12 @@ grant execute on function guardrail.gr_ensure_bucket(text) to authenticated;
289
294
 
290
295
  /* ----------------------------------------------------------- bootstrap --- */
291
296
  -- A site with no members has nobody who can add members, and there is
292
- -- deliberately no in-app way to grant yourself a role. Seed the first admin
293
- -- here, replacing both values:
297
+ -- deliberately no in-app way to grant yourself a role. Prefer the bundled
298
+ -- `guardrail-seed-user` command (see the README's Bootstrapping section) —
299
+ -- it creates the account via Supabase's own Admin API if it doesn't exist
300
+ -- yet, rather than assuming one already does. The raw SQL below still works
301
+ -- if you'd rather not use the service_role key it needs, replacing both
302
+ -- values:
294
303
  --
295
304
  -- insert into guardrail.gr_members (site_id, email, user_id, role, status)
296
305
  -- select 'your-site-id', u.email, u.id, 'admin', 'active'