@dalgoridim/headless-cms 0.5.0 → 0.5.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 (2) hide show
  1. package/README.md +85 -1
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -26,6 +26,7 @@ npm install @dalgoridim/headless-cms
26
26
  npm install firebase firebase-admin cloudinary # Firestore + Cloudinary + Firebase
27
27
  npm install pg # Postgres
28
28
  npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner # S3
29
+ npm install @react-oauth/google # Google sign-in (auth/google/client)
29
30
  ```
30
31
 
31
32
  `react` / `react-dom` are peer deps; every backend SDK is an **optional** peer —
@@ -38,7 +39,7 @@ Server-only code never leaks into the client bundle. Import from the right entry
38
39
  | Entry | Contents |
39
40
  |---|---|
40
41
  | `@dalgoridim/headless-cms` | Shared types only (safe anywhere) |
41
- | `.../client` | `PageProvider`, `usePageContext`, `ContentEditSpan`, `EditableImage`, `useMarkdownEditor`, `CmsAuthProvider`, `useCmsAuth` |
42
+ | `.../client` | `PageProvider`, `usePageContext` (field edits **+ collection add/remove/reorder**), `ContentEditSpan`, `EditableImage`, `useMarkdownEditor`, `CmsAuthProvider`, `useCmsAuth` |
42
43
  | `.../server` | `createCmsHandlers`, `createAdminGate`, `resolveRelations` |
43
44
  | `.../adapters/firestore` | `FirestoreDataAdapter` |
44
45
  | `.../adapters/postgres` | `PostgresDataAdapter` (hybrid JSONB + typed tables) |
@@ -46,6 +47,8 @@ Server-only code never leaks into the client bundle. Import from the right entry
46
47
  | `.../storage/{cloudinary,s3,local}/server` | **Server** signers — pull in SDKs, server-only |
47
48
  | `.../auth/firebase` | `firebaseAuth` (server gate) |
48
49
  | `.../auth/firebase/client` | `FirebaseAuthProvider`, `useFirebaseAuth` |
50
+ | `.../auth/google` | `googleAuth` (server gate), `verifyGoogleIdToken` — **Firebase-free** Google sign-in |
51
+ | `.../auth/google/client` | `GoogleAuthProvider`, `useGoogleAuth`, `GoogleSignInButton` |
49
52
  | `.../auth/nextauth` | `nextAuthAuth`, `customAuth` |
50
53
 
51
54
  ## Core interfaces
@@ -214,6 +217,45 @@ function Editor({ initialValue, onSave }) {
214
217
  }
215
218
  ```
216
219
 
220
+ ## Editable collections (add / remove / reorder)
221
+
222
+ `ContentEditSpan` / `EditableImage` edit the **fields of one item**. To let an admin
223
+ change **which items exist** and **their order** (e.g. add a project, delete a tool,
224
+ drag to reorder), `usePageContext` exposes optimistic, self-persisting collection
225
+ ops. Hydrate the lists from the server via `initialCollections`:
226
+
227
+ ```tsx
228
+ <PageProvider
229
+ initialSections={sections}
230
+ initialCollections={{ projects: await data.fetchCollection("projects") }}
231
+ >
232
+ ```
233
+
234
+ ```tsx
235
+ import { usePageContext } from "@dalgoridim/headless-cms/client";
236
+
237
+ const { collections, createItem, deleteItem, reorderItems } = usePageContext();
238
+
239
+ // add — PUT (upsert) with a generated id; returns the new id
240
+ await createItem("projects", { title: "New project", order: collections.projects.length });
241
+
242
+ // remove — DELETE
243
+ await deleteItem("projects", id);
244
+
245
+ // reorder — PATCH each item's integer `order` to match the new sequence
246
+ await reorderItems("projects", ["id-c", "id-a", "id-b"]);
247
+ ```
248
+
249
+ | Op | Persists via | Notes |
250
+ |---|---|---|
251
+ | `createItem(collection, data, opts?)` | `PUT {base}/{collection}/{id}` | `opts.id` to set the id, `opts.atStart` to prepend; returns the id |
252
+ | `deleteItem(collection, id)` | `DELETE {base}/{collection}/{id}` | |
253
+ | `reorderItems(collection, orderedIds)` | `PATCH {base}/{collection}/{id}` ×N | writes `{ order: index }` per item |
254
+
255
+ All three update `collections` optimistically and **roll back on failure** (with a
256
+ `notify` toast). Order is an `int` — register the collection with a typed `order`
257
+ column (see [Postgres](#postgres-hybrid)) so it sorts numerically.
258
+
217
259
  ## The Query language
218
260
 
219
261
  A neutral, backend-agnostic query passed to `DataAdapter.fetchCollection`:
@@ -321,6 +363,48 @@ createCmsHandlers({
321
363
  });
322
364
  ```
323
365
 
366
+ ### Google sign-in (no Firebase)
367
+
368
+ `auth/google` is a Firebase-free way to do "Sign in with Google" — one OAuth client
369
+ ID, no service account. The server verifies the Google ID token **locally** against
370
+ Google's public keys (Node `crypto` + `fetch`, zero new deps) and grants admin only
371
+ to a verified email in your allowlist.
372
+
373
+ ```ts
374
+ // server gate
375
+ import { googleAuth } from "@dalgoridim/headless-cms/auth/google";
376
+
377
+ export const { GET, PATCH, PUT, DELETE } = createCmsHandlers({
378
+ data,
379
+ auth: googleAuth({
380
+ clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
381
+ adminEmails: process.env.ADMIN_EMAILS!.split(","),
382
+ }),
383
+ });
384
+ ```
385
+
386
+ ```tsx
387
+ // client — built on @react-oauth/google (install it)
388
+ import {
389
+ GoogleAuthProvider,
390
+ GoogleSignInButton,
391
+ useGoogleAuth,
392
+ } from "@dalgoridim/headless-cms/auth/google/client";
393
+
394
+ <GoogleAuthProvider clientId={clientId} adminEmails={["you@example.com"]}>
395
+ <PageProvider …>{children}</PageProvider>
396
+ </GoogleAuthProvider>;
397
+
398
+ // anywhere inside the provider — renders the official Google button:
399
+ <GoogleSignInButton onError={() => toast.error("Sign-in failed")} />;
400
+ // useGoogleAuth() → { user, isAdmin, isEditing, toggleEdit, logout }
401
+ ```
402
+
403
+ The button writes the ID token to an `adminToken` cookie that `googleAuth` reads;
404
+ `isAdmin` is optimistic on the client (via `adminEmails`) with the server gate
405
+ authoritative. `verifyGoogleIdToken(token, { clientId })` is also exported for
406
+ custom flows.
407
+
324
408
  ## Your content types stay unconstrained
325
409
 
326
410
  The engine only adds addressing fields; your domain type `T` is never forced to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dalgoridim/headless-cms",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Database-agnostic, inline-edit headless CMS engine for React / Next.js apps",
5
5
  "license": "UNLICENSED",
6
6
  "author": "dalgoridim",