@jskit-ai/agent-docs 0.1.149 → 0.1.151

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 (240) hide show
  1. package/guide/agent/app-setup/existing-application-migration.md +8 -6
  2. package/guide/agent/app-setup/initial-scaffolding.md +9 -7
  3. package/guide/agent/app-setup/quickstart.md +9 -6
  4. package/guide/agent/framework/application-operations.md +91 -0
  5. package/guide/agent/framework/crud-operations.md +83 -0
  6. package/guide/agent/framework/material-3.md +113 -0
  7. package/guide/agent/framework/ui-operations.md +79 -0
  8. package/guide/agent/index.md +13 -3
  9. package/package.json +1 -1
  10. package/patterns/feature-package/example/booking-engine/package.json +1 -1
  11. package/patterns/minimal-foundation/PATTERN.md +2 -0
  12. package/patterns/minimal-foundation/example/AGENTS.md +12 -8
  13. package/patterns/minimal-foundation/example/Procfile +0 -1
  14. package/patterns/minimal-foundation/example/package.json +4 -5
  15. package/patterns/minimal-foundation/example/src/pages/home/index.vue +0 -27
  16. package/patterns/minimal-foundation/example/tests/e2e/base-shell.spec.ts +1 -1
  17. package/patterns/page-scaffolding.md +5 -3
  18. package/patterns/shell-foundation/PATTERN.md +2 -0
  19. package/patterns/shell-foundation/example/AGENTS.md +12 -8
  20. package/patterns/shell-foundation/example/Procfile +0 -1
  21. package/patterns/shell-foundation/example/package.json +5 -6
  22. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  23. package/patterns/shell-foundation/example/src/pages/home/index.vue +5 -31
  24. package/patterns/shell-foundation/example/src/pages/home/settings/general/index.vue +3 -6
  25. package/patterns/shell-foundation/example/src/pages/home/settings.vue +0 -19
  26. package/patterns/shell-foundation/example/tests/e2e/base-shell.spec.ts +1 -1
  27. package/patterns/ui-contract.md +2 -0
  28. package/patterns/ui-testing.md +1 -1
  29. package/reference/autogen/PATTERN_INDEX.md +61 -61
  30. package/skills/jskit/SKILL.md +8 -5
  31. package/skills/jskit/references/app-operations.md +12 -11
  32. package/skills/jskit/references/crud-operations.md +10 -6
  33. package/skills/jskit/references/existing-application-migration.md +9 -12
  34. package/skills/jskit/references/material-3.md +5 -2
  35. package/skills/jskit/references/pattern-index.md +61 -61
  36. package/skills/jskit/references/patterns/app/minimal-foundation/PATTERN.md +107 -0
  37. package/skills/jskit/references/patterns/app/minimal-foundation/example/.nvmrc +1 -0
  38. package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +21 -0
  39. package/skills/jskit/references/patterns/app/minimal-foundation/example/Procfile +1 -0
  40. package/skills/jskit/references/patterns/app/minimal-foundation/example/app.json +14 -0
  41. package/skills/jskit/references/patterns/app/minimal-foundation/example/bin/develop.js +71 -0
  42. package/skills/jskit/references/patterns/app/minimal-foundation/example/bin/server.js +8 -0
  43. package/skills/jskit/references/patterns/app/minimal-foundation/example/config/public.js +40 -0
  44. package/skills/jskit/references/patterns/app/minimal-foundation/example/config/server.js +1 -0
  45. package/skills/jskit/references/patterns/app/minimal-foundation/example/config/surfaceAccessPolicies.js +3 -0
  46. package/skills/jskit/references/patterns/app/minimal-foundation/example/eslint.config.mjs +19 -0
  47. package/skills/jskit/references/patterns/app/minimal-foundation/example/favicon.svg +7 -0
  48. package/skills/jskit/references/patterns/app/minimal-foundation/example/gitignore +9 -0
  49. package/skills/jskit/references/patterns/app/minimal-foundation/example/index.html +13 -0
  50. package/skills/jskit/references/patterns/app/minimal-foundation/example/jsconfig.json +8 -0
  51. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +62 -0
  52. package/skills/jskit/references/patterns/app/minimal-foundation/example/packages/main/package.json +42 -0
  53. package/skills/jskit/references/patterns/app/minimal-foundation/example/packages/main/src/shared/index.js +10 -0
  54. package/skills/jskit/references/patterns/app/minimal-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
  55. package/skills/jskit/references/patterns/app/minimal-foundation/example/playwright.config.mjs +31 -0
  56. package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/runtimeEnv.js +45 -0
  57. package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/surfaceRuntime.js +10 -0
  58. package/skills/jskit/references/patterns/app/minimal-foundation/example/server.js +195 -0
  59. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/App.vue +13 -0
  60. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/main.js +85 -0
  61. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/pages/home/index.vue +21 -0
  62. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/pages/home.vue +13 -0
  63. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/views/NotFound.vue +13 -0
  64. package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/client/smoke.vitest.js +7 -0
  65. package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
  66. package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/server/smoke.test.js +16 -0
  67. package/skills/jskit/references/patterns/app/minimal-foundation/example/vite.config.mjs +80 -0
  68. package/skills/jskit/references/patterns/app/shell-foundation/PATTERN.md +113 -0
  69. package/skills/jskit/references/patterns/app/shell-foundation/example/.nvmrc +1 -0
  70. package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +21 -0
  71. package/skills/jskit/references/patterns/app/shell-foundation/example/Procfile +1 -0
  72. package/skills/jskit/references/patterns/app/shell-foundation/example/app.json +14 -0
  73. package/skills/jskit/references/patterns/app/shell-foundation/example/bin/develop.js +71 -0
  74. package/skills/jskit/references/patterns/app/shell-foundation/example/bin/server.js +8 -0
  75. package/skills/jskit/references/patterns/app/shell-foundation/example/config/public.js +40 -0
  76. package/skills/jskit/references/patterns/app/shell-foundation/example/config/server.js +1 -0
  77. package/skills/jskit/references/patterns/app/shell-foundation/example/config/surfaceAccessPolicies.js +3 -0
  78. package/skills/jskit/references/patterns/app/shell-foundation/example/eslint.config.mjs +19 -0
  79. package/skills/jskit/references/patterns/app/shell-foundation/example/favicon.svg +7 -0
  80. package/skills/jskit/references/patterns/app/shell-foundation/example/gitignore +9 -0
  81. package/skills/jskit/references/patterns/app/shell-foundation/example/index.html +13 -0
  82. package/skills/jskit/references/patterns/app/shell-foundation/example/jsconfig.json +8 -0
  83. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +64 -0
  84. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +56 -0
  85. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/index.js +9 -0
  86. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +18 -0
  87. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/shared/index.js +10 -0
  88. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
  89. package/skills/jskit/references/patterns/app/shell-foundation/example/playwright.config.mjs +31 -0
  90. package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/runtimeEnv.js +45 -0
  91. package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/surfaceRuntime.js +10 -0
  92. package/skills/jskit/references/patterns/app/shell-foundation/example/server.js +195 -0
  93. package/skills/jskit/references/patterns/app/shell-foundation/example/src/App.vue +11 -0
  94. package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/ShellLayout.vue +12 -0
  95. package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/MenuLinkItem.vue +30 -0
  96. package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
  97. package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/TabLinkItem.vue +42 -0
  98. package/skills/jskit/references/patterns/app/shell-foundation/example/src/error.js +19 -0
  99. package/skills/jskit/references/patterns/app/shell-foundation/example/src/main.js +85 -0
  100. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/index.vue +90 -0
  101. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/general/index.vue +37 -0
  102. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/index.vue +7 -0
  103. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings.vue +90 -0
  104. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home.vue +20 -0
  105. package/skills/jskit/references/patterns/app/shell-foundation/example/src/placement.js +56 -0
  106. package/skills/jskit/references/patterns/app/shell-foundation/example/src/placementTopology.js +149 -0
  107. package/skills/jskit/references/patterns/app/shell-foundation/example/src/views/NotFound.vue +13 -0
  108. package/skills/jskit/references/patterns/app/shell-foundation/example/tests/client/smoke.vitest.js +7 -0
  109. package/skills/jskit/references/patterns/app/shell-foundation/example/tests/e2e/adaptive-shell.spec.ts +10 -0
  110. package/skills/jskit/references/patterns/app/shell-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
  111. package/skills/jskit/references/patterns/app/shell-foundation/example/tests/server/smoke.test.js +16 -0
  112. package/skills/jskit/references/patterns/app/shell-foundation/example/vite.config.mjs +80 -0
  113. package/skills/jskit/references/patterns/assistant/assistant-surface/PATTERN.md +77 -0
  114. package/skills/jskit/references/patterns/assistant/assistant-surface/example/config/public.js +19 -0
  115. package/skills/jskit/references/patterns/assistant/assistant-surface/example/config/server.js +9 -0
  116. package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/pages/admin/assistant/index.vue +7 -0
  117. package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/pages/admin/settings/assistant/index.vue +7 -0
  118. package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/placement.js +38 -0
  119. package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +90 -0
  120. package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/login.vue +17 -0
  121. package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/reset-password.vue +17 -0
  122. package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/signout.vue +17 -0
  123. package/skills/jskit/references/patterns/auth/auth-surface/example/src/runtime/authGuardRuntime.js +7 -0
  124. package/skills/jskit/references/patterns/auth/auth-surface/example/src/runtime/authHttpClient.js +1 -0
  125. package/skills/jskit/references/patterns/auth/auth-surface/example/src/runtime/useSignOut.js +1 -0
  126. package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/LoginView.vue +7 -0
  127. package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/ResetPasswordView.vue +7 -0
  128. package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/SignOutView.vue +7 -0
  129. package/skills/jskit/references/patterns/auth/auth-surface/example/tools/preview-identity +5 -0
  130. package/skills/jskit/references/patterns/auth/supabase-auth/PATTERN.md +67 -0
  131. package/skills/jskit/references/patterns/auth/supabase-auth/example/.env.example +4 -0
  132. package/skills/jskit/references/patterns/auth/supabase-auth/example/config/server.js +11 -0
  133. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +7 -0
  134. package/skills/jskit/references/patterns/console/console-surface/PATTERN.md +59 -0
  135. package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/index.vue +43 -0
  136. package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings/index.vue +16 -0
  137. package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings.vue +81 -0
  138. package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console.vue +20 -0
  139. package/skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md +89 -0
  140. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookEditPage.vue +52 -0
  141. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookFormFields.vue +52 -0
  142. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookListPage.vue +50 -0
  143. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookNewPage.vue +43 -0
  144. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookViewPage.vue +49 -0
  145. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/formFields.js +31 -0
  146. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/listExtensions.js +9 -0
  147. package/skills/jskit/references/patterns/crud/json-api-resource-package/PATTERN.md +160 -0
  148. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/migrations/20260815000000_books.cjs +22 -0
  149. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +41 -0
  150. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/server/BooksFeature.js +13 -0
  151. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/shared/bookResource.js +83 -0
  152. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/shared/index.js +1 -0
  153. package/skills/jskit/references/patterns/crud/resource-contract/PATTERN.md +102 -0
  154. package/skills/jskit/references/patterns/crud/resource-contract/example/bookResource.js +87 -0
  155. package/skills/jskit/references/patterns/database/mysql-application/PATTERN.md +80 -0
  156. package/skills/jskit/references/patterns/database/mysql-application/example/.env.example +5 -0
  157. package/skills/jskit/references/patterns/database/mysql-application/example/.github/workflows/verify.yml +39 -0
  158. package/skills/jskit/references/patterns/database/mysql-application/example/knexfile.js +11 -0
  159. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +13 -0
  160. package/skills/jskit/references/patterns/database/mysql-application/example/scripts/prepare-database.js +3 -0
  161. package/skills/jskit/references/patterns/database/postgres-application/PATTERN.md +80 -0
  162. package/skills/jskit/references/patterns/database/postgres-application/example/.env.example +5 -0
  163. package/skills/jskit/references/patterns/database/postgres-application/example/.github/workflows/verify.yml +38 -0
  164. package/skills/jskit/references/patterns/database/postgres-application/example/knexfile.js +11 -0
  165. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +13 -0
  166. package/skills/jskit/references/patterns/database/postgres-application/example/scripts/prepare-database.js +3 -0
  167. package/skills/jskit/references/patterns/mobile/android-application/PATTERN.md +71 -0
  168. package/skills/jskit/references/patterns/mobile/android-application/example/capacitor.config.json +10 -0
  169. package/skills/jskit/references/patterns/realtime/realtime-application/PATTERN.md +69 -0
  170. package/skills/jskit/references/patterns/realtime/realtime-application/example/.env.example +1 -0
  171. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +8 -0
  172. package/skills/jskit/references/patterns/realtime/realtime-application/example/src/placement.js +17 -0
  173. package/skills/jskit/references/patterns/server/feature-package/PATTERN.md +108 -0
  174. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +48 -0
  175. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +33 -0
  176. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/actions.js +26 -0
  177. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/inputSchemas.js +19 -0
  178. package/skills/jskit/references/patterns/server/feature-package/example/variations/AvailabilityEngineProvider.js +31 -0
  179. package/skills/jskit/references/patterns/server/feature-package/example/variations/InvoiceRollupProvider.js +36 -0
  180. package/skills/jskit/references/patterns/server/feature-package/example/variations/customKnexRepository.js +34 -0
  181. package/skills/jskit/references/patterns/server/feature-package/example/variations/orchestratorService.js +23 -0
  182. package/skills/jskit/references/patterns/shell/application-shell/PATTERN.md +67 -0
  183. package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/App.vue +13 -0
  184. package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/pages/home/index.vue +21 -0
  185. package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/pages/home.vue +13 -0
  186. package/skills/jskit/references/patterns/shell/application-shell/example/src/App.vue +11 -0
  187. package/skills/jskit/references/patterns/shell/application-shell/example/src/components/ShellLayout.vue +13 -0
  188. package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/MenuLinkItem.vue +30 -0
  189. package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
  190. package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/TabLinkItem.vue +42 -0
  191. package/skills/jskit/references/patterns/shell/application-shell/example/src/error.js +19 -0
  192. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/index.vue +80 -0
  193. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/general/index.vue +37 -0
  194. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/index.vue +7 -0
  195. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings.vue +90 -0
  196. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home.vue +20 -0
  197. package/skills/jskit/references/patterns/shell/application-shell/example/src/placement.js +56 -0
  198. package/skills/jskit/references/patterns/shell/application-shell/example/src/placementTopology.js +149 -0
  199. package/skills/jskit/references/patterns/shell/application-shell/example/tests/e2e/adaptive-shell.spec.ts +10 -0
  200. package/skills/jskit/references/patterns/ui/page-and-placement/PATTERN.md +77 -0
  201. package/skills/jskit/references/patterns/ui/page-and-placement/example/packages/main/src/client/providers/MainClientProvider.js +14 -0
  202. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/components/SyncStatusElement.vue +16 -0
  203. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/activity.vue +7 -0
  204. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/index.vue +7 -0
  205. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/overview.vue +7 -0
  206. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports.vue +25 -0
  207. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placement.js +52 -0
  208. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placementTopology.js +29 -0
  209. package/skills/jskit/references/patterns/users/account-settings/PATTERN.md +63 -0
  210. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsNotificationsSection.vue +67 -0
  211. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue +137 -0
  212. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsProfileSection.vue +106 -0
  213. package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account/index.vue +17 -0
  214. package/skills/jskit/references/patterns/users/user-administration-server/PATTERN.md +64 -0
  215. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +42 -0
  216. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/server/UsersFeature.js +13 -0
  217. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/shared/index.js +1 -0
  218. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/shared/userResource.js +69 -0
  219. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +43 -0
  220. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +22 -0
  221. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/shared/index.js +1 -0
  222. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/shared/userResource.js +56 -0
  223. package/skills/jskit/references/patterns/workspaces/workspace-server/PATTERN.md +60 -0
  224. package/skills/jskit/references/patterns/workspaces/workspace-server/example/config/roles.js +27 -0
  225. package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/email/workspaceInviteEmail.js +23 -0
  226. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/PATTERN.md +65 -0
  227. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountPendingInvitesCue.vue +106 -0
  228. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountSettingsInvitesSection.vue +7 -0
  229. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/components/WorkspaceNotFoundCard.vue +42 -0
  230. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/composables/useWorkspaceNotFoundState.js +41 -0
  231. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/members/index.vue +7 -0
  232. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings/index.vue +15 -0
  233. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings.vue +81 -0
  234. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/invite/[token].vue +7 -0
  235. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/index.vue +58 -0
  236. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/root.vue +20 -0
  237. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/index.vue +31 -0
  238. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/root.vue +20 -0
  239. package/skills/jskit/references/ui-operations.md +7 -4
  240. package/templates/app/AGENTS.md +12 -8
@@ -0,0 +1,52 @@
1
+ <script setup>
2
+ const title = defineModel("title", { type: String, default: "" });
3
+ const author = defineModel("author", { type: String, default: "" });
4
+ const notes = defineModel("notes", { type: String, default: "" });
5
+
6
+ defineProps({
7
+ readonly: {
8
+ type: Boolean,
9
+ default: false
10
+ },
11
+ resolveFieldErrors: {
12
+ type: Function,
13
+ required: true
14
+ }
15
+ });
16
+ </script>
17
+
18
+ <template>
19
+ <v-col cols="12" md="6">
20
+ <v-text-field
21
+ v-model="title"
22
+ label="Title"
23
+ variant="outlined"
24
+ density="comfortable"
25
+ maxlength="255"
26
+ :readonly="readonly"
27
+ :error-messages="resolveFieldErrors('title')"
28
+ />
29
+ </v-col>
30
+ <v-col cols="12" md="6">
31
+ <v-text-field
32
+ v-model="author"
33
+ label="Author"
34
+ variant="outlined"
35
+ density="comfortable"
36
+ maxlength="255"
37
+ :readonly="readonly"
38
+ :error-messages="resolveFieldErrors('author')"
39
+ />
40
+ </v-col>
41
+ <v-col cols="12">
42
+ <v-textarea
43
+ v-model="notes"
44
+ label="Notes"
45
+ variant="outlined"
46
+ density="comfortable"
47
+ auto-grow
48
+ :readonly="readonly"
49
+ :error-messages="resolveFieldErrors('notes')"
50
+ />
51
+ </v-col>
52
+ </template>
@@ -0,0 +1,50 @@
1
+ <script setup>
2
+ import CrudListScreen from "@jskit-ai/http-web/client/components/CrudListScreen";
3
+ import { useCrudListScreen } from "@jskit-ai/http-web/client/composables/useCrudListScreen";
4
+ import { bookResource } from "@local/books/shared";
5
+ import { listBulkActions, listFilters, listRowActions } from "./listExtensions.js";
6
+
7
+ const screen = useCrudListScreen({
8
+ resource: bookResource,
9
+ resourceNamespace: "books",
10
+ apiSuffix: "/books",
11
+ recordIdParam: "bookId",
12
+ recordIdSelector: (book = {}) => book.id,
13
+ titleFallbackFieldKey: "title",
14
+ viewUrlTemplate: "/books/:bookId",
15
+ editUrlTemplate: "/books/:bookId/edit",
16
+ newUrlTemplate: "/books/new",
17
+ listFilters,
18
+ listBulkActions,
19
+ listRowActions,
20
+ requestRecoveryLabel: "Books",
21
+ fallbackLoadError: "Unable to load books."
22
+ });
23
+ </script>
24
+
25
+ <template>
26
+ <CrudListScreen
27
+ :screen="screen"
28
+ create-label="Add book"
29
+ empty-body="Add your first book to begin the catalogue."
30
+ >
31
+ <template #card-fields="{ record, formatListCardValue }">
32
+ <dl class="d-grid ga-2 mb-0">
33
+ <div>
34
+ <dt class="text-caption text-medium-emphasis">Author</dt>
35
+ <dd class="text-body-2 mb-0">{{ formatListCardValue(record.author) }}</dd>
36
+ </div>
37
+ </dl>
38
+ </template>
39
+
40
+ <template #table-header>
41
+ <th>Title</th>
42
+ <th>Author</th>
43
+ </template>
44
+
45
+ <template #table-row="{ record }">
46
+ <td>{{ record.title }}</td>
47
+ <td>{{ record.author }}</td>
48
+ </template>
49
+ </CrudListScreen>
50
+ </template>
@@ -0,0 +1,43 @@
1
+ <script setup>
2
+ import CrudAddEditScreen from "@jskit-ai/http-web/client/components/CrudAddEditScreen";
3
+ import { useCrudAddEditScreen } from "@jskit-ai/http-web/client/composables/useCrudAddEditScreen";
4
+ import { bookResource } from "@local/books/shared";
5
+ import BookFormFields from "./BookFormFields.vue";
6
+ import { bookFormFields } from "./formFields.js";
7
+
8
+ const screen = useCrudAddEditScreen({
9
+ mode: "new",
10
+ saveLabel: "Save book",
11
+ cancelTo: "/books",
12
+ resource: bookResource,
13
+ operationName: "create",
14
+ formFields: bookFormFields,
15
+ addEditOptions: {
16
+ apiSuffix: "/books",
17
+ placementSource: "crud.books.new",
18
+ readEnabled: false,
19
+ writeMethod: "POST",
20
+ recordIdParam: "bookId",
21
+ viewUrlTemplate: "/books/:bookId",
22
+ listUrlTemplate: "/books"
23
+ },
24
+ saveSuccess: {
25
+ invalidateQueryKey: ["crud", "books"],
26
+ listUrlTemplate: "/books"
27
+ }
28
+ });
29
+ </script>
30
+
31
+ <template>
32
+ <CrudAddEditScreen :screen="screen">
33
+ <template #fields="fieldRuntime">
34
+ <BookFormFields
35
+ v-model:title="fieldRuntime.formState.title"
36
+ v-model:author="fieldRuntime.formState.author"
37
+ v-model:notes="fieldRuntime.formState.notes"
38
+ :readonly="fieldRuntime.addEdit.isFieldLocked"
39
+ :resolve-field-errors="fieldRuntime.resolveFieldErrors"
40
+ />
41
+ </template>
42
+ </CrudAddEditScreen>
43
+ </template>
@@ -0,0 +1,49 @@
1
+ <script setup>
2
+ import CrudDeleteAction from "@jskit-ai/http-web/client/components/CrudDeleteAction";
3
+ import CrudViewScreen from "@jskit-ai/http-web/client/components/CrudViewScreen";
4
+ import { useCrudDeleteAction } from "@jskit-ai/http-web/client/composables/useCrudDeleteAction";
5
+ import { useCrudViewScreen } from "@jskit-ai/http-web/client/composables/useCrudViewScreen";
6
+ import { bookResource } from "@local/books/shared";
7
+
8
+ const screen = useCrudViewScreen({
9
+ resource: bookResource,
10
+ resourceNamespace: "books",
11
+ apiUrlTemplate: "/books/:bookId",
12
+ recordIdParam: "bookId",
13
+ titleFallbackFieldKey: "title",
14
+ listUrlTemplate: "/books",
15
+ editUrlTemplate: "/books/:bookId/edit",
16
+ requestRecoveryLabel: "Book",
17
+ fallbackLoadError: "Unable to load this book."
18
+ });
19
+
20
+ const deleteAction = useCrudDeleteAction({
21
+ screen,
22
+ resource: bookResource,
23
+ resourceNamespace: "books",
24
+ apiUrlTemplate: "/books/:bookId"
25
+ });
26
+ </script>
27
+
28
+ <template>
29
+ <CrudViewScreen
30
+ :screen="screen"
31
+ resource-singular-title="Book"
32
+ resource-plural-title="Books"
33
+ >
34
+ <template #actions>
35
+ <CrudDeleteAction :action="deleteAction" resource-singular-title="Book" />
36
+ </template>
37
+
38
+ <template #fields="{ view }">
39
+ <v-col cols="12" md="6">
40
+ <div class="text-caption text-medium-emphasis">Author</div>
41
+ <div class="text-body-1">{{ view.record?.author }}</div>
42
+ </v-col>
43
+ <v-col cols="12">
44
+ <div class="text-caption text-medium-emphasis">Notes</div>
45
+ <div class="text-body-1 text-pre-wrap">{{ view.record?.notes || "No notes" }}</div>
46
+ </v-col>
47
+ </template>
48
+ </CrudViewScreen>
49
+ </template>
@@ -0,0 +1,31 @@
1
+ const bookFormFields = Object.freeze([
2
+ Object.freeze({
3
+ key: "title",
4
+ label: "Title",
5
+ type: "string",
6
+ nullable: false,
7
+ inputType: "text",
8
+ component: "text",
9
+ maxLength: 255
10
+ }),
11
+ Object.freeze({
12
+ key: "author",
13
+ label: "Author",
14
+ type: "string",
15
+ nullable: false,
16
+ inputType: "text",
17
+ component: "text",
18
+ maxLength: 255
19
+ }),
20
+ Object.freeze({
21
+ key: "notes",
22
+ label: "Notes",
23
+ type: "string",
24
+ nullable: true,
25
+ inputType: "textarea",
26
+ component: "textarea",
27
+ maxLength: 65535
28
+ })
29
+ ]);
30
+
31
+ export { bookFormFields };
@@ -0,0 +1,9 @@
1
+ import { defineCrudListBulkActions } from "@jskit-ai/http-web/client/bulkActions";
2
+ import { defineCrudListFilters } from "@jskit-ai/http-web/client/filters";
3
+ import { defineCrudListRowActions } from "@jskit-ai/http-web/client/rowActions";
4
+
5
+ const listFilters = defineCrudListFilters({});
6
+ const listBulkActions = defineCrudListBulkActions([]);
7
+ const listRowActions = defineCrudListRowActions([]);
8
+
9
+ export { listBulkActions, listFilters, listRowActions };
@@ -0,0 +1,160 @@
1
+ ---
2
+ id: crud/json-api-resource-package
3
+ title: JSON API CRUD resource package
4
+ summary: Build a complete application-owned CRUD server package from a migration, resource contract, and one framework module declaration.
5
+ keywords: actions, crud, database, json-api, migration, permissions, provider, repository, routes, service
6
+ requires: @jskit-ai/crud-core, @jskit-ai/resource-crud-core
7
+ ---
8
+
9
+ # JSON API CRUD resource package
10
+
11
+ ## Use when
12
+
13
+ Use this pattern when a product entity needs conventional JSON API list, view,
14
+ create, patch, and delete behavior. JSKIT owns the repeated repository, service,
15
+ action, route, and provider mechanics. The application owns the migration,
16
+ resource contract, access surface, ownership policy, and package composition.
17
+
18
+ ## Do not use when
19
+
20
+ Do not use this pattern for a command-oriented workflow, aggregate, import job,
21
+ multi-resource transaction, or domain with materially different operations.
22
+ Use a feature package and explicit services for those cases.
23
+
24
+ ## Product decisions
25
+
26
+ Decide the entity fields, validation, search, sorting, access surface, ownership,
27
+ enabled operations, route name, permissions, and user-facing messages. Author
28
+ those decisions in normal migration and resource source. Do not derive them
29
+ from a generator questionnaire or treat a live table as the sole contract.
30
+
31
+ ## Invariants
32
+
33
+ - Migration and resource storage metadata describe the same table.
34
+ - `apiAccess`, surface access, and `autofilter` agree.
35
+ - Public routes use public ownership only.
36
+ - Workspace routes receive explicit scope validators and route-to-action input.
37
+ - Application source contains one module declaration, not copied generic CRUD
38
+ repository, service, action, route, and provider files.
39
+ - Current migration/resource/source state is authoritative; there is no receipt,
40
+ provenance marker, generator identity, or field-patching lane.
41
+
42
+ ## Framework APIs
43
+
44
+ Use `defineCrudResource()` from `@jskit-ai/resource-crud-core` and
45
+ `defineCrudJsonApiFeature()` from `@jskit-ai/crud-core`. The latter composes the
46
+ JSON REST resource, repository, service events, standard CRUD actions, route
47
+ contracts, access policy, and HTTP routes.
48
+
49
+ ## Example files
50
+
51
+ `example/` contains a complete books package plus a source-controlled migration.
52
+ `BooksFeature.js` is deliberately tiny: it binds the readable resource contract
53
+ to the framework module and exports the resulting provider.
54
+
55
+ ## Variation points
56
+
57
+ Change the package name, resource fields, table, surface, ownership filter,
58
+ relative route, messages, and migration. Supply explicit permissions when the
59
+ surface uses named policy. For workspace ownership, add the workspace route and
60
+ action validators through the module's `scope` option. The feature exposes the
61
+ operations declared by the resource. Use `operations` only to narrow that
62
+ surface further; do not register unused mutation actions and routes. Use
63
+ `routes: false` only for an internal
64
+ capability/action surface that deliberately has no HTTP API.
65
+
66
+ When otherwise-standard CRUD earns product behavior, name the exact
67
+ capabilities in `requires` and provide `decorateService({ service, repository,
68
+ resource, ...dependencies })`. Retain the standard methods, override only the
69
+ methods whose CRUD behavior changes, and add resource-related domain methods
70
+ such as `confirmBooking()` or `sendReminder()` directly to that service. Those
71
+ additional methods remain available through the resource capability. When the
72
+ product needs only a few additional queries or locking operations, provide
73
+ `decorateRepository({ repository, database, http, jsonRestApi, resource,
74
+ ...dependencies })`; return the standard methods plus only those unique
75
+ persistence operations instead of copying the CRUD repository.
76
+
77
+ Use `operationLifecycle` for behavior surrounding a standard operation. Its
78
+ per-operation `before`, `execute`, and `after` phases share one repository
79
+ transaction for create, update, and delete. An `execute` hook receives
80
+ `standard(nextInput)` so it can retain the framework write while adjusting its
81
+ input or coordinating other repository work. `afterCommit` runs only after the
82
+ mutation transaction succeeds. Put durable external delivery behind an outbox
83
+ written inside the transaction; do not make an email or webhook the arbiter of
84
+ whether an already-committed request appears successful.
85
+
86
+ Declare resource-related non-CRUD commands in `actions`. Each named action has
87
+ a normal JSKIT input, permission, audit, events, and `execute({ input, context,
88
+ service, repository, ...dependencies })` contract. Its optional `route`
89
+ declares an HTTP method, path below the resource base, explicit route contract,
90
+ status code, and optional request-to-action input mapper. This is the normal
91
+ home for `confirm`, `publish`, `cancel`, `reorder`, or `sendReminder`. Move to a
92
+ separate explicit feature only when the operation belongs to another domain or
93
+ the resource is no longer principally CRUD.
94
+
95
+ If a product-specific list filter needs additional transport validation and
96
+ JSON API search mapping, pass its validator as `listFilterQueryValidator` and
97
+ its storage mapping as `searchSchema`; keep that contract next to the feature.
98
+ Use `operationInputs.create` or `operationInputs.update` when a standard CRUD
99
+ operation intentionally accepts a small virtual product field that is handled
100
+ by the service decorator rather than stored on the resource row.
101
+
102
+ ```js
103
+ const BookingsFeature = defineCrudJsonApiFeature({
104
+ resource: bookingResource,
105
+ surface: "app",
106
+ decorateService({ service, repository }) {
107
+ return {
108
+ ...service,
109
+ async confirmBooking(recordId, options) {
110
+ return repository.confirmBooking(recordId, options);
111
+ }
112
+ };
113
+ },
114
+ operationLifecycle: {
115
+ update: {
116
+ async before({ input, service, trx, context }) {
117
+ await service.assertUpdateAllowed(input.recordId, { trx, context });
118
+ },
119
+ async afterCommit({ result, service, context }) {
120
+ await service.recordUpdateCompleted(result.data.id, { context });
121
+ }
122
+ }
123
+ },
124
+ actions: {
125
+ confirm: {
126
+ input: confirmBookingInput,
127
+ permission: { require: "all", permissions: ["bookings.confirm"] },
128
+ execute({ input, context, service }) {
129
+ return service.confirmBooking(input.recordId, { context });
130
+ },
131
+ route: {
132
+ method: "POST",
133
+ path: "/:recordId/confirm",
134
+ contract: confirmBookingRouteContract,
135
+ statusCode: 200
136
+ }
137
+ }
138
+ }
139
+ });
140
+ ```
141
+
142
+ ## Verification
143
+
144
+ - Rebuild the table from migrations in disposable MySQL and PostgreSQL databases
145
+ when both drivers are supported by the application.
146
+ - Validate representative accepted and rejected resource values.
147
+ - Exercise every enabled route and JSON API document shape.
148
+ - Prove positive owner access and negative cross-owner access.
149
+ - Verify public routes require neither identity nor CSRF only when explicitly
150
+ declared public.
151
+ - Run the package tests and application verifier.
152
+
153
+ ## Avoid
154
+
155
+ - copied generic repository/service/action/route/provider boilerplate
156
+ - generator subcommands for adding fields
157
+ - editing an applied baseline migration instead of adding a new migration
158
+ - public access combined with user or workspace ownership
159
+ - implicit workspace route parameters or permissions
160
+ - receipt, replay, provenance, ownership-marker, or scaffold-shape files
@@ -0,0 +1,22 @@
1
+ const TABLE_NAME = "books";
2
+
3
+ exports.up = async function up(knex) {
4
+ if (await knex.schema.hasTable(TABLE_NAME)) {
5
+ return;
6
+ }
7
+
8
+ await knex.schema.createTable(TABLE_NAME, (table) => {
9
+ table.bigIncrements("id").primary();
10
+ table.bigInteger("user_id").unsigned().notNullable().index()
11
+ .references("id").inTable("users").onDelete("CASCADE");
12
+ table.string("title", 255).notNullable();
13
+ table.string("author", 255).notNullable();
14
+ table.text("notes").nullable();
15
+ table.timestamp("created_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
16
+ table.timestamp("updated_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
17
+ });
18
+ };
19
+
20
+ exports.down = async function down(knex) {
21
+ await knex.schema.dropTableIfExists(TABLE_NAME);
22
+ };
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@app/books",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "exports": {
7
+ "./server/BooksFeature": "./src/server/BooksFeature.js",
8
+ "./shared": "./src/shared/index.js"
9
+ },
10
+ "dependencies": {
11
+ "@jskit-ai/crud-core": "0.1.190",
12
+ "@jskit-ai/resource-crud-core": "0.1.121"
13
+ },
14
+ "jskit": {
15
+ "kind": "runtime",
16
+ "capabilities": {
17
+ "provides": [
18
+ "app.books"
19
+ ],
20
+ "requires": [
21
+ "runtime.http",
22
+ "runtime.json-rest-api",
23
+ "runtime.actions",
24
+ "runtime.database"
25
+ ]
26
+ },
27
+ "runtime": {
28
+ "server": {
29
+ "providers": [
30
+ {
31
+ "entrypoint": "src/server/BooksFeature.js",
32
+ "export": "BooksFeature"
33
+ }
34
+ ]
35
+ },
36
+ "client": {
37
+ "providers": []
38
+ }
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,13 @@
1
+ import { defineCrudJsonApiFeature } from "@jskit-ai/crud-core/server/defineCrudJsonApiFeature";
2
+ import { bookResource } from "../shared/bookResource.js";
3
+
4
+ const BooksFeature = defineCrudJsonApiFeature({
5
+ resource: bookResource,
6
+ id: "app.books",
7
+ capability: "app.books",
8
+ surface: "app",
9
+ ownershipFilter: "user",
10
+ relativePath: "/books"
11
+ });
12
+
13
+ export { BooksFeature };
@@ -0,0 +1,83 @@
1
+ import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
2
+
3
+ const bookResource = defineCrudResource({
4
+ namespace: "books",
5
+ tableName: "books",
6
+ apiAccess: "authenticated",
7
+ autofilter: "user",
8
+ schema: {
9
+ userId: {
10
+ type: "id",
11
+ required: true,
12
+ hidden: true,
13
+ operations: {}
14
+ },
15
+ title: {
16
+ type: "string",
17
+ maxLength: 255,
18
+ required: true,
19
+ search: true,
20
+ operations: {
21
+ output: { required: true },
22
+ create: { required: true },
23
+ patch: { required: false }
24
+ }
25
+ },
26
+ author: {
27
+ type: "string",
28
+ maxLength: 255,
29
+ required: true,
30
+ search: true,
31
+ operations: {
32
+ output: { required: true },
33
+ create: { required: true },
34
+ patch: { required: false }
35
+ }
36
+ },
37
+ notes: {
38
+ type: "string",
39
+ maxLength: 65535,
40
+ nullable: true,
41
+ search: true,
42
+ operations: {
43
+ output: { required: true },
44
+ create: { required: false },
45
+ patch: { required: false }
46
+ }
47
+ },
48
+ createdAt: {
49
+ type: "dateTime",
50
+ temporalPrecision: 0,
51
+ default: "now()",
52
+ storage: { writeSerializer: "datetime-utc" },
53
+ operations: { output: { required: true } }
54
+ },
55
+ updatedAt: {
56
+ type: "dateTime",
57
+ temporalPrecision: 0,
58
+ default: "now()",
59
+ storage: { writeSerializer: "datetime-utc" },
60
+ operations: { output: { required: true } }
61
+ }
62
+ },
63
+ searchSchema: {
64
+ id: { type: "id", actualField: "id" },
65
+ q: {
66
+ type: "string",
67
+ oneOf: ["title", "author", "notes"],
68
+ filterOperator: "like",
69
+ splitBy: " ",
70
+ matchAll: true
71
+ }
72
+ },
73
+ defaultSort: ["-createdAt"],
74
+ messages: {
75
+ validation: "Fix invalid values and try again.",
76
+ saveSuccess: "Book saved.",
77
+ saveError: "Unable to save this book.",
78
+ deleteSuccess: "Book deleted.",
79
+ deleteError: "Unable to delete this book."
80
+ }
81
+ });
82
+
83
+ export { bookResource };
@@ -0,0 +1 @@
1
+ export { bookResource } from "./bookResource.js";
@@ -0,0 +1,102 @@
1
+ ---
2
+ id: crud/resource-contract
3
+ title: Owner-scoped CRUD resource contract
4
+ summary: Define an authenticated application resource whose records belong to the current user.
5
+ keywords: authenticated, crud, database, owner-scoped, resource, user
6
+ requires: @jskit-ai/resource-crud-core
7
+ ---
8
+
9
+ # Owner-scoped CRUD resource contract
10
+
11
+ ## Use when
12
+
13
+ Use this pattern when an authenticated application stores records that belong
14
+ to one user and standard list, view, create, patch, and delete behaviour fits
15
+ the product.
16
+
17
+ The example is intentionally a normal source file. Copy it directly when the
18
+ shape fits, adapt its fields and language for another entity, or read it as a
19
+ reference for `defineCrudResource()`.
20
+
21
+ ## Do not use when
22
+
23
+ Do not use this pattern for public records, workspace-owned records, inherited
24
+ ownership, composite identities, or a domain whose operations do not match
25
+ normal CRUD behaviour. Select a pattern matching the real ownership and
26
+ operation model instead of changing `autofilter` merely to make a test pass.
27
+
28
+ ## Product decisions
29
+
30
+ Know these decisions before adapting the example:
31
+
32
+ - whether records truly belong to the current user
33
+ - which fields are required, optional, nullable, searchable, or hidden
34
+ - whether a value is free text or a constrained vocabulary
35
+ - which operations the product actually exposes
36
+ - the user-facing success and failure language
37
+
38
+ Those decisions come from the product conversation or existing application,
39
+ not from a JSKIT questionnaire.
40
+
41
+ ## Invariants
42
+
43
+ - The persisted table has a normal single-column primary key.
44
+ - User ownership is represented by a non-null `user_id` column.
45
+ - The resource's hidden `userId` field maps to that ownership column.
46
+ - `autofilter: "user"` is used only for genuine current-user ownership.
47
+ - Output, create, and patch participation is explicit on each product field.
48
+ - Temporal database values use the declared storage serializer.
49
+ - The source-controlled migration and resource contract agree.
50
+ - Server policy tests prove that one user cannot read or mutate another user's
51
+ records.
52
+
53
+ ## Framework APIs
54
+
55
+ The example uses `defineCrudResource()` from
56
+ `@jskit-ai/resource-crud-core/shared/crudResource`. That API derives the normal
57
+ CRUD operation validators from one readable field contract.
58
+
59
+ Higher layers should consume this resource through JSKIT's CRUD service,
60
+ repository, route, and UI APIs. They should not rebuild field validators or
61
+ serialize requests independently.
62
+
63
+ ## Example files
64
+
65
+ - `example/bookResource.js` is a complete resource contract for personal book
66
+ records.
67
+
68
+ The example deliberately uses a concrete domain. Rename and edit normal source
69
+ rather than feeding it through placeholder interpolation.
70
+
71
+ ## Variation points
72
+
73
+ Safe adaptations include:
74
+
75
+ - namespace and table name
76
+ - product fields and validation
77
+ - searchable fields and default sort
78
+ - enabled CRUD operations
79
+ - user-facing messages
80
+ - lookup relationships supported by the resource APIs
81
+
82
+ Changing ownership, access, identity, or persistence architecture selects a
83
+ different pattern and requires corresponding policy and migration evidence.
84
+
85
+ ## Verification
86
+
87
+ - Import the resource and prove all intended operations exist.
88
+ - Validate representative accepted and rejected field values.
89
+ - Rebuild the table from source-controlled migrations in a disposable database.
90
+ - Run positive current-user CRUD tests.
91
+ - Run negative cross-user read and mutation tests.
92
+ - Run the application's focused tests and broad verifier at sign-off.
93
+
94
+ ## Avoid
95
+
96
+ - generator provenance or scaffold-shape metadata
97
+ - a field-by-field questionnaire
98
+ - a live table as the only source of schema truth
99
+ - app-specific request serializers that duplicate the resource contract
100
+ - hidden ownership aliases other than the framework's explicit ownership field
101
+ - durable receipts recording that this pattern was copied
102
+ - changing an installed baseline migration instead of adding a new migration