rahman-resources 0.9.1 → 0.11.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.
package/bin/cli.js CHANGED
@@ -58,6 +58,9 @@ async function main() {
58
58
  return runInit(rest);
59
59
  case "add":
60
60
  return runAdd(rest);
61
+ case "update":
62
+ case "update-slice":
63
+ return runUpdate(rest);
61
64
  case "add-skill":
62
65
  return runAddSkill(rest);
63
66
  case "scaffold-slice":
@@ -435,6 +438,35 @@ async function runAdd(rest) {
435
438
  if (kind === "recipe") return addRecipe(entry);
436
439
  }
437
440
 
441
+ /**
442
+ * `update <slug> [target] [--dry-run]` — re-pull a slice from the kitab into
443
+ * an existing consumer directory. Use after `@rahman/shared` bumps or when
444
+ * the source slice has shipped patches. `--dry-run` previews changes via
445
+ * `runLift`'s built-in diff.
446
+ *
447
+ * For slices only (not layouts/features/recipes — those use `add`).
448
+ */
449
+ async function runUpdate(rest) {
450
+ const { positional, flags } = parseFlags(rest);
451
+ const [slug, targetArg = "."] = positional;
452
+ if (!slug) {
453
+ console.error(kleur.red("Missing slug. Usage: rahman-resources update <slug> [target] [--dry-run]"));
454
+ process.exit(1);
455
+ }
456
+ const found = findEntry(slug);
457
+ if (!found) throw new Error(`"${slug}" not found. Run ${kleur.cyan("npx rahman-resources list slices")}.`);
458
+ const { kind, entry } = found;
459
+ if (kind !== "slice") {
460
+ console.error(kleur.red(`update only supported for slices. "${slug}" is a ${kind} — use \`add\` instead.`));
461
+ process.exit(1);
462
+ }
463
+ console.log(kleur.bold(`\n→ Re-pulling ${kleur.cyan(entry.slug)} into ${kleur.dim(targetArg)}\n`));
464
+ const liftArgs = [`rahman:${entry.slug}`];
465
+ if (targetArg !== ".") liftArgs.push("--target", targetArg);
466
+ if (flags["dry-run"]) liftArgs.push("--dry-run");
467
+ return runLift(liftArgs);
468
+ }
469
+
438
470
  async function addLayout(t, target, targetArg, flags = {}) {
439
471
  console.log(kleur.bold(`\n→ Installing ${kleur.cyan(t.title)} into ${kleur.dim(target)}\n`));
440
472
  if (!t.pullPaths || t.pullPaths.length === 0) {
package/lib/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 2,
3
- "generatedAt": "2026-05-10T04:09:55.146Z",
3
+ "generatedAt": "2026-05-13T13:37:06.031Z",
4
4
  "repo": "rahmanef63/resource-site",
5
5
  "branch": "main",
6
6
  "layouts": [
@@ -804,151 +804,7 @@
804
804
  "primaryFile": "app/preview/riset-kit/public/page.tsx"
805
805
  }
806
806
  ],
807
- "recipes": [
808
- {
809
- "slug": "block-editor",
810
- "title": "Notion-Style Block Editor",
811
- "description": "21-block contenteditable editor with slash command menu, markdown shortcuts, drag handles. Real-time via Convex.",
812
- "source": "notion-page-clone",
813
- "files": [
814
- "frontend/slices/notion/slices/editor/BlockEditor.tsx",
815
- "frontend/slices/notion/slices/editor/SlashMenu.tsx",
816
- "frontend/slices/notion/slices/editor/blockSpecs.ts"
817
- ],
818
- "exampleCode": "import { BlockEditor } from \"@/frontend/slices/notion/slices/editor/BlockEditor\";\n\n<BlockEditor pageId={pageId} />",
819
- "agentRecipe": "Already copied at frontend/slices/notion/slices/editor/. See PORT-NOTION.md for Vite→Next port checklist (routing rewrite, use-client markers, Convex API surface rename).",
820
- "tags": [
821
- "editor",
822
- "notion",
823
- "blocks",
824
- "real-time"
825
- ]
826
- },
827
- {
828
- "slug": "page-tree-sidebar",
829
- "title": "Page Tree Sidebar",
830
- "description": "Hierarchical workspace sidebar with @dnd-kit drag-drop reordering, favorites, recents.",
831
- "source": "notion-page-clone",
832
- "files": [
833
- "frontend/slices/notion/slices/workspace-sidebar/components/WorkspaceSidebar.tsx",
834
- "frontend/slices/notion/slices/workspace-sidebar/components/SortablePageRow.tsx"
835
- ],
836
- "exampleCode": "import { WorkspaceSidebar } from \"@/frontend/slices/notion/slices/workspace-sidebar/components/WorkspaceSidebar\";\n\n<WorkspaceSidebar />",
837
- "agentRecipe": "Mount WorkspaceSidebar inside the left slot of <ThreeColumnLayout>. State backed by Zustand store at frontend/slices/notion/shared/lib/store.tsx.",
838
- "tags": [
839
- "sidebar",
840
- "tree",
841
- "dnd-kit",
842
- "navigation"
843
- ]
844
- },
845
- {
846
- "slug": "multi-block-selection",
847
- "title": "Multi-Block Selection",
848
- "description": "Marquee + click+shift selection of editor blocks with floating toolbar. Bulk actions: delete, duplicate, convert.",
849
- "source": "notion-page-clone",
850
- "files": [
851
- "frontend/slices/notion/slices/block-selection/components/BlockSelectionProvider.tsx",
852
- "frontend/slices/notion/slices/block-selection/components/MarqueeOverlay.tsx"
853
- ],
854
- "exampleCode": "import { BlockSelectionProvider } from \"@/frontend/slices/notion/slices/block-selection/components/BlockSelectionProvider\";\n\n<BlockSelectionProvider>\n <BlockEditor />\n</BlockSelectionProvider>",
855
- "agentRecipe": "Wrap BlockEditor with BlockSelectionProvider. The marquee overlay attaches to document; toolbar floats above the selection bounding box.",
856
- "tags": [
857
- "selection",
858
- "editor",
859
- "bulk",
860
- "marquee"
861
- ]
862
- },
863
- {
864
- "slug": "database-views",
865
- "title": "Database Views (11 types)",
866
- "description": "Properties+rows database with 11 view types: table, board, calendar, timeline, chart, gallery, map. Per-view filter/sort/group.",
867
- "source": "notion-page-clone",
868
- "files": [
869
- "frontend/slices/notion/slices/databases/DatabaseBlock.tsx",
870
- "frontend/slices/notion/slices/databases/views/TableView.tsx",
871
- "frontend/slices/notion/slices/databases/views/BoardView.tsx"
872
- ],
873
- "exampleCode": "import { DatabaseBlock } from \"@/frontend/slices/notion/slices/databases/DatabaseBlock\";\n\n<DatabaseBlock databaseId={dbId} />",
874
- "agentRecipe": "DatabaseBlock auto-routes to the active view component. Add custom property types by extending PropertyCell.tsx.",
875
- "tags": [
876
- "database",
877
- "views",
878
- "table",
879
- "kanban",
880
- "calendar"
881
- ]
882
- },
883
- {
884
- "slug": "command-palette",
885
- "title": "Command Palette (⌘K)",
886
- "description": "Cmd+K modal: feature navigation, workspace switching, theme, sign-out, custom commands. Auto-builds from feature registry.",
887
- "source": "kitab-core + notion-page-clone",
888
- "files": [
889
- "frontend/shared/foundation/utils/system/command-menu/components.tsx"
890
- ],
891
- "exampleCode": "import { CommandMenu } from \"@/frontend/shared/foundation/utils/system/command-menu/components\";\n\n<CommandMenu actions={customActions} />",
892
- "agentRecipe": "Mount CommandMenu once at the app shell level. It listens for Cmd+K globally. Pass extra commands via the actions prop or register via the command-registry.",
893
- "tags": [
894
- "palette",
895
- "cmd-k",
896
- "navigation"
897
- ]
898
- },
899
- {
900
- "slug": "comments-threaded",
901
- "title": "Threaded Comments",
902
- "description": "Page + block-level threaded comments with resolved state. Real-time via Convex.",
903
- "source": "notion-page-clone",
904
- "files": [
905
- "frontend/slices/notion/slices/comments/components/BlockCommentsPopover.tsx",
906
- "frontend/slices/notion/slices/comments/hooks/useComments.ts"
907
- ],
908
- "exampleCode": "import { BlockCommentsPopover } from \"@/frontend/slices/notion/slices/comments/components/BlockCommentsPopover\";\n\n<BlockCommentsPopover blockId={blockId} pageId={pageId} />",
909
- "agentRecipe": "Anchor comments by passing pageId (always) and optional blockId. Use useComments(blockId) hook for the reactive list.",
910
- "tags": [
911
- "comments",
912
- "real-time",
913
- "threading"
914
- ]
915
- },
916
- {
917
- "slug": "theme-preset-switcher",
918
- "title": "Theme Preset Switcher",
919
- "description": "Runtime theme swap (colors + fonts + shadows + tracking). OKLch CSS vars per preset. Persists to localStorage + Convex.",
920
- "source": "rahmanef.com",
921
- "files": [
922
- "frontend/shared/theme/theme-presets.ts",
923
- "frontend/shared/ui/components/theme-preset-switcher.tsx"
924
- ],
925
- "exampleCode": "import { ThemePresetSwitcher } from \"@/frontend/shared/ui/components/theme-preset-switcher\";\n\n<ThemePresetSwitcher />",
926
- "agentRecipe": "Add a new preset by appending a CSS block in app/globals.css with [data-theme=\"<name>\"], then register in preset-groups.ts.",
927
- "tags": [
928
- "theme",
929
- "presets",
930
- "oklch",
931
- "design-system"
932
- ]
933
- },
934
- {
935
- "slug": "contact-form-resend",
936
- "title": "Contact Form + Resend",
937
- "description": "Contact form posting to Resend email API. Server Action + Zod input validation.",
938
- "source": "cescadesigns",
939
- "files": [
940
- "recipes/contact-form-resend/src/page.tsx"
941
- ],
942
- "exampleCode": "// app/api/contact/route.ts\nimport { Resend } from \"resend\";\nconst resend = new Resend(process.env.RESEND_API_KEY!);\n\nexport async function POST(req: Request) {\n const data = await req.formData();\n await resend.emails.send({\n from: \"form@yourdomain.com\",\n to: \"you@yourdomain.com\",\n subject: `From ${data.get(\"name\")}`,\n html: `<p>${data.get(\"message\")}</p>`,\n });\n return Response.json({ ok: true });\n}",
943
- "agentRecipe": "Wire ContactForm.tsx (form action /api/contact) to the route handler. Always validate inputs with Zod server-side.",
944
- "tags": [
945
- "form",
946
- "email",
947
- "resend",
948
- "server-action"
949
- ]
950
- }
951
- ],
807
+ "recipes": [],
952
808
  "features": [
953
809
  {
954
810
  "slug": "convex-auth",
@@ -972,6 +828,27 @@
972
828
  "no-clerk"
973
829
  ]
974
830
  },
831
+ {
832
+ "slug": "doku-payment",
833
+ "title": "DOKU — Indonesia Payment",
834
+ "category": "payment",
835
+ "description": "Pembayaran lokal Indonesia via DOKU — Checkout (hosted) + Direct (VA / QRIS / e-Wallet / PayLater / Minimarket / Kartu). HMAC-signed REST + signature-verified webhook + idempotent retries. Sibling slice to midtrans-payment dengan paymentOrders schema yang dishare.",
836
+ "source": "rahmanef63/resource-site",
837
+ "docsUrl": "https://sandbox.doku.com/integration",
838
+ "install": "",
839
+ "npmPackages": [],
840
+ "exampleCode": "",
841
+ "agentRecipe": "DOKU dual-mode: Checkout (hosted, all channels) atau Direct (single channel, returns VA/QRIS/deeplink). Webhook di /webhooks/doku verify HMAC-SHA256 (canonical: Client-Id + Request-Id + Request-Timestamp + Request-Target + Digest). Idempotency by request_id index. Server-only — no NEXT_PUBLIC_*. Sandbox default (api-sandbox.doku.com); flip DOKU_IS_PRODUCTION=true for live.",
842
+ "tags": [
843
+ "payment",
844
+ "doku",
845
+ "indonesia",
846
+ "qris",
847
+ "virtual-account",
848
+ "ewallet",
849
+ "checkout"
850
+ ]
851
+ },
975
852
  {
976
853
  "slug": "midtrans-payment",
977
854
  "title": "Midtrans — Indonesia Payment",
@@ -1099,6 +976,107 @@
1099
976
  "bookings"
1100
977
  ]
1101
978
  },
979
+ {
980
+ "slug": "full-width-toggle",
981
+ "title": "Full Width Toggle",
982
+ "category": "ui",
983
+ "description": "Page-container width toggle (contained / wide / full). Per-device localStorage + cross-tab sync. Ships useFullWidth hook + FullWidthToggle button (icon/button/segment variants) + WidthContainer wrapper. Zero backend, zero env.",
984
+ "source": "rahmanef63/resource-site",
985
+ "docsUrl": "",
986
+ "install": "",
987
+ "npmPackages": [],
988
+ "exampleCode": "",
989
+ "agentRecipe": "Drop <WidthContainer> around page content, <FullWidthToggle variant='icon' /> in topbar. Variant 'segment' best for settings page. Hook useFullWidth() returns [mode, setMode, cycle]. SSR-safe — defaults to 'contained' until hydrate.",
990
+ "tags": [
991
+ "ui",
992
+ "layout",
993
+ "preference",
994
+ "localstorage",
995
+ "dashboard"
996
+ ]
997
+ },
998
+ {
999
+ "slug": "command-menu",
1000
+ "title": "Command Menu (⌘K)",
1001
+ "category": "ui",
1002
+ "description": "Global ⌘K command palette. Auto-builds entries from your feature registry (defineFeature) + custom actions. Fuzzy search via cmdk. Mount once at app shell; listens globally. Facade slice — pulls from template-base/frontend/slices/command-menu which re-exports shared/foundation/utils/system/command-menu.",
1003
+ "source": "superspace + kitab-core",
1004
+ "docsUrl": "https://cmdk.paco.me",
1005
+ "install": "npm i cmdk",
1006
+ "npmPackages": [
1007
+ "cmdk"
1008
+ ],
1009
+ "exampleCode": "",
1010
+ "agentRecipe": "Mount <CommandMenu actions={...} /> once at the dashboard shell level. Listens for Cmd+K globally. Actions auto-build from feature registry + workspace + theme/sign-out. Add custom commands via actions prop or by registering in command-registry.ts.",
1011
+ "tags": [
1012
+ "ui",
1013
+ "palette",
1014
+ "cmd-k",
1015
+ "navigation",
1016
+ "keyboard"
1017
+ ]
1018
+ },
1019
+ {
1020
+ "slug": "motion-primitives",
1021
+ "title": "Motion Primitives (8)",
1022
+ "category": "ui",
1023
+ "description": "Eight ready-to-style motion components: marquee, kinetic-heading, magnetic, cursor-spotlight, stat-counter, reading-progress, grain, lightbox. Framer-Motion-powered, tree-shakeable. Facade slice — pulls from template-base/frontend/slices/motion-primitives.",
1024
+ "source": "rahmanef.com",
1025
+ "docsUrl": "",
1026
+ "install": "npm i framer-motion",
1027
+ "npmPackages": [
1028
+ "framer-motion"
1029
+ ],
1030
+ "exampleCode": "",
1031
+ "agentRecipe": "Each primitive is independently importable from @/features/motion-primitives. Use marquee for logo strips, kinetic-heading for hero text, magnetic for CTA buttons, cursor-spotlight for hover-reveal panels, stat-counter for animated numbers, reading-progress for blog top bar, grain for film texture, lightbox for image gallery.",
1032
+ "tags": [
1033
+ "ui",
1034
+ "motion",
1035
+ "animation",
1036
+ "marquee",
1037
+ "framer-motion"
1038
+ ]
1039
+ },
1040
+ {
1041
+ "slug": "responsive-dialog",
1042
+ "title": "Responsive Dialog (Sheet ↔ Modal)",
1043
+ "category": "ui",
1044
+ "description": "ResponsiveDialog — auto-switches between bottom Sheet (mobile) and centered Dialog (desktop) at the md breakpoint. Same API as shadcn Dialog. Kitab forbids raw <dialog>; use this everywhere. Facade slice — pulls from template-base/frontend/slices/responsive-dialog.",
1045
+ "source": "superspace",
1046
+ "docsUrl": "",
1047
+ "install": "",
1048
+ "npmPackages": [],
1049
+ "exampleCode": "",
1050
+ "agentRecipe": "Drop-in for shadcn Dialog. Use <ResponsiveDialog><ResponsiveDialogTrigger>…</ResponsiveDialogTrigger><ResponsiveDialogContent>…</ResponsiveDialogContent></ResponsiveDialog>. On mobile renders as Sheet sliding from bottom; on desktop as centered Dialog. Threshold via useMediaQuery('(min-width: 768px)').",
1051
+ "tags": [
1052
+ "ui",
1053
+ "dialog",
1054
+ "modal",
1055
+ "sheet",
1056
+ "responsive",
1057
+ "primitive"
1058
+ ]
1059
+ },
1060
+ {
1061
+ "slug": "dashboard-shell",
1062
+ "title": "Dashboard Shell — Responsive",
1063
+ "category": "ui",
1064
+ "description": "ResponsiveDashboardShell — desktop sidebar + topbar, mobile dock + sheet sidebar, breakpoint-aware. Ports superspace's layout/dashboard/{Desktop,Mobile,Responsive}DashboardShell + sidebar primary/secondary slots. Facade slice — pulls from template-base/frontend/slices/dashboard-shell.",
1065
+ "source": "superspace",
1066
+ "docsUrl": "",
1067
+ "install": "",
1068
+ "npmPackages": [],
1069
+ "exampleCode": "",
1070
+ "agentRecipe": "Wraps app/(admin) routes. <ResponsiveDashboardShell sidebar={<AppSidebar />} topbar={<TopBar />}>{children}</ResponsiveDashboardShell>. Mobile: sidebar collapses to <Sheet>. Desktop: persistent sidebar + topbar. Embed FullWidthToggle in topbar for instant container resize.",
1071
+ "tags": [
1072
+ "ui",
1073
+ "layout",
1074
+ "dashboard",
1075
+ "sidebar",
1076
+ "topbar",
1077
+ "responsive"
1078
+ ]
1079
+ },
1102
1080
  {
1103
1081
  "slug": "broadcast-channel-sync",
1104
1082
  "title": "BroadcastChannel — Cross-tab Sync",
@@ -1116,6 +1094,115 @@
1116
1094
  "broadcast-channel",
1117
1095
  "demo-pattern"
1118
1096
  ]
1097
+ },
1098
+ {
1099
+ "slug": "rbac-roles",
1100
+ "title": "RBAC — Tiered System Roles",
1101
+ "category": "auth",
1102
+ "description": "Workspace-scoped RBAC with 6 system roles (owner/admin/manager/staff/client/guest) and three tier presets — solo, influencer, organization. Env-based platform admin bypass via PLATFORM_ADMIN_EMAILS. checkPermission / requirePermission helpers, role seeding mutation, @convex-dev/auth aware (no Clerk).",
1103
+ "source": "superspace",
1104
+ "install": "npx rahman-resources add rbac-roles",
1105
+ "npmPackages": [],
1106
+ "exampleCode": "",
1107
+ "agentRecipe": "Three tier presets pick which system roles to seed: solo (owner+admin), influencer (+manager), organization (6 roles). Platform admin via env PLATFORM_ADMIN_EMAILS bypasses all checks. Resolution: platform admin → workspace owner → membership.additionalPermissions → role.permissions.",
1108
+ "tags": [
1109
+ "rbac",
1110
+ "auth",
1111
+ "permissions",
1112
+ "roles",
1113
+ "workspaces"
1114
+ ]
1115
+ },
1116
+ {
1117
+ "slug": "admin-panel",
1118
+ "title": "Admin Panel — Unified Product Admin",
1119
+ "category": "infra",
1120
+ "description": "17-section admin surface (events, funnels, attribution, users, A/B, flags, pricing, CMS, email, audit, ...) gated by RBAC. Auto-filters sidebar by tier (solo/influencer/organization) and user permissions. Single backend resolver (getMyAdminAccess) mirrors frontend gate so UI can never leak.",
1121
+ "source": "superspace + spec",
1122
+ "install": "npx rahman-resources add admin-panel",
1123
+ "npmPackages": [],
1124
+ "exampleCode": "",
1125
+ "agentRecipe": "Wrap pages with <AdminPage workspaceId tier>. AccessGate hides UI for non-admins, AdminShell renders 2-col layout with sidebar filtered by tier+perms. ADMIN_SECTIONS in config.ts is SSOT (17 entries). Personal-brand-os = tier 'solo' = owner sees everything.",
1126
+ "tags": [
1127
+ "admin",
1128
+ "owner",
1129
+ "platform",
1130
+ "rbac",
1131
+ "instrumentation",
1132
+ "panel"
1133
+ ]
1134
+ },
1135
+ {
1136
+ "slug": "event-tracking",
1137
+ "title": "Event Tracking — P0 Instrumentation",
1138
+ "category": "data",
1139
+ "description": "Client SDK + Convex ingestion endpoint for structured product events. Auto-captures page_view/signup/login + UTM/referrer/first-touch attribution. Batched flush via requestIdleCallback. Targets <100ms p99 ingestion.",
1140
+ "source": "spec + superspace analytics",
1141
+ "install": "npx rahman-resources add event-tracking",
1142
+ "npmPackages": [],
1143
+ "exampleCode": "",
1144
+ "agentRecipe": "Writes to analyticsEvents table (no new schema). Anonymous page_view allowed pre-signup; other events require workspaceId. Session id per tab (sessionStorage), first-touch UTM in localStorage. Flush every ~500ms via requestIdleCallback. Cap retry queue at 500.",
1145
+ "tags": [
1146
+ "events",
1147
+ "analytics",
1148
+ "instrumentation",
1149
+ "attribution",
1150
+ "utm",
1151
+ "p0"
1152
+ ]
1153
+ },
1154
+ {
1155
+ "slug": "theme-preset-switcher",
1156
+ "title": "Theme Preset Switcher",
1157
+ "category": "ui",
1158
+ "description": "Runtime theme swap (colors + fonts + shadows + tracking). OKLch CSS vars per preset. Persists to localStorage + Convex. Add a new preset by appending a CSS block in app/globals.css with [data-theme=\"<name>\"], then register in preset-groups.ts.",
1159
+ "source": "rahmanef.com",
1160
+ "install": "npx rahman-resources add theme-preset-switcher",
1161
+ "npmPackages": [],
1162
+ "exampleCode": "",
1163
+ "agentRecipe": "Import ThemePresetSwitcher from @/frontend/shared/ui/components/theme-preset-switcher and mount in the topbar. Presets live in theme-presets.ts; preset-groups.ts groups them for the picker UI.",
1164
+ "tags": [
1165
+ "theme",
1166
+ "presets",
1167
+ "oklch",
1168
+ "design-system"
1169
+ ]
1170
+ },
1171
+ {
1172
+ "slug": "icon-picker",
1173
+ "title": "Notion-Style Icon Picker",
1174
+ "category": "ui",
1175
+ "description": "Emoji + lucide icon picker with search, 10-color Notion palette, Twemoji/native toggle. One string stores emoji OR lucide:Name OR with ?c=hex tint — backwards-compat with raw-emoji fields. Single icon field stores emoji or 'lucide:Name' plus optional '?c=hex'.",
1176
+ "source": "notion-page-clone",
1177
+ "install": "npx rahman-resources add icon-picker",
1178
+ "npmPackages": [],
1179
+ "exampleCode": "",
1180
+ "agentRecipe": "parseIconValue() decodes; lucideValue()/withColor() build. Add 'icon: v.string()' to Convex table — no migration needed for existing emoji fields. Popover variant for inline UI, Inline for sheets/dialogs.",
1181
+ "tags": [
1182
+ "icon",
1183
+ "emoji",
1184
+ "lucide",
1185
+ "picker",
1186
+ "twemoji",
1187
+ "notion"
1188
+ ]
1189
+ },
1190
+ {
1191
+ "slug": "contact-form-resend",
1192
+ "title": "Contact Form + Resend",
1193
+ "category": "email",
1194
+ "description": "Contact form posting to Resend email API. Server Action + Zod input validation. Convex mutation for storage + Resend send.",
1195
+ "source": "cescadesigns",
1196
+ "install": "npx rahman-resources add contact-form-resend",
1197
+ "npmPackages": [],
1198
+ "exampleCode": "",
1199
+ "agentRecipe": "Wire contactMessages.send mutation in convex/. Server emails via Resend from form@yourdomain.com. Always validate inputs with Zod or v.* server-side. Anonymous allowed.",
1200
+ "tags": [
1201
+ "form",
1202
+ "email",
1203
+ "resend",
1204
+ "convex"
1205
+ ]
1119
1206
  }
1120
1207
  ],
1121
1208
  "slices": [
@@ -1123,6 +1210,7 @@
1123
1210
  "slug": "convex-auth",
1124
1211
  "title": "Convex Auth — Email Magic Link",
1125
1212
  "category": "auth",
1213
+ "kind": "backend",
1126
1214
  "version": "0.1.0",
1127
1215
  "description": "@convex-dev/auth with email magic link via Resend. Self-hosted Convex friendly. Hard mandate per kitab CLAUDE.md (no Clerk).",
1128
1216
  "source": "rahmanef63/resource-site",
@@ -1171,10 +1259,75 @@
1171
1259
  ],
1172
1260
  "agentRecipe": "Run `rr add convex-auth`. Then create convex/auth.ts using the kitab pattern (Resend provider). Set env via `npx convex env set` for self-hosted."
1173
1261
  },
1262
+ {
1263
+ "slug": "doku-payment",
1264
+ "title": "DOKU — Indonesia Payment",
1265
+ "category": "payment",
1266
+ "kind": "full",
1267
+ "version": "0.1.0",
1268
+ "description": "Pembayaran lokal Indonesia via DOKU — Checkout (hosted) + Direct (VA / QRIS / e-Wallet / PayLater / Minimarket / Kartu). HMAC-signed REST + signature-verified webhook + idempotent retries. Sibling slice to midtrans-payment dengan paymentOrders schema yang dishare.",
1269
+ "source": "rahmanef63/resource-site",
1270
+ "slicePath": "frontend/slices/doku-payment",
1271
+ "convexPaths": [
1272
+ "convex/features/payment"
1273
+ ],
1274
+ "npm": [],
1275
+ "shadcn": [
1276
+ "card",
1277
+ "button",
1278
+ "dialog",
1279
+ "input",
1280
+ "label",
1281
+ "select",
1282
+ "badge",
1283
+ "skeleton"
1284
+ ],
1285
+ "env": [
1286
+ {
1287
+ "name": "DOKU_CLIENT_ID",
1288
+ "scope": "convex",
1289
+ "required": true
1290
+ },
1291
+ {
1292
+ "name": "DOKU_SECRET_KEY",
1293
+ "scope": "convex",
1294
+ "required": true
1295
+ },
1296
+ {
1297
+ "name": "DOKU_IS_PRODUCTION",
1298
+ "scope": "convex"
1299
+ },
1300
+ {
1301
+ "name": "DOKU_NOTIFY_PATH",
1302
+ "scope": "convex"
1303
+ }
1304
+ ],
1305
+ "peers": [
1306
+ {
1307
+ "slug": "convex-auth",
1308
+ "range": "^0.1",
1309
+ "reason": "Order ownership requires authenticated user."
1310
+ }
1311
+ ],
1312
+ "providers": [
1313
+ "doku"
1314
+ ],
1315
+ "tags": [
1316
+ "payment",
1317
+ "doku",
1318
+ "indonesia",
1319
+ "qris",
1320
+ "virtual-account",
1321
+ "ewallet",
1322
+ "checkout"
1323
+ ],
1324
+ "agentRecipe": "DOKU dual-mode: Checkout (hosted, all channels) atau Direct (single channel, returns VA/QRIS/deeplink). Webhook di /webhooks/doku verify HMAC-SHA256 (canonical: Client-Id + Request-Id + Request-Timestamp + Request-Target + Digest). Idempotency by request_id index. Server-only — no NEXT_PUBLIC_*. Sandbox default (api-sandbox.doku.com); flip DOKU_IS_PRODUCTION=true for live."
1325
+ },
1174
1326
  {
1175
1327
  "slug": "midtrans-payment",
1176
1328
  "title": "Midtrans — Indonesia Payment",
1177
1329
  "category": "payment",
1330
+ "kind": "full",
1178
1331
  "version": "0.1.0",
1179
1332
  "description": "Pembayaran lokal Indonesia via Midtrans Snap (BCA, Mandiri, BRI, e-wallet GoPay/OVO/Dana, QRIS). Webhook untuk konfirmasi. Provider-isolated under components/providers/midtrans + actions/midtrans so Doku/Stripe land as siblings.",
1180
1333
  "source": "rahmanef63/resource-site",
@@ -1231,6 +1384,7 @@
1231
1384
  "slug": "resend-newsletter",
1232
1385
  "title": "Resend — Transactional & Newsletter",
1233
1386
  "category": "email",
1387
+ "kind": "backend",
1234
1388
  "version": "0.1.0",
1235
1389
  "description": "Transactional email + newsletter blast via Resend. Double opt-in flow + audience segmentation. Magic-link delivery for Convex Auth.",
1236
1390
  "source": "rahmanef63/resource-site",
@@ -1273,6 +1427,7 @@
1273
1427
  "slug": "ai-router",
1274
1428
  "title": "AI Router (OpenRouter)",
1275
1429
  "category": "ai",
1430
+ "kind": "backend",
1276
1431
  "version": "0.1.0",
1277
1432
  "description": "Tier-routed LLM access via OpenRouter — nano (Haiku) for classification, mid (Sonnet) for chat, flagship (Opus) for deep reasoning. Per-call usage log.",
1278
1433
  "source": "rahmanef63/resource-site",
@@ -1308,6 +1463,7 @@
1308
1463
  "slug": "vector-search",
1309
1464
  "title": "Convex Vector Search",
1310
1465
  "category": "search",
1466
+ "kind": "full",
1311
1467
  "version": "0.1.0",
1312
1468
  "description": "Embeddings-based search via Convex's built-in vector index. Embed via OpenAI text-embedding-3-small (1536-dim), query via vectorIndex().",
1313
1469
  "source": "rahmanef63/resource-site",
@@ -1344,6 +1500,7 @@
1344
1500
  "slug": "mdx-blog",
1345
1501
  "title": "MDX Blog",
1346
1502
  "category": "content",
1503
+ "kind": "ui",
1347
1504
  "version": "0.1.0",
1348
1505
  "description": "Markdown-with-JSX untuk blog post. File-based under content/blog/*.mdx. Auto-generate ToC, reading-time, syntax highlight, plus embed React components inline.",
1349
1506
  "source": "rahmanef63/resource-site",
@@ -1372,6 +1529,7 @@
1372
1529
  "slug": "cal-com-booking",
1373
1530
  "title": "Cal.com Booking",
1374
1531
  "category": "data",
1532
+ "kind": "full",
1375
1533
  "version": "0.1.0",
1376
1534
  "description": "Embedded Cal.com booking widget + webhook receiver to mirror bookings into Convex.",
1377
1535
  "source": "rahmanef63/resource-site",
@@ -1407,10 +1565,156 @@
1407
1565
  ],
1408
1566
  "agentRecipe": "Embed Cal.com via @calcom/embed-react di halaman services. Configure webhook di Cal.com dashboard → POST ke /api/cal-webhook → upsert booking di Convex."
1409
1567
  },
1568
+ {
1569
+ "slug": "full-width-toggle",
1570
+ "title": "Full Width Toggle",
1571
+ "category": "ui",
1572
+ "kind": "ui",
1573
+ "version": "0.1.0",
1574
+ "description": "Page-container width toggle (contained / wide / full). Per-device localStorage + cross-tab sync. Ships useFullWidth hook + FullWidthToggle button (icon/button/segment variants) + WidthContainer wrapper. Zero backend, zero env.",
1575
+ "source": "rahmanef63/resource-site",
1576
+ "slicePath": "frontend/slices/full-width-toggle",
1577
+ "convexPaths": [],
1578
+ "npm": [],
1579
+ "shadcn": [
1580
+ "button"
1581
+ ],
1582
+ "env": [],
1583
+ "peers": [],
1584
+ "providers": [],
1585
+ "tags": [
1586
+ "ui",
1587
+ "layout",
1588
+ "preference",
1589
+ "localstorage",
1590
+ "dashboard"
1591
+ ],
1592
+ "agentRecipe": "Drop <WidthContainer> around page content, <FullWidthToggle variant='icon' /> in topbar. Variant 'segment' best for settings page. Hook useFullWidth() returns [mode, setMode, cycle]. SSR-safe — defaults to 'contained' until hydrate."
1593
+ },
1594
+ {
1595
+ "slug": "command-menu",
1596
+ "title": "Command Menu (⌘K)",
1597
+ "category": "ui",
1598
+ "kind": "ui",
1599
+ "version": "0.1.0",
1600
+ "description": "Global ⌘K command palette. Auto-builds entries from your feature registry (defineFeature) + custom actions. Fuzzy search via cmdk. Mount once at app shell; listens globally. Facade slice — pulls from template-base/frontend/slices/command-menu which re-exports shared/foundation/utils/system/command-menu.",
1601
+ "source": "superspace + kitab-core",
1602
+ "slicePath": "template-base/frontend/slices/command-menu",
1603
+ "convexPaths": [],
1604
+ "npm": [
1605
+ "cmdk@^1.0.0"
1606
+ ],
1607
+ "shadcn": [
1608
+ "command",
1609
+ "dialog"
1610
+ ],
1611
+ "env": [],
1612
+ "peers": [],
1613
+ "providers": [],
1614
+ "tags": [
1615
+ "ui",
1616
+ "palette",
1617
+ "cmd-k",
1618
+ "navigation",
1619
+ "keyboard"
1620
+ ],
1621
+ "agentRecipe": "Mount <CommandMenu actions={...} /> once at the dashboard shell level. Listens for Cmd+K globally. Actions auto-build from feature registry + workspace + theme/sign-out. Add custom commands via actions prop or by registering in command-registry.ts."
1622
+ },
1623
+ {
1624
+ "slug": "motion-primitives",
1625
+ "title": "Motion Primitives (8)",
1626
+ "category": "ui",
1627
+ "kind": "ui",
1628
+ "version": "0.1.0",
1629
+ "description": "Eight ready-to-style motion components: marquee, kinetic-heading, magnetic, cursor-spotlight, stat-counter, reading-progress, grain, lightbox. Framer-Motion-powered, tree-shakeable. Facade slice — pulls from template-base/frontend/slices/motion-primitives.",
1630
+ "source": "rahmanef.com",
1631
+ "slicePath": "template-base/frontend/slices/motion-primitives",
1632
+ "convexPaths": [],
1633
+ "npm": [
1634
+ "framer-motion@^11.0.0"
1635
+ ],
1636
+ "shadcn": [],
1637
+ "env": [],
1638
+ "peers": [],
1639
+ "providers": [],
1640
+ "tags": [
1641
+ "ui",
1642
+ "motion",
1643
+ "animation",
1644
+ "marquee",
1645
+ "framer-motion"
1646
+ ],
1647
+ "agentRecipe": "Each primitive is independently importable from @/features/motion-primitives. Use marquee for logo strips, kinetic-heading for hero text, magnetic for CTA buttons, cursor-spotlight for hover-reveal panels, stat-counter for animated numbers, reading-progress for blog top bar, grain for film texture, lightbox for image gallery."
1648
+ },
1649
+ {
1650
+ "slug": "responsive-dialog",
1651
+ "title": "Responsive Dialog (Sheet ↔ Modal)",
1652
+ "category": "ui",
1653
+ "kind": "ui",
1654
+ "version": "0.1.0",
1655
+ "description": "ResponsiveDialog — auto-switches between bottom Sheet (mobile) and centered Dialog (desktop) at the md breakpoint. Same API as shadcn Dialog. Kitab forbids raw <dialog>; use this everywhere. Facade slice — pulls from template-base/frontend/slices/responsive-dialog.",
1656
+ "source": "superspace",
1657
+ "slicePath": "template-base/frontend/slices/responsive-dialog",
1658
+ "convexPaths": [],
1659
+ "npm": [],
1660
+ "shadcn": [
1661
+ "dialog",
1662
+ "sheet"
1663
+ ],
1664
+ "env": [],
1665
+ "peers": [],
1666
+ "providers": [],
1667
+ "tags": [
1668
+ "ui",
1669
+ "dialog",
1670
+ "modal",
1671
+ "sheet",
1672
+ "responsive",
1673
+ "primitive"
1674
+ ],
1675
+ "agentRecipe": "Drop-in for shadcn Dialog. Use <ResponsiveDialog><ResponsiveDialogTrigger>…</ResponsiveDialogTrigger><ResponsiveDialogContent>…</ResponsiveDialogContent></ResponsiveDialog>. On mobile renders as Sheet sliding from bottom; on desktop as centered Dialog. Threshold via useMediaQuery('(min-width: 768px)')."
1676
+ },
1677
+ {
1678
+ "slug": "dashboard-shell",
1679
+ "title": "Dashboard Shell — Responsive",
1680
+ "category": "ui",
1681
+ "kind": "ui",
1682
+ "version": "0.1.0",
1683
+ "description": "ResponsiveDashboardShell — desktop sidebar + topbar, mobile dock + sheet sidebar, breakpoint-aware. Ports superspace's layout/dashboard/{Desktop,Mobile,Responsive}DashboardShell + sidebar primary/secondary slots. Facade slice — pulls from template-base/frontend/slices/dashboard-shell.",
1684
+ "source": "superspace",
1685
+ "slicePath": "template-base/frontend/slices/dashboard-shell",
1686
+ "convexPaths": [],
1687
+ "npm": [],
1688
+ "shadcn": [
1689
+ "sheet",
1690
+ "scroll-area",
1691
+ "separator",
1692
+ "tooltip"
1693
+ ],
1694
+ "env": [],
1695
+ "peers": [
1696
+ {
1697
+ "slug": "full-width-toggle",
1698
+ "range": "^0.1",
1699
+ "reason": "Topbar surfaces the width toggle when this is mounted."
1700
+ }
1701
+ ],
1702
+ "providers": [],
1703
+ "tags": [
1704
+ "ui",
1705
+ "layout",
1706
+ "dashboard",
1707
+ "sidebar",
1708
+ "topbar",
1709
+ "responsive"
1710
+ ],
1711
+ "agentRecipe": "Wraps app/(admin) routes. <ResponsiveDashboardShell sidebar={<AppSidebar />} topbar={<TopBar />}>{children}</ResponsiveDashboardShell>. Mobile: sidebar collapses to <Sheet>. Desktop: persistent sidebar + topbar. Embed FullWidthToggle in topbar for instant container resize."
1712
+ },
1410
1713
  {
1411
1714
  "slug": "broadcast-channel-sync",
1412
1715
  "title": "BroadcastChannel — Cross-tab Sync",
1413
1716
  "category": "realtime",
1717
+ "kind": "ui",
1414
1718
  "version": "0.1.0",
1415
1719
  "description": "Same-origin cross-tab + cross-iframe state sync via BroadcastChannel API. Tiny, no backend, no install.",
1416
1720
  "source": "Web Platform — BroadcastChannel API",
@@ -1428,6 +1732,211 @@
1428
1732
  "demo-pattern"
1429
1733
  ],
1430
1734
  "agentRecipe": "Use BroadcastChannel only for demo / cross-iframe state mirroring. Production data still goes through Convex realtime. Use the useBroadcastSync(channelName, initial) hook from @/features/broadcast-channel-sync."
1735
+ },
1736
+ {
1737
+ "slug": "rbac-roles",
1738
+ "title": "RBAC — Tiered System Roles",
1739
+ "category": "auth",
1740
+ "kind": "backend",
1741
+ "version": "0.1.0",
1742
+ "description": "Workspace-scoped RBAC with 6 system roles (owner/admin/manager/staff/client/guest) and three tier presets — solo, influencer, organization. Env-based platform admin bypass via PLATFORM_ADMIN_EMAILS. checkPermission / requirePermission helpers, role seeding mutation, @convex-dev/auth aware (no Clerk).",
1743
+ "source": "superspace",
1744
+ "slicePath": "",
1745
+ "convexPaths": [
1746
+ "template-base/convex/lib/rbac"
1747
+ ],
1748
+ "npm": [],
1749
+ "shadcn": [],
1750
+ "env": [
1751
+ {
1752
+ "name": "PLATFORM_ADMIN_EMAILS",
1753
+ "scope": "convex",
1754
+ "description": "Comma-separated emails granted cross-workspace admin."
1755
+ }
1756
+ ],
1757
+ "peers": [
1758
+ {
1759
+ "slug": "convex-auth",
1760
+ "range": "^0.1",
1761
+ "reason": "RBAC checks the authed user identity."
1762
+ }
1763
+ ],
1764
+ "providers": [],
1765
+ "tags": [
1766
+ "rbac",
1767
+ "auth",
1768
+ "permissions",
1769
+ "roles",
1770
+ "workspaces"
1771
+ ],
1772
+ "agentRecipe": "Three tier presets pick which system roles to seed: solo (owner+admin), influencer (+manager), organization (6 roles). Platform admin via env PLATFORM_ADMIN_EMAILS bypasses all checks. Resolution: platform admin → workspace owner → membership.additionalPermissions → role.permissions."
1773
+ },
1774
+ {
1775
+ "slug": "admin-panel",
1776
+ "title": "Admin Panel — Unified Product Admin",
1777
+ "category": "infra",
1778
+ "kind": "full",
1779
+ "version": "0.1.0",
1780
+ "description": "17-section admin surface (events, funnels, attribution, users, A/B, flags, pricing, CMS, email, audit, ...) gated by RBAC. Auto-filters sidebar by tier (solo/influencer/organization) and user permissions. Single backend resolver (getMyAdminAccess) mirrors frontend gate so UI can never leak.",
1781
+ "source": "superspace + spec",
1782
+ "slicePath": "template-base/frontend/slices/admin-panel",
1783
+ "convexPaths": [
1784
+ "template-base/convex/features/admin-panel"
1785
+ ],
1786
+ "npm": [],
1787
+ "shadcn": [
1788
+ "card",
1789
+ "badge",
1790
+ "button"
1791
+ ],
1792
+ "env": [],
1793
+ "peers": [
1794
+ {
1795
+ "slug": "rbac-roles",
1796
+ "range": "^0.1",
1797
+ "reason": "Admin sections require RBAC perms — must seed roles first."
1798
+ }
1799
+ ],
1800
+ "providers": [],
1801
+ "tags": [
1802
+ "admin",
1803
+ "owner",
1804
+ "platform",
1805
+ "rbac",
1806
+ "instrumentation",
1807
+ "panel"
1808
+ ],
1809
+ "agentRecipe": "Wrap pages with <AdminPage workspaceId tier>. AccessGate hides UI for non-admins, AdminShell renders 2-col layout with sidebar filtered by tier+perms. ADMIN_SECTIONS in config.ts is SSOT (17 entries). Personal-brand-os = tier 'solo' = owner sees everything."
1810
+ },
1811
+ {
1812
+ "slug": "event-tracking",
1813
+ "title": "Event Tracking — P0 Instrumentation",
1814
+ "category": "data",
1815
+ "kind": "full",
1816
+ "version": "0.1.0",
1817
+ "description": "Client SDK + Convex ingestion endpoint for structured product events. Auto-captures page_view/signup/login + UTM/referrer/first-touch attribution. Batched flush via requestIdleCallback. Targets <100ms p99 ingestion.",
1818
+ "source": "spec + superspace analytics",
1819
+ "slicePath": "template-base/frontend/slices/admin-panel/slices/events",
1820
+ "convexPaths": [
1821
+ "template-base/convex/features/admin-panel",
1822
+ "template-base/convex/features/analytics"
1823
+ ],
1824
+ "npm": [],
1825
+ "shadcn": [],
1826
+ "env": [],
1827
+ "peers": [
1828
+ {
1829
+ "slug": "admin-panel",
1830
+ "range": "^0.1",
1831
+ "reason": "Lives under admin slice events subfolder."
1832
+ }
1833
+ ],
1834
+ "providers": [],
1835
+ "tags": [
1836
+ "events",
1837
+ "analytics",
1838
+ "instrumentation",
1839
+ "attribution",
1840
+ "utm",
1841
+ "p0"
1842
+ ],
1843
+ "agentRecipe": "Writes to analyticsEvents table (no new schema). Anonymous page_view allowed pre-signup; other events require workspaceId. Session id per tab (sessionStorage), first-touch UTM in localStorage. Flush every ~500ms via requestIdleCallback. Cap retry queue at 500."
1844
+ },
1845
+ {
1846
+ "slug": "theme-preset-switcher",
1847
+ "title": "Theme Preset Switcher",
1848
+ "category": "ui",
1849
+ "kind": "ui",
1850
+ "version": "0.1.0",
1851
+ "description": "Runtime theme swap (colors + fonts + shadows + tracking). OKLch CSS vars per preset. Persists to localStorage + Convex. Add a new preset by appending a CSS block in app/globals.css with [data-theme=\"<name>\"], then register in preset-groups.ts.",
1852
+ "source": "rahmanef.com",
1853
+ "slicePath": "template-base/frontend/shared/theme",
1854
+ "convexPaths": [],
1855
+ "npm": [],
1856
+ "shadcn": [],
1857
+ "env": [],
1858
+ "peers": [],
1859
+ "providers": [],
1860
+ "tags": [
1861
+ "theme",
1862
+ "presets",
1863
+ "oklch",
1864
+ "design-system"
1865
+ ],
1866
+ "agentRecipe": "Import ThemePresetSwitcher from @/frontend/shared/ui/components/theme-preset-switcher and mount in the topbar. Presets live in theme-presets.ts; preset-groups.ts groups them for the picker UI."
1867
+ },
1868
+ {
1869
+ "slug": "icon-picker",
1870
+ "title": "Notion-Style Icon Picker",
1871
+ "category": "ui",
1872
+ "kind": "ui",
1873
+ "version": "0.1.0",
1874
+ "description": "Emoji + lucide icon picker with search, 10-color Notion palette, Twemoji/native toggle. One string stores emoji OR lucide:Name OR with ?c=hex tint — backwards-compat with raw-emoji fields. Single icon field stores emoji or 'lucide:Name' plus optional '?c=hex'.",
1875
+ "source": "notion-page-clone",
1876
+ "slicePath": "template-base/frontend/slices/notion/slices/icon-picker",
1877
+ "convexPaths": [],
1878
+ "npm": [],
1879
+ "shadcn": [
1880
+ "popover",
1881
+ "button",
1882
+ "input"
1883
+ ],
1884
+ "env": [],
1885
+ "peers": [],
1886
+ "providers": [],
1887
+ "tags": [
1888
+ "icon",
1889
+ "emoji",
1890
+ "lucide",
1891
+ "picker",
1892
+ "twemoji",
1893
+ "notion"
1894
+ ],
1895
+ "agentRecipe": "parseIconValue() decodes; lucideValue()/withColor() build. Add 'icon: v.string()' to Convex table — no migration needed for existing emoji fields. Popover variant for inline UI, Inline for sheets/dialogs."
1896
+ },
1897
+ {
1898
+ "slug": "contact-form-resend",
1899
+ "title": "Contact Form + Resend",
1900
+ "category": "email",
1901
+ "kind": "full",
1902
+ "version": "0.1.0",
1903
+ "description": "Contact form posting to Resend email API. Server Action + Zod input validation. Convex mutation for storage + Resend send.",
1904
+ "source": "cescadesigns",
1905
+ "slicePath": "template-base/frontend/slices/contact-form-resend",
1906
+ "convexPaths": [],
1907
+ "npm": [
1908
+ "resend@^4.0.0",
1909
+ "framer-motion@^11.0.0"
1910
+ ],
1911
+ "shadcn": [
1912
+ "card",
1913
+ "button",
1914
+ "input",
1915
+ "label",
1916
+ "textarea"
1917
+ ],
1918
+ "env": [
1919
+ {
1920
+ "name": "RESEND_API_KEY",
1921
+ "scope": "convex",
1922
+ "required": true
1923
+ }
1924
+ ],
1925
+ "peers": [
1926
+ {
1927
+ "slug": "convex-auth",
1928
+ "range": "^0.1",
1929
+ "reason": "Optional — anonymous submission works without auth."
1930
+ }
1931
+ ],
1932
+ "providers": [],
1933
+ "tags": [
1934
+ "form",
1935
+ "email",
1936
+ "resend",
1937
+ "convex"
1938
+ ],
1939
+ "agentRecipe": "Wire contactMessages.send mutation in convex/. Server emails via Resend from form@yourdomain.com. Always validate inputs with Zod or v.* server-side. Anonymous allowed."
1431
1940
  }
1432
1941
  ]
1433
1942
  }
@@ -43,6 +43,11 @@
43
43
  "infra"
44
44
  ]
45
45
  },
46
+ "kind": {
47
+ "type": "string",
48
+ "enum": ["ui", "backend", "full"],
49
+ "description": "Slice shape — ui (no convex), backend (no UI), full (both)."
50
+ },
46
51
  "title": { "type": "string", "minLength": 3 },
47
52
  "description": { "type": "string", "minLength": 10 },
48
53
  "namespace": {
@@ -53,12 +58,11 @@
53
58
  "convex": {
54
59
  "type": "object",
55
60
  "additionalProperties": false,
56
- "required": ["rootPaths"],
57
61
  "properties": {
58
62
  "tablesExport": {
59
63
  "type": "string",
60
- "pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
61
- "description": "Name of the table-fragment export in schema.ts (e.g., midtransTables)."
64
+ "pattern": "^([a-zA-Z][a-zA-Z0-9_]*)?$",
65
+ "description": "Name of the table-fragment export in schema.ts (e.g., midtransTables). Empty for ui-only slices."
62
66
  },
63
67
  "schemaPath": {
64
68
  "type": "string",
@@ -66,25 +70,25 @@
66
70
  },
67
71
  "rootPaths": {
68
72
  "type": "array",
69
- "minItems": 1,
70
73
  "items": { "type": "string" },
71
- "description": "Convex folders the lift pulls into the consumer's convex/."
74
+ "description": "Convex folders the lift pulls into the consumer's convex/. Empty for ui-only slices."
72
75
  }
73
76
  }
74
77
  },
75
78
  "frontend": {
76
79
  "type": "object",
77
80
  "additionalProperties": false,
78
- "required": ["slicePath", "configExport"],
81
+ "required": ["slicePath"],
79
82
  "properties": {
80
83
  "slicePath": {
81
84
  "type": "string",
82
- "pattern": "^frontend/slices/[a-z0-9_/-]+$"
85
+ "pattern": "^(template-base/)?frontend/slices/[a-z0-9_/-]+$",
86
+ "description": "Portable home → frontend/slices/<slug>. Foundation home → template-base/frontend/slices/<slug>."
83
87
  },
84
88
  "configExport": {
85
89
  "type": "string",
86
- "pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
87
- "description": "Name of the defineFeature(...) export the registry generators look for."
90
+ "pattern": "^([a-zA-Z][a-zA-Z0-9_]*)?$",
91
+ "description": "Name of the defineFeature(...) export the registry generators look for. Empty for ui-only slices."
88
92
  }
89
93
  }
90
94
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rahman-resources",
3
- "version": "0.9.1",
3
+ "version": "0.11.1",
4
4
  "description": "Scaffolder + installer for Rahman Resources kitab — npx rahman-resources init/add/lift/scaffold-slice/publish-slice. Tier-3 portable feature slices + manifest + skills + CRUD workflows.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,9 +26,12 @@
26
26
  "gen": "node scripts/gen-manifest.mjs",
27
27
  "validate": "node scripts/validate.mjs",
28
28
  "validate:slices": "node scripts/validate-slice.mjs",
29
+ "validate:parity": "node scripts/validate-slice-parity.mjs",
30
+ "validate:structure": "node scripts/validate-structure.mjs",
31
+ "validate:all": "node scripts/validate.mjs && node scripts/validate-slice.mjs --check && node scripts/validate-slice-parity.mjs && node scripts/validate-structure.mjs",
29
32
  "sync:skills": "node scripts/sync-skills.mjs",
30
33
  "sync:skills:check": "node scripts/sync-skills.mjs --check",
31
- "prepublishOnly": "node scripts/sync-skills.mjs --check && node scripts/validate.mjs && node scripts/validate-slice.mjs --check"
34
+ "prepublishOnly": "node scripts/sync-skills.mjs --check && node scripts/validate.mjs && node scripts/validate-slice.mjs --check && node scripts/validate-slice-parity.mjs && node scripts/validate-structure.mjs"
32
35
  },
33
36
  "dependencies": {
34
37
  "kleur": "^4.1.5",