cvx-kit 0.1.8 → 0.1.10

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 (140) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +28 -28
  3. package/dist/agent-tools.d.mts +5 -5
  4. package/dist/agent-tools.mjs.map +1 -1
  5. package/dist/auth.d.mts +39 -289
  6. package/dist/auth.mjs.map +1 -1
  7. package/dist/components/approvals/_generated/api.d.mts +3 -4
  8. package/dist/components/approvals/_generated/api.mjs.map +1 -1
  9. package/dist/components/approvals/_generated/component.d.mts +1 -2
  10. package/dist/components/approvals/_generated/dataModel.d.mts +4 -5
  11. package/dist/components/approvals/_generated/server.d.mts +12 -13
  12. package/dist/components/approvals/_generated/server.mjs.map +1 -1
  13. package/dist/components/approvals/audit.d.mts +8 -8
  14. package/dist/components/approvals/audit.mjs +1 -1
  15. package/dist/components/approvals/audit.mjs.map +1 -1
  16. package/dist/components/approvals/client.d.mts +10 -10
  17. package/dist/components/approvals/client.mjs.map +1 -1
  18. package/dist/components/approvals/constants.d.mts +30 -30
  19. package/dist/components/approvals/constants.mjs.map +1 -1
  20. package/dist/components/approvals/convex.config.mjs.map +1 -1
  21. package/dist/components/approvals/decisions.d.mts +6 -6
  22. package/dist/components/approvals/decisions.mjs.map +1 -1
  23. package/dist/components/approvals/functions.d.mts +3 -3
  24. package/dist/components/approvals/functions.mjs.map +1 -1
  25. package/dist/components/approvals/health.d.mts +2 -2
  26. package/dist/components/approvals/health.mjs.map +1 -1
  27. package/dist/components/approvals/requests.d.mts +8 -8
  28. package/dist/components/approvals/requests.mjs.map +1 -1
  29. package/dist/components/approvals/validators.d.mts +29 -29
  30. package/dist/components/approvals/validators.mjs +1 -1
  31. package/dist/components/approvals/validators.mjs.map +1 -1
  32. package/dist/components/approvals/workflow.d.mts +3 -3
  33. package/dist/components/approvals/workflow.mjs.map +1 -1
  34. package/dist/components/approvals/workflow_steps.d.mts +15 -15
  35. package/dist/components/approvals/workflow_steps.mjs.map +1 -1
  36. package/dist/components/foundation/_generated/api.d.mts +3 -4
  37. package/dist/components/foundation/_generated/api.mjs.map +1 -1
  38. package/dist/components/foundation/_generated/component.d.mts +1 -2
  39. package/dist/components/foundation/_generated/dataModel.d.mts +4 -5
  40. package/dist/components/foundation/_generated/server.d.mts +12 -13
  41. package/dist/components/foundation/_generated/server.mjs.map +1 -1
  42. package/dist/components/foundation/client.d.mts +13 -13
  43. package/dist/components/foundation/client.mjs.map +1 -1
  44. package/dist/components/foundation/convex.config.mjs.map +1 -1
  45. package/dist/components/foundation/functions.d.mts +2 -2
  46. package/dist/components/foundation/functions.mjs.map +1 -1
  47. package/dist/components/foundation/modules/command/command.d.mts +14 -14
  48. package/dist/components/foundation/modules/command/command.mjs.map +1 -1
  49. package/dist/components/foundation/modules/command/lifecycle.d.mts +12 -13
  50. package/dist/components/foundation/modules/command/lifecycle.mjs.map +1 -1
  51. package/dist/components/foundation/modules/observability/observability.d.mts +4 -4
  52. package/dist/components/foundation/modules/observability/observability.mjs.map +1 -1
  53. package/dist/components/foundation/modules/query/query.d.mts +5 -5
  54. package/dist/components/foundation/modules/query/query.mjs.map +1 -1
  55. package/dist/components/foundation/operation.d.mts +5 -6
  56. package/dist/components/foundation/operation.mjs.map +1 -1
  57. package/dist/components/foundation/result.d.mts +6 -6
  58. package/dist/components/foundation/result.mjs.map +1 -1
  59. package/dist/components/foundation/telemetry.d.mts +2 -2
  60. package/dist/components/foundation/telemetry.mjs.map +1 -1
  61. package/dist/crud.d.mts +6 -5
  62. package/dist/crud.mjs.map +1 -1
  63. package/dist/errors.d.mts +4 -4
  64. package/dist/errors.mjs.map +1 -1
  65. package/dist/index.d.mts +2 -2
  66. package/dist/middleware.d.mts +2 -3
  67. package/dist/middleware.mjs.map +1 -1
  68. package/dist/modules/contracts/contract.d.mts +12 -13
  69. package/dist/modules/contracts/contract.mjs.map +1 -1
  70. package/dist/modules/contracts/convex.d.mts +3 -5
  71. package/dist/modules/contracts/convex.mjs.map +1 -1
  72. package/dist/modules/contracts/errors.d.mts +8 -9
  73. package/dist/modules/contracts/errors.mjs +1 -1
  74. package/dist/modules/contracts/errors.mjs.map +1 -1
  75. package/dist/modules/contracts/exposure.d.mts +13 -14
  76. package/dist/modules/contracts/exposure.mjs +1 -1
  77. package/dist/modules/contracts/exposure.mjs.map +1 -1
  78. package/dist/modules/contracts/idempotency.d.mts +15 -16
  79. package/dist/modules/contracts/idempotency.mjs +1 -1
  80. package/dist/modules/contracts/idempotency.mjs.map +1 -1
  81. package/dist/modules/contracts/standard.d.mts +2 -3
  82. package/dist/modules/contracts/standard.mjs.map +1 -1
  83. package/dist/modules/contracts/workflow.d.mts +26 -27
  84. package/dist/modules/contracts/workflow.mjs.map +1 -1
  85. package/dist/modules/effect/api-runtime.d.mts +10 -11
  86. package/dist/modules/effect/api-runtime.mjs.map +1 -1
  87. package/dist/modules/effect/api-zod.d.mts +2 -3
  88. package/dist/modules/effect/api-zod.mjs.map +1 -1
  89. package/dist/modules/effect/api.d.mts +4 -5
  90. package/dist/modules/effect/api.mjs.map +1 -1
  91. package/dist/modules/effect/command.d.mts +4 -5
  92. package/dist/modules/effect/command.mjs.map +1 -1
  93. package/dist/modules/effect/crud.d.mts +7 -8
  94. package/dist/modules/effect/crud.mjs.map +1 -1
  95. package/dist/modules/effect/errors.d.mts +1 -2
  96. package/dist/modules/effect/errors.mjs.map +1 -1
  97. package/dist/modules/effect/foundation.d.mts +3 -4
  98. package/dist/modules/effect/foundation.mjs.map +1 -1
  99. package/dist/modules/effect/idempotency.d.mts +2 -3
  100. package/dist/modules/effect/idempotency.mjs.map +1 -1
  101. package/dist/modules/effect/normalize.d.mts +2 -3
  102. package/dist/modules/effect/normalize.mjs.map +1 -1
  103. package/dist/modules/effect/operation.d.mts +17 -18
  104. package/dist/modules/effect/operation.mjs.map +1 -1
  105. package/dist/modules/effect/query.d.mts +3 -4
  106. package/dist/modules/effect/query.mjs.map +1 -1
  107. package/dist/modules/effect/schema.d.mts +8 -9
  108. package/dist/modules/effect/schema.mjs.map +1 -1
  109. package/dist/modules/effect/workflow.d.mts +5 -6
  110. package/dist/modules/effect/workflow.mjs.map +1 -1
  111. package/dist/oxlint/architecture.d.mts +6 -7
  112. package/dist/oxlint/architecture.mjs +1 -1
  113. package/dist/oxlint/architecture.mjs.map +1 -1
  114. package/dist/oxlint/ast.d.mts +22 -23
  115. package/dist/oxlint/ast.mjs +1 -1
  116. package/dist/oxlint/ast.mjs.map +1 -1
  117. package/dist/oxlint/ownership.d.mts +2 -3
  118. package/dist/oxlint/ownership.mjs +1 -1
  119. package/dist/oxlint/ownership.mjs.map +1 -1
  120. package/dist/oxlint.d.mts +4 -4
  121. package/dist/oxlint.mjs +1 -1
  122. package/dist/oxlint.mjs.map +1 -1
  123. package/dist/state-machine.d.mts +1 -2
  124. package/dist/state-machine.mjs.map +1 -1
  125. package/dist/tenancy.d.mts +6 -6
  126. package/dist/tenancy.mjs.map +1 -1
  127. package/dist/triggers.d.mts +6 -6
  128. package/dist/triggers.mjs.map +1 -1
  129. package/dist/webhooks.d.mts +5 -6
  130. package/dist/webhooks.mjs.map +1 -1
  131. package/dist/zod-table.d.mts +10 -10
  132. package/dist/zod-table.mjs.map +1 -1
  133. package/docs/oxlint.md +1 -1
  134. package/docs/upgrading.md +9 -1
  135. package/docs/zod-table.md +1 -1
  136. package/package.json +21 -15
  137. package/scripts/test-packed-auth.mjs +68 -0
  138. package/scripts/test-packed-effect.mjs +1 -1
  139. package/scripts/test-packed-ids.mjs +2 -2
  140. package/dist/node_modules/@standard-schema/spec/dist/index.d.mts +0 -79
@@ -1 +1 @@
1
- {"version":3,"file":"tenancy.mjs","names":[],"sources":["../src/tenancy.ts"],"sourcesContent":["import {\n\twrapDatabaseReader,\n\twrapDatabaseWriter,\n\ttype RLSConfig,\n\ttype Rules,\n} from 'convex-helpers/server/rowLevelSecurity'\nimport type { GenericDataModel, TableNamesInDataModel } from 'convex/server'\nimport { defaultErrors, type ErrorFactory } from './errors'\n\nexport { wrapDatabaseReader, wrapDatabaseWriter }\nexport type { RLSConfig, Rules }\n\n/** The server-owned tenancy field stamped on every tenant table row. */\nexport const TENANT_FIELD = 'tenant' as const\n\n/**\n * Deny-oriented row-level security rules generated mechanically from a table\n * registry: read/insert/modify pass only when the row's tenant matches.\n * Registering a table here is the entire onboarding — there is no second\n * per-table step to forget.\n */\nexport function createTenantRules<DataModel extends GenericDataModel>(\n\ttenant: string,\n\ttables: readonly TableNamesInDataModel<DataModel>[],\n): Rules<unknown, DataModel> {\n\tconst rules: Rules<unknown, DataModel> = {}\n\tfor (const table of tables) {\n\t\trules[table] = {\n\t\t\tread: async (_context, document) => document[TENANT_FIELD] === tenant,\n\t\t\tinsert: async (_context, document) => document[TENANT_FIELD] === tenant,\n\t\t\tmodify: async (_context, document) => document[TENANT_FIELD] === tenant,\n\t\t}\n\t}\n\treturn rules\n}\n\n/**\n * AND-composes rule sets: a row passes an operation only when every rule set\n * that defines a rule for it passes. Lets tenant isolation and role-level\n * rules coexist on the same wrapped database.\n */\nexport function composeRules<Ctx, DataModel extends GenericDataModel>(\n\t...sets: readonly Rules<Ctx, DataModel>[]\n): Rules<Ctx, DataModel> {\n\t// SAFETY: Rules keys are table names from DataModel; values come only from the supplied rule sets.\n\tconst tables = new Set(sets.flatMap((set) => Object.keys(set))) as Set<\n\t\tTableNamesInDataModel<DataModel>\n\t>\n\tconst composed: Rules<Ctx, DataModel> = {}\n\tfor (const table of tables) {\n\t\tconst reads = sets.flatMap((set) => set[table]?.read ?? [])\n\t\tconst inserts = sets.flatMap((set) => set[table]?.insert ?? [])\n\t\tconst modifies = sets.flatMap((set) => set[table]?.modify ?? [])\n\t\tconst rules: NonNullable<Rules<Ctx, DataModel>[typeof table]> = {}\n\t\tif (reads.length)\n\t\t\trules.read = async (context, document) => {\n\t\t\t\tfor (const rule of reads) if (!(await rule(context, document))) return false\n\t\t\t\treturn true\n\t\t\t}\n\t\tif (inserts.length)\n\t\t\trules.insert = async (context, document) => {\n\t\t\t\tfor (const rule of inserts) if (!(await rule(context, document))) return false\n\t\t\t\treturn true\n\t\t\t}\n\t\tif (modifies.length)\n\t\t\trules.modify = async (context, document) => {\n\t\t\t\tfor (const rule of modifies) if (!(await rule(context, document))) return false\n\t\t\t\treturn true\n\t\t\t}\n\t\tcomposed[table] = rules\n\t}\n\treturn composed\n}\n\n/**\n * Loads a client-referenced document and verifies tenant ownership without\n * disclosing existence: a missing row and a foreign row fail identically\n * with REFERENCE_NOT_FOUND. Use for every id argument a client supplies.\n */\nexport async function requireTenantReference<Document extends { [TENANT_FIELD]?: unknown }>(\n\ttenant: string,\n\tload: () => Promise<Document | null>,\n\terrors: ErrorFactory = defaultErrors,\n): Promise<Document> {\n\tconst document = await load()\n\tif (!document || document[TENANT_FIELD] !== tenant) {\n\t\treturn errors.throw({\n\t\t\tcode: 'REFERENCE_NOT_FOUND',\n\t\t\tmessage: 'The referenced row is unavailable',\n\t\t})\n\t}\n\treturn document\n}\n\n/** Asserts a loaded document belongs to the tenant; use on internal paths. */\nexport function assertTenantOwned<Document extends { [TENANT_FIELD]?: unknown }>(\n\ttenant: string,\n\tdocument: Document,\n\terrors: ErrorFactory = defaultErrors,\n): Document {\n\tif (document[TENANT_FIELD] !== tenant) {\n\t\treturn errors.throw({\n\t\t\tcode: 'CROSS_TENANT_REFERENCE',\n\t\t\tmessage: 'The document belongs to another tenant',\n\t\t})\n\t}\n\treturn document\n}\n"],"mappings":"iJAaA,MAAa,EAAe,SAQ5B,SAAgB,EACf,EACA,EAC4B,CAC5B,IAAM,EAAmC,CAAC,EAC1C,IAAK,IAAM,KAAS,EACnB,EAAM,GAAS,CACd,KAAM,MAAO,EAAU,IAAa,EAAS,KAAkB,EAC/D,OAAQ,MAAO,EAAU,IAAa,EAAS,KAAkB,EACjE,OAAQ,MAAO,EAAU,IAAa,EAAS,KAAkB,CAClE,EAED,OAAO,CACR,CAOA,SAAgB,EACf,GAAG,EACqB,CAExB,IAAM,EAAS,IAAI,IAAI,EAAK,QAAS,GAAQ,OAAO,KAAK,CAAG,CAAC,CAAC,EAGxD,EAAkC,CAAC,EACzC,IAAK,IAAM,KAAS,EAAQ,CAC3B,IAAM,EAAQ,EAAK,QAAS,GAAQ,EAAI,EAAM,EAAE,MAAQ,CAAC,CAAC,EACpD,EAAU,EAAK,QAAS,GAAQ,EAAI,EAAM,EAAE,QAAU,CAAC,CAAC,EACxD,EAAW,EAAK,QAAS,GAAQ,EAAI,EAAM,EAAE,QAAU,CAAC,CAAC,EACzD,EAA0D,CAAC,EAC7D,EAAM,SACT,EAAM,KAAO,MAAO,EAAS,IAAa,CACzC,IAAK,IAAM,KAAQ,EAAO,GAAI,CAAE,MAAM,EAAK,EAAS,CAAQ,EAAI,MAAO,GACvE,MAAO,EACR,GACG,EAAQ,SACX,EAAM,OAAS,MAAO,EAAS,IAAa,CAC3C,IAAK,IAAM,KAAQ,EAAS,GAAI,CAAE,MAAM,EAAK,EAAS,CAAQ,EAAI,MAAO,GACzE,MAAO,EACR,GACG,EAAS,SACZ,EAAM,OAAS,MAAO,EAAS,IAAa,CAC3C,IAAK,IAAM,KAAQ,EAAU,GAAI,CAAE,MAAM,EAAK,EAAS,CAAQ,EAAI,MAAO,GAC1E,MAAO,EACR,GACD,EAAS,GAAS,CACnB,CACA,OAAO,CACR,CAOA,eAAsB,EACrB,EACA,EACA,EAAuB,EACH,CACpB,IAAM,EAAW,MAAM,EAAK,EAO5B,MANI,CAAC,GAAY,EAAA,SAA2B,EACpC,EAAO,MAAM,CACnB,KAAM,sBACN,QAAS,mCACV,CAAC,EAEK,CACR,CAGA,SAAgB,EACf,EACA,EACA,EAAuB,EACZ,CAOX,OANI,EAAA,SAA2B,EAMxB,EALC,EAAO,MAAM,CACnB,KAAM,yBACN,QAAS,wCACV,CAAC,CAGH"}
1
+ {"version":3,"file":"tenancy.mjs","names":["TENANT_FIELD","createTenantRules","rules","table","tables","document","tenant","composeRules","sets","set","composed","reads","inserts","modifies","rule","context","requireTenantReference","defaultErrors","load","\"tenant\"","errors","assertTenantOwned","wrapDatabaseReader","wrapDatabaseWriter"],"sources":["../src/tenancy.ts"],"sourcesContent":["import {\n\twrapDatabaseReader,\n\twrapDatabaseWriter,\n\ttype RLSConfig,\n\ttype Rules,\n} from 'convex-helpers/server/rowLevelSecurity'\nimport type { GenericDataModel, TableNamesInDataModel } from 'convex/server'\nimport { defaultErrors, type ErrorFactory } from './errors'\n\nexport { wrapDatabaseReader, wrapDatabaseWriter }\nexport type { RLSConfig, Rules }\n\n/** The server-owned tenancy field stamped on every tenant table row. */\nexport const TENANT_FIELD = 'tenant' as const\n\n/**\n * Deny-oriented row-level security rules generated mechanically from a table\n * registry: read/insert/modify pass only when the row's tenant matches.\n * Registering a table here is the entire onboarding — there is no second\n * per-table step to forget.\n */\nexport function createTenantRules<DataModel extends GenericDataModel>(\n\ttenant: string,\n\ttables: readonly TableNamesInDataModel<DataModel>[],\n): Rules<unknown, DataModel> {\n\tconst rules: Rules<unknown, DataModel> = {}\n\tfor (const table of tables) {\n\t\trules[table] = {\n\t\t\tread: async (_context, document) => document[TENANT_FIELD] === tenant,\n\t\t\tinsert: async (_context, document) => document[TENANT_FIELD] === tenant,\n\t\t\tmodify: async (_context, document) => document[TENANT_FIELD] === tenant,\n\t\t}\n\t}\n\treturn rules\n}\n\n/**\n * AND-composes rule sets: a row passes an operation only when every rule set\n * that defines a rule for it passes. Lets tenant isolation and role-level\n * rules coexist on the same wrapped database.\n */\nexport function composeRules<Ctx, DataModel extends GenericDataModel>(\n\t...sets: readonly Rules<Ctx, DataModel>[]\n): Rules<Ctx, DataModel> {\n\t// SAFETY: Rules keys are table names from DataModel; values come only from the supplied rule sets.\n\tconst tables = new Set(sets.flatMap((set) => Object.keys(set))) as Set<\n\t\tTableNamesInDataModel<DataModel>\n\t>\n\tconst composed: Rules<Ctx, DataModel> = {}\n\tfor (const table of tables) {\n\t\tconst reads = sets.flatMap((set) => set[table]?.read ?? [])\n\t\tconst inserts = sets.flatMap((set) => set[table]?.insert ?? [])\n\t\tconst modifies = sets.flatMap((set) => set[table]?.modify ?? [])\n\t\tconst rules: NonNullable<Rules<Ctx, DataModel>[typeof table]> = {}\n\t\tif (reads.length)\n\t\t\trules.read = async (context, document) => {\n\t\t\t\tfor (const rule of reads) if (!(await rule(context, document))) return false\n\t\t\t\treturn true\n\t\t\t}\n\t\tif (inserts.length)\n\t\t\trules.insert = async (context, document) => {\n\t\t\t\tfor (const rule of inserts) if (!(await rule(context, document))) return false\n\t\t\t\treturn true\n\t\t\t}\n\t\tif (modifies.length)\n\t\t\trules.modify = async (context, document) => {\n\t\t\t\tfor (const rule of modifies) if (!(await rule(context, document))) return false\n\t\t\t\treturn true\n\t\t\t}\n\t\tcomposed[table] = rules\n\t}\n\treturn composed\n}\n\n/**\n * Loads a client-referenced document and verifies tenant ownership without\n * disclosing existence: a missing row and a foreign row fail identically\n * with REFERENCE_NOT_FOUND. Use for every id argument a client supplies.\n */\nexport async function requireTenantReference<Document extends { [TENANT_FIELD]?: unknown }>(\n\ttenant: string,\n\tload: () => Promise<Document | null>,\n\terrors: ErrorFactory = defaultErrors,\n): Promise<Document> {\n\tconst document = await load()\n\tif (!document || document[TENANT_FIELD] !== tenant) {\n\t\treturn errors.throw({\n\t\t\tcode: 'REFERENCE_NOT_FOUND',\n\t\t\tmessage: 'The referenced row is unavailable',\n\t\t})\n\t}\n\treturn document\n}\n\n/** Asserts a loaded document belongs to the tenant; use on internal paths. */\nexport function assertTenantOwned<Document extends { [TENANT_FIELD]?: unknown }>(\n\ttenant: string,\n\tdocument: Document,\n\terrors: ErrorFactory = defaultErrors,\n): Document {\n\tif (document[TENANT_FIELD] !== tenant) {\n\t\treturn errors.throw({\n\t\t\tcode: 'CROSS_TENANT_REFERENCE',\n\t\t\tmessage: 'The document belongs to another tenant',\n\t\t})\n\t}\n\treturn document\n}\n"],"mappings":"iJAaA,MAAaA,EAAe,SAQ5B,SAAgBC,EACf,EACA,EAC4B,CAC5B,IAAMC,EAAmC,CAAC,EAC1C,IAAK,IAAMC,KAASC,EACnB,EAAMD,GAAS,CACd,KAAM,MAAO,EAAU,IAAaE,EAASL,KAAkBM,EAC/D,OAAQ,MAAO,EAAU,IAAaD,EAASL,KAAkBM,EACjE,OAAQ,MAAO,EAAU,IAAaD,EAASL,KAAkBM,CAClE,EAED,OAAOJ,CACR,CAOA,SAAgBK,EACf,GAAGC,EACqB,CAExB,IAAMJ,EAAS,IAAI,IAAII,EAAK,QAAS,GAAQ,OAAO,KAAKC,CAAG,CAAC,CAAC,EAGxDC,EAAkC,CAAC,EACzC,IAAK,IAAMP,KAASC,EAAQ,CAC3B,IAAMO,EAAQH,EAAK,QAAS,GAAQC,EAAIN,EAAM,EAAE,MAAQ,CAAC,CAAC,EACpDS,EAAUJ,EAAK,QAAS,GAAQC,EAAIN,EAAM,EAAE,QAAU,CAAC,CAAC,EACxDU,EAAWL,EAAK,QAAS,GAAQC,EAAIN,EAAM,EAAE,QAAU,CAAC,CAAC,EACzDD,EAA0D,CAAC,EAC7DS,EAAM,SACT,EAAM,KAAO,MAAO,EAAS,IAAa,CACzC,IAAK,IAAMG,KAAQH,EAAO,GAAI,CAAE,MAAMG,EAAKC,EAASV,CAAQ,EAAI,MAAO,GACvE,MAAO,EACR,GACGO,EAAQ,SACX,EAAM,OAAS,MAAO,EAAS,IAAa,CAC3C,IAAK,IAAME,KAAQF,EAAS,GAAI,CAAE,MAAME,EAAKC,EAASV,CAAQ,EAAI,MAAO,GACzE,MAAO,EACR,GACGQ,EAAS,SACZ,EAAM,OAAS,MAAO,EAAS,IAAa,CAC3C,IAAK,IAAMC,KAAQD,EAAU,GAAI,CAAE,MAAMC,EAAKC,EAASV,CAAQ,EAAI,MAAO,GAC1E,MAAO,EACR,GACD,EAASF,GAASD,CACnB,CACA,OAAOQ,CACR,CAOA,eAAsBM,EACrB,EACA,EACA,EAAuBC,EACH,CACpB,IAAMZ,EAAW,MAAMa,EAAK,EAO5B,MANI,CAACb,GAAYA,EAAAc,SAA2Bb,EACpCc,EAAO,MAAM,CACnB,KAAM,sBACN,QAAS,mCACV,CAAC,EAEKf,CACR,CAGA,SAAgBgB,EACf,EACA,EACA,EAAuBJ,EACZ,CAOX,OANIZ,EAAAc,SAA2Bb,EAMxBD,EALCe,EAAO,MAAM,CACnB,KAAM,yBACN,QAAS,wCACV,CAAC,CAGH"}
@@ -7,14 +7,14 @@ import { Change, Trigger, Triggers } from "convex-helpers/server/triggers";
7
7
  * systemMutation runs writes through it — trigger enforcement becomes
8
8
  * structural, not a convention.
9
9
  */
10
- declare function createTriggers<DataModel extends GenericDataModel>(): Triggers<DataModel>;
10
+ export declare function createTriggers<DataModel extends GenericDataModel>(): Triggers<DataModel>;
11
11
  /**
12
12
  * Marks tables as append-only evidence tables: inserts pass, every update or
13
13
  * delete throws. The ontology pattern for votes/history/audit-style tables.
14
14
  */
15
- declare function appendOnly<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
15
+ export declare function appendOnly<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
16
16
  /** Forbids hard deletes on the given tables (soft-delete discipline). */
17
- declare function noDelete<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
17
+ export declare function noDelete<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
18
18
  /**
19
19
  * Post-image tenant invariant for tenantTable rows. Row-level security
20
20
  * authorizes a modification against the PRE-image, so ownership must also be
@@ -22,14 +22,14 @@ declare function noDelete<DataModel extends GenericDataModel>(triggers: Triggers
22
22
  * may never reassign it. Register for every tenant table (same registry that
23
23
  * drives createTenantRules).
24
24
  */
25
- declare function tenantOwnership<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
25
+ export declare function tenantOwnership<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
26
26
  /**
27
27
  * Maintains zod-table's opinionated lifecycle timestamps: createdAt and
28
28
  * updatedAt on insert, updatedAt on every update. archivedAt stays under
29
29
  * application control. Register once per table; writes made through the
30
30
  * auth constructors keep these fields correct automatically.
31
31
  */
32
- declare function timestamps<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
32
+ export declare function timestamps<DataModel extends GenericDataModel>(triggers: Triggers<DataModel>, ...tables: readonly TableNamesInDataModel<DataModel>[]): void;
33
33
  //#endregion
34
- export { type Change, type Trigger, Triggers, appendOnly, createTriggers, noDelete, tenantOwnership, timestamps };
34
+ export { type Change, type Trigger, Triggers };
35
35
  //# sourceMappingURL=triggers.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"triggers.mjs","names":[],"sources":["../src/triggers.ts"],"sourcesContent":["import { z } from 'zod'\nimport { Triggers, type Change, type Trigger } from 'convex-helpers/server/triggers'\nimport type { GenericDataModel, TableNamesInDataModel } from 'convex/server'\n\nexport { Triggers }\nexport type { Change, Trigger }\n\n/**\n * One application-wide mutation interceptor registry. Pass it to\n * createAuthFunctions({ triggers }) so every authMutation/roleMutation/\n * systemMutation runs writes through it — trigger enforcement becomes\n * structural, not a convention.\n */\nexport function createTriggers<DataModel extends GenericDataModel>(): Triggers<DataModel> {\n\treturn new Triggers<DataModel>()\n}\n\n/**\n * Marks tables as append-only evidence tables: inserts pass, every update or\n * delete throws. The ontology pattern for votes/history/audit-style tables.\n */\nexport function appendOnly<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (_ctx, change) => {\n\t\t\tif (change.operation !== 'insert') {\n\t\t\t\tthrow new Error(`${table} is append-only`)\n\t\t\t}\n\t\t})\n\t}\n}\n\n/** Forbids hard deletes on the given tables (soft-delete discipline). */\nexport function noDelete<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (_ctx, change) => {\n\t\t\tif (change.operation === 'delete') {\n\t\t\t\tthrow new Error(`${table} rows cannot be deleted`)\n\t\t\t}\n\t\t})\n\t}\n}\n\n/**\n * Post-image tenant invariant for tenantTable rows. Row-level security\n * authorizes a modification against the PRE-image, so ownership must also be\n * enforced against the post-image: inserts must carry a tenant, and updates\n * may never reassign it. Register for every tenant table (same registry that\n * drives createTenantRules).\n */\nexport function tenantOwnership<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (_ctx, change) => {\n\t\t\tif (change.operation === 'insert') {\n\t\t\t\tconst ownership = tenantDocument.safeParse(change.newDoc)\n\t\t\t\tif (!ownership.success) {\n\t\t\t\t\tthrow new Error(`${table} rows require a tenant`)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (change.operation === 'update') {\n\t\t\t\tconst previous = change.oldDoc\n\t\t\t\tconst current = change.newDoc\n\t\t\t\tif (previous.tenant !== current.tenant) {\n\t\t\t\t\tthrow new Error(`${table} tenant ownership cannot be reassigned`)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n/**\n * Maintains zod-table's opinionated lifecycle timestamps: createdAt and\n * updatedAt on insert, updatedAt on every update. archivedAt stays under\n * application control. Register once per table; writes made through the\n * auth constructors keep these fields correct automatically.\n */\nexport function timestamps<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (ctx, change) => {\n\t\t\tif (change.operation === 'delete') return\n\t\t\tconst now = Date.now()\n\t\t\tif (change.operation === 'insert') {\n\t\t\t\tconst document = change.newDoc\n\t\t\t\tif (document.createdAt === undefined) {\n\t\t\t\t\t// SAFETY: registration requires zod-table lifecycle fields; Convex validates this numeric timestamp patch.\n\t\t\t\t\tawait ctx.innerDb.patch(change.id, {\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t} as never)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = change.oldDoc\n\t\t\tconst current = change.newDoc\n\t\t\t// A write that already moved updatedAt (including our own patch)\n\t\t\t// is left alone — this is the recursion guard.\n\t\t\tif (current.updatedAt === previous.updatedAt) {\n\t\t\t\t// SAFETY: registration requires zod-table lifecycle fields; Convex validates this numeric timestamp patch.\n\t\t\t\tawait ctx.innerDb.patch(change.id, { updatedAt: now } as never)\n\t\t\t}\n\t\t})\n\t}\n}\n\nconst tenantDocument = z.object({ tenant: z.string().min(1) })\n"],"mappings":"kFAaA,SAAgB,GAA0E,CACzF,OAAO,IAAI,CACZ,CAMA,SAAgB,EACf,EACA,GAAG,EACF,CACD,IAAK,IAAM,KAAS,EACnB,EAAS,SAAS,EAAO,MAAO,EAAM,IAAW,CAChD,GAAI,EAAO,YAAc,SACxB,MAAU,MAAM,GAAG,EAAM,gBAAgB,CAE3C,CAAC,CAEH,CAGA,SAAgB,EACf,EACA,GAAG,EACF,CACD,IAAK,IAAM,KAAS,EACnB,EAAS,SAAS,EAAO,MAAO,EAAM,IAAW,CAChD,GAAI,EAAO,YAAc,SACxB,MAAU,MAAM,GAAG,EAAM,wBAAwB,CAEnD,CAAC,CAEH,CASA,SAAgB,EACf,EACA,GAAG,EACF,CACD,IAAK,IAAM,KAAS,EACnB,EAAS,SAAS,EAAO,MAAO,EAAM,IAAW,CAChD,GAAI,EAAO,YAAc,SAAU,CAElC,GAAI,CADc,EAAe,UAAU,EAAO,MACrC,CAAC,CAAC,QACd,MAAU,MAAM,GAAG,EAAM,uBAAuB,EAEjD,MACD,CACA,GAAI,EAAO,YAAc,SAAU,CAClC,IAAM,EAAW,EAAO,OAClB,EAAU,EAAO,OACvB,GAAI,EAAS,SAAW,EAAQ,OAC/B,MAAU,MAAM,GAAG,EAAM,uCAAuC,CAElE,CACD,CAAC,CAEH,CAQA,SAAgB,EACf,EACA,GAAG,EACF,CACD,IAAK,IAAM,KAAS,EACnB,EAAS,SAAS,EAAO,MAAO,EAAK,IAAW,CAC/C,GAAI,EAAO,YAAc,SAAU,OACnC,IAAM,EAAM,KAAK,IAAI,EACrB,GAAI,EAAO,YAAc,SAAU,CACjB,EAAO,OACX,YAAc,IAAA,IAE1B,MAAM,EAAI,QAAQ,MAAM,EAAO,GAAI,CAClC,UAAW,EACX,UAAW,CACZ,CAAU,EAEX,MACD,CACA,IAAM,EAAW,EAAO,OACR,EAAO,OAGX,YAAc,EAAS,WAElC,MAAM,EAAI,QAAQ,MAAM,EAAO,GAAI,CAAE,UAAW,CAAI,CAAU,CAEhE,CAAC,CAEH,CAEA,MAAM,EAAiB,EAAE,OAAO,CAAE,OAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAE,CAAC"}
1
+ {"version":3,"file":"triggers.mjs","names":["createTriggers","Triggers","appendOnly","tables","table","triggers","change","noDelete","tenantOwnership","tenantDocument","previous","current","timestamps","now","ctx","z"],"sources":["../src/triggers.ts"],"sourcesContent":["import { z } from 'zod'\nimport { Triggers, type Change, type Trigger } from 'convex-helpers/server/triggers'\nimport type { GenericDataModel, TableNamesInDataModel } from 'convex/server'\n\nexport { Triggers }\nexport type { Change, Trigger }\n\n/**\n * One application-wide mutation interceptor registry. Pass it to\n * createAuthFunctions({ triggers }) so every authMutation/roleMutation/\n * systemMutation runs writes through it — trigger enforcement becomes\n * structural, not a convention.\n */\nexport function createTriggers<DataModel extends GenericDataModel>(): Triggers<DataModel> {\n\treturn new Triggers<DataModel>()\n}\n\n/**\n * Marks tables as append-only evidence tables: inserts pass, every update or\n * delete throws. The ontology pattern for votes/history/audit-style tables.\n */\nexport function appendOnly<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (_ctx, change) => {\n\t\t\tif (change.operation !== 'insert') {\n\t\t\t\tthrow new Error(`${table} is append-only`)\n\t\t\t}\n\t\t})\n\t}\n}\n\n/** Forbids hard deletes on the given tables (soft-delete discipline). */\nexport function noDelete<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (_ctx, change) => {\n\t\t\tif (change.operation === 'delete') {\n\t\t\t\tthrow new Error(`${table} rows cannot be deleted`)\n\t\t\t}\n\t\t})\n\t}\n}\n\n/**\n * Post-image tenant invariant for tenantTable rows. Row-level security\n * authorizes a modification against the PRE-image, so ownership must also be\n * enforced against the post-image: inserts must carry a tenant, and updates\n * may never reassign it. Register for every tenant table (same registry that\n * drives createTenantRules).\n */\nexport function tenantOwnership<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (_ctx, change) => {\n\t\t\tif (change.operation === 'insert') {\n\t\t\t\tconst ownership = tenantDocument.safeParse(change.newDoc)\n\t\t\t\tif (!ownership.success) {\n\t\t\t\t\tthrow new Error(`${table} rows require a tenant`)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (change.operation === 'update') {\n\t\t\t\tconst previous = change.oldDoc\n\t\t\t\tconst current = change.newDoc\n\t\t\t\tif (previous.tenant !== current.tenant) {\n\t\t\t\t\tthrow new Error(`${table} tenant ownership cannot be reassigned`)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n/**\n * Maintains zod-table's opinionated lifecycle timestamps: createdAt and\n * updatedAt on insert, updatedAt on every update. archivedAt stays under\n * application control. Register once per table; writes made through the\n * auth constructors keep these fields correct automatically.\n */\nexport function timestamps<DataModel extends GenericDataModel>(\n\ttriggers: Triggers<DataModel>,\n\t...tables: readonly TableNamesInDataModel<DataModel>[]\n) {\n\tfor (const table of tables) {\n\t\ttriggers.register(table, async (ctx, change) => {\n\t\t\tif (change.operation === 'delete') return\n\t\t\tconst now = Date.now()\n\t\t\tif (change.operation === 'insert') {\n\t\t\t\tconst document = change.newDoc\n\t\t\t\tif (document.createdAt === undefined) {\n\t\t\t\t\t// SAFETY: registration requires zod-table lifecycle fields; Convex validates this numeric timestamp patch.\n\t\t\t\t\tawait ctx.innerDb.patch(change.id, {\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t} as never)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = change.oldDoc\n\t\t\tconst current = change.newDoc\n\t\t\t// A write that already moved updatedAt (including our own patch)\n\t\t\t// is left alone — this is the recursion guard.\n\t\t\tif (current.updatedAt === previous.updatedAt) {\n\t\t\t\t// SAFETY: registration requires zod-table lifecycle fields; Convex validates this numeric timestamp patch.\n\t\t\t\tawait ctx.innerDb.patch(change.id, { updatedAt: now } as never)\n\t\t\t}\n\t\t})\n\t}\n}\n\nconst tenantDocument = z.object({ tenant: z.string().min(1) })\n"],"mappings":"kFAaA,SAAgBA,GAA0E,CACzF,OAAO,IAAIC,CACZ,CAMA,SAAgBC,EACf,EACA,GAAGC,EACF,CACD,IAAK,IAAMC,KAASD,EACnBE,EAAS,SAASD,EAAO,MAAO,EAAM,IAAW,CAChD,GAAIE,EAAO,YAAc,SACxB,MAAU,MAAM,GAAGF,EAAM,gBAAgB,CAE3C,CAAC,CAEH,CAGA,SAAgBG,EACf,EACA,GAAGJ,EACF,CACD,IAAK,IAAMC,KAASD,EACnBE,EAAS,SAASD,EAAO,MAAO,EAAM,IAAW,CAChD,GAAIE,EAAO,YAAc,SACxB,MAAU,MAAM,GAAGF,EAAM,wBAAwB,CAEnD,CAAC,CAEH,CASA,SAAgBI,EACf,EACA,GAAGL,EACF,CACD,IAAK,IAAMC,KAASD,EACnBE,EAAS,SAASD,EAAO,MAAO,EAAM,IAAW,CAChD,GAAIE,EAAO,YAAc,SAAU,CAElC,GAAI,CADcG,EAAe,UAAUH,EAAO,MACrC,CAAC,CAAC,QACd,MAAU,MAAM,GAAGF,EAAM,uBAAuB,EAEjD,MACD,CACA,GAAIE,EAAO,YAAc,SAAU,CAClC,IAAMI,EAAWJ,EAAO,OAClBK,EAAUL,EAAO,OACvB,GAAII,EAAS,SAAWC,EAAQ,OAC/B,MAAU,MAAM,GAAGP,EAAM,uCAAuC,CAElE,CACD,CAAC,CAEH,CAQA,SAAgBQ,EACf,EACA,GAAGT,EACF,CACD,IAAK,IAAMC,KAASD,EACnBE,EAAS,SAASD,EAAO,MAAO,EAAK,IAAW,CAC/C,GAAIE,EAAO,YAAc,SAAU,OACnC,IAAMO,EAAM,KAAK,IAAI,EACrB,GAAIP,EAAO,YAAc,SAAU,CACjBA,EAAO,OACX,YAAc,IAAA,IAE1B,MAAMQ,EAAI,QAAQ,MAAMR,EAAO,GAAI,CAClC,UAAWO,EACX,UAAWA,CACZ,CAAU,EAEX,MACD,CACA,IAAMH,EAAWJ,EAAO,OACRA,EAAO,OAGX,YAAcI,EAAS,WAElC,MAAMI,EAAI,QAAQ,MAAMR,EAAO,GAAI,CAAE,UAAWO,CAAI,CAAU,CAEhE,CAAC,CAEH,CAEA,MAAMJ,EAAiBM,EAAE,OAAO,CAAE,OAAQA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAE,CAAC"}
@@ -13,7 +13,7 @@ import { z } from "zod";
13
13
  * longer than the provider's redelivery horizon (a cleanup cron over
14
14
  * `receivedAt` — see docs/webhooks.md).
15
15
  */
16
- declare function webhookEventsTable(): TableBoundary<"webhookEvents", {
16
+ export declare function webhookEventsTable(): TableBoundary<"webhookEvents", {
17
17
  eventKey: z.ZodString;
18
18
  source: z.ZodString;
19
19
  receivedAt: z.ZodNumber;
@@ -25,14 +25,14 @@ type DedupContext = Pick<GenericMutationCtx<GenericDataModel>, 'db'>;
25
25
  * mutation (transactional), never in the action. Returns duplicate: true
26
26
  * when the natural key was already recorded; the caller then no-ops.
27
27
  */
28
- declare function recordWebhookEvent(ctx: DedupContext, input: {
28
+ export declare function recordWebhookEvent(ctx: DedupContext, input: {
29
29
  key: string;
30
30
  source?: string;
31
31
  table?: string;
32
32
  }): Promise<{
33
33
  duplicate: boolean;
34
34
  }>;
35
- type WebhookBoundaryConfig = {
35
+ export type WebhookBoundaryConfig = {
36
36
  /**
37
37
  * Verifies the provider signature over the RAW body bytes — the helper
38
38
  * hands it `request.text()` BEFORE any parsing. Implementations must use
@@ -60,16 +60,15 @@ type WebhookBoundaryConfig = {
60
60
  * handler: httpAction((ctx, request) =>
61
61
  * boundary.handle(ctx, request, internal.domain.vendor.functions.applyEvent)) })
62
62
  */
63
- declare function createWebhookBoundary(config: WebhookBoundaryConfig): {
63
+ export declare function createWebhookBoundary(config: WebhookBoundaryConfig): {
64
64
  handle: <Result>(ctx: {
65
65
  runMutation: (target: FunctionReference<'mutation', 'internal', WebhookDelivery, Result>, args: WebhookDelivery) => Promise<Result>;
66
66
  }, request: Request, target: FunctionReference<'mutation', 'internal', WebhookDelivery, Result>) => Promise<Response>;
67
67
  };
68
- type WebhookDelivery = {
68
+ export type WebhookDelivery = {
69
69
  eventKey: string;
70
70
  payload: string;
71
71
  source: string;
72
72
  };
73
73
  //#endregion
74
- export { WebhookBoundaryConfig, WebhookDelivery, createWebhookBoundary, recordWebhookEvent, webhookEventsTable };
75
74
  //# sourceMappingURL=webhooks.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.mjs","names":[],"sources":["../src/webhooks.ts"],"sourcesContent":["import type { GenericMutationCtx, GenericDataModel, FunctionReference } from 'convex/server'\nimport { z } from 'zod'\nimport { defaultErrors, type ErrorFactory } from './errors'\nimport { zodTable } from './zod-table'\n\n/**\n * The host-owned dedup table (the kit gains no tables). Register it in the\n * schema with the by_eventKey index:\n *\n * webhookEvents: webhookEventsTable().table.index('by_eventKey', ['eventKey'])\n *\n * Rows are host-owned data: prune them on a retention window comfortably\n * longer than the provider's redelivery horizon (a cleanup cron over\n * `receivedAt` — see docs/webhooks.md).\n */\nexport function webhookEventsTable() {\n\treturn zodTable('webhookEvents', () => ({\n\t\teventKey: z.string(),\n\t\tsource: z.string(),\n\t\treceivedAt: z.number(),\n\t}))\n}\n\ntype DedupContext = Pick<GenericMutationCtx<GenericDataModel>, 'db'>\n\n/**\n * Transactional insert-if-absent dedup guard. Call this FIRST inside the\n * internal mutation the boundary delegates to — dedup must live in the\n * mutation (transactional), never in the action. Returns duplicate: true\n * when the natural key was already recorded; the caller then no-ops.\n */\nexport async function recordWebhookEvent(\n\tctx: DedupContext,\n\tinput: { key: string; source?: string; table?: string },\n): Promise<{ duplicate: boolean }> {\n\tconst table = input.table ?? 'webhookEvents'\n\tconst existing = await ctx.db\n\t\t.query(table)\n\t\t.withIndex('by_eventKey', (builder) => builder.eq('eventKey', input.key))\n\t\t.first()\n\tif (existing) return { duplicate: true }\n\tawait ctx.db.insert(table, {\n\t\teventKey: input.key,\n\t\tsource: input.source ?? 'unknown',\n\t\treceivedAt: Date.now(),\n\t})\n\treturn { duplicate: false }\n}\n\nexport type WebhookBoundaryConfig = {\n\t/**\n\t * Verifies the provider signature over the RAW body bytes — the helper\n\t * hands it `request.text()` BEFORE any parsing. Implementations must use\n\t * a constant-time comparison (e.g. crypto.subtle HMAC verify) and read\n\t * the secret from a Convex environment variable. Return false or throw\n\t * to reject.\n\t */\n\tverify: (raw: string, request: Request) => boolean | Promise<boolean>\n\t/**\n\t * Derives the natural dedup key — the `${event}:${id}:${updatedAt}`\n\t * pattern. Receives the raw body and the request; parse inside if needed.\n\t */\n\teventKey: (raw: string, request: Request) => string | Promise<string>\n\tsource?: string\n\terrors?: ErrorFactory\n}\n\n/**\n * The sanctioned webhook pattern as code: verify (raw body, fail closed) →\n * derive natural key → delegate to a host internal mutation whose FIRST act\n * is recordWebhookEvent (transactional dedup). Use inside the host's\n * httpAction:\n *\n * const boundary = createWebhookBoundary({ verify, eventKey })\n * http.route({ path: '/webhooks/vendor', method: 'POST',\n * handler: httpAction((ctx, request) =>\n * boundary.handle(ctx, request, internal.domain.vendor.functions.applyEvent)) })\n */\nexport function createWebhookBoundary(config: WebhookBoundaryConfig) {\n\tconst errors = config.errors ?? defaultErrors\n\n\tasync function handle<Result>(\n\t\tctx: {\n\t\t\trunMutation: (\n\t\t\t\ttarget: FunctionReference<'mutation', 'internal', WebhookDelivery, Result>,\n\t\t\t\targs: WebhookDelivery,\n\t\t\t) => Promise<Result>\n\t\t},\n\t\trequest: Request,\n\t\ttarget: FunctionReference<'mutation', 'internal', WebhookDelivery, Result>,\n\t): Promise<Response> {\n\t\tconst raw = await request.text()\n\t\tlet verified = false\n\t\ttry {\n\t\t\tverified = await config.verify(raw, request)\n\t\t} catch {\n\t\t\tverified = false\n\t\t}\n\t\tif (!verified) {\n\t\t\terrors.throw({\n\t\t\t\tcode: 'WEBHOOK_SIGNATURE_INVALID',\n\t\t\t\tmessage: 'Webhook signature verification failed',\n\t\t\t})\n\t\t}\n\t\tconst key = await config.eventKey(raw, request)\n\t\tawait ctx.runMutation(target, {\n\t\t\teventKey: key,\n\t\t\tpayload: raw,\n\t\t\tsource: config.source ?? 'unknown',\n\t\t})\n\t\treturn new Response(null, { status: 200 })\n\t}\n\n\treturn { handle }\n}\n\nexport type WebhookDelivery = { eventKey: string; payload: string; source: string }\n"],"mappings":"gHAeA,SAAgB,GAAqB,CACpC,OAAO,EAAS,qBAAwB,CACvC,SAAU,EAAE,OAAO,EACnB,OAAQ,EAAE,OAAO,EACjB,WAAY,EAAE,OAAO,CACtB,EAAE,CACH,CAUA,eAAsB,EACrB,EACA,EACkC,CAClC,IAAM,EAAQ,EAAM,OAAS,gBAW7B,OANI,MAJmB,EAAI,GACzB,MAAM,CAAK,CAAC,CACZ,UAAU,cAAgB,GAAY,EAAQ,GAAG,WAAY,EAAM,GAAG,CAAC,CAAC,CACxE,MAAM,EACa,CAAE,UAAW,EAAK,GACvC,MAAM,EAAI,GAAG,OAAO,EAAO,CAC1B,SAAU,EAAM,IAChB,OAAQ,EAAM,QAAU,UACxB,WAAY,KAAK,IAAI,CACtB,CAAC,EACM,CAAE,UAAW,EAAM,EAC3B,CA+BA,SAAgB,EAAsB,EAA+B,CACpE,IAAM,EAAS,EAAO,QAAU,EAEhC,eAAe,EACd,EAMA,EACA,EACoB,CACpB,IAAM,EAAM,MAAM,EAAQ,KAAK,EAC3B,EAAW,GACf,GAAI,CACH,EAAW,MAAM,EAAO,OAAO,EAAK,CAAO,CAC5C,MAAQ,CACP,EAAW,EACZ,CACK,GACJ,EAAO,MAAM,CACZ,KAAM,4BACN,QAAS,uCACV,CAAC,EAEF,IAAM,EAAM,MAAM,EAAO,SAAS,EAAK,CAAO,EAM9C,OALA,MAAM,EAAI,YAAY,EAAQ,CAC7B,SAAU,EACV,QAAS,EACT,OAAQ,EAAO,QAAU,SAC1B,CAAC,EACM,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,CAC1C,CAEA,MAAO,CAAE,QAAO,CACjB"}
1
+ {"version":3,"file":"webhooks.mjs","names":["webhookEventsTable","zodTable","z","recordWebhookEvent","table","input","ctx","builder","createWebhookBoundary","errors","config","defaultErrors","handle","raw","request","verified","key","target"],"sources":["../src/webhooks.ts"],"sourcesContent":["import type { GenericMutationCtx, GenericDataModel, FunctionReference } from 'convex/server'\nimport { z } from 'zod'\nimport { defaultErrors, type ErrorFactory } from './errors'\nimport { zodTable } from './zod-table'\n\n/**\n * The host-owned dedup table (the kit gains no tables). Register it in the\n * schema with the by_eventKey index:\n *\n * webhookEvents: webhookEventsTable().table.index('by_eventKey', ['eventKey'])\n *\n * Rows are host-owned data: prune them on a retention window comfortably\n * longer than the provider's redelivery horizon (a cleanup cron over\n * `receivedAt` — see docs/webhooks.md).\n */\nexport function webhookEventsTable() {\n\treturn zodTable('webhookEvents', () => ({\n\t\teventKey: z.string(),\n\t\tsource: z.string(),\n\t\treceivedAt: z.number(),\n\t}))\n}\n\ntype DedupContext = Pick<GenericMutationCtx<GenericDataModel>, 'db'>\n\n/**\n * Transactional insert-if-absent dedup guard. Call this FIRST inside the\n * internal mutation the boundary delegates to — dedup must live in the\n * mutation (transactional), never in the action. Returns duplicate: true\n * when the natural key was already recorded; the caller then no-ops.\n */\nexport async function recordWebhookEvent(\n\tctx: DedupContext,\n\tinput: { key: string; source?: string; table?: string },\n): Promise<{ duplicate: boolean }> {\n\tconst table = input.table ?? 'webhookEvents'\n\tconst existing = await ctx.db\n\t\t.query(table)\n\t\t.withIndex('by_eventKey', (builder) => builder.eq('eventKey', input.key))\n\t\t.first()\n\tif (existing) return { duplicate: true }\n\tawait ctx.db.insert(table, {\n\t\teventKey: input.key,\n\t\tsource: input.source ?? 'unknown',\n\t\treceivedAt: Date.now(),\n\t})\n\treturn { duplicate: false }\n}\n\nexport type WebhookBoundaryConfig = {\n\t/**\n\t * Verifies the provider signature over the RAW body bytes — the helper\n\t * hands it `request.text()` BEFORE any parsing. Implementations must use\n\t * a constant-time comparison (e.g. crypto.subtle HMAC verify) and read\n\t * the secret from a Convex environment variable. Return false or throw\n\t * to reject.\n\t */\n\tverify: (raw: string, request: Request) => boolean | Promise<boolean>\n\t/**\n\t * Derives the natural dedup key — the `${event}:${id}:${updatedAt}`\n\t * pattern. Receives the raw body and the request; parse inside if needed.\n\t */\n\teventKey: (raw: string, request: Request) => string | Promise<string>\n\tsource?: string\n\terrors?: ErrorFactory\n}\n\n/**\n * The sanctioned webhook pattern as code: verify (raw body, fail closed) →\n * derive natural key → delegate to a host internal mutation whose FIRST act\n * is recordWebhookEvent (transactional dedup). Use inside the host's\n * httpAction:\n *\n * const boundary = createWebhookBoundary({ verify, eventKey })\n * http.route({ path: '/webhooks/vendor', method: 'POST',\n * handler: httpAction((ctx, request) =>\n * boundary.handle(ctx, request, internal.domain.vendor.functions.applyEvent)) })\n */\nexport function createWebhookBoundary(config: WebhookBoundaryConfig) {\n\tconst errors = config.errors ?? defaultErrors\n\n\tasync function handle<Result>(\n\t\tctx: {\n\t\t\trunMutation: (\n\t\t\t\ttarget: FunctionReference<'mutation', 'internal', WebhookDelivery, Result>,\n\t\t\t\targs: WebhookDelivery,\n\t\t\t) => Promise<Result>\n\t\t},\n\t\trequest: Request,\n\t\ttarget: FunctionReference<'mutation', 'internal', WebhookDelivery, Result>,\n\t): Promise<Response> {\n\t\tconst raw = await request.text()\n\t\tlet verified = false\n\t\ttry {\n\t\t\tverified = await config.verify(raw, request)\n\t\t} catch {\n\t\t\tverified = false\n\t\t}\n\t\tif (!verified) {\n\t\t\terrors.throw({\n\t\t\t\tcode: 'WEBHOOK_SIGNATURE_INVALID',\n\t\t\t\tmessage: 'Webhook signature verification failed',\n\t\t\t})\n\t\t}\n\t\tconst key = await config.eventKey(raw, request)\n\t\tawait ctx.runMutation(target, {\n\t\t\teventKey: key,\n\t\t\tpayload: raw,\n\t\t\tsource: config.source ?? 'unknown',\n\t\t})\n\t\treturn new Response(null, { status: 200 })\n\t}\n\n\treturn { handle }\n}\n\nexport type WebhookDelivery = { eventKey: string; payload: string; source: string }\n"],"mappings":"gHAeA,SAAgBA,GAAqB,CACpC,OAAOC,EAAS,qBAAwB,CACvC,SAAUC,EAAE,OAAO,EACnB,OAAQA,EAAE,OAAO,EACjB,WAAYA,EAAE,OAAO,CACtB,EAAE,CACH,CAUA,eAAsBC,EACrB,EACA,EACkC,CAClC,IAAMC,EAAQC,EAAM,OAAS,gBAW7B,OANI,MAJmBC,EAAI,GACzB,MAAMF,CAAK,CAAC,CACZ,UAAU,cAAgB,GAAYG,EAAQ,GAAG,WAAYF,EAAM,GAAG,CAAC,CAAC,CACxE,MAAM,EACa,CAAE,UAAW,EAAK,GACvC,MAAMC,EAAI,GAAG,OAAOF,EAAO,CAC1B,SAAUC,EAAM,IAChB,OAAQA,EAAM,QAAU,UACxB,WAAY,KAAK,IAAI,CACtB,CAAC,EACM,CAAE,UAAW,EAAM,EAC3B,CA+BA,SAAgBG,EAAsB,EAA+B,CACpE,IAAMC,EAASC,EAAO,QAAUC,EAEhC,eAAeC,EACd,EAMA,EACA,EACoB,CACpB,IAAMC,EAAM,MAAMC,EAAQ,KAAK,EAC3BC,EAAW,GACf,GAAI,CACH,EAAW,MAAML,EAAO,OAAOG,EAAKC,CAAO,CAC5C,MAAQ,CACP,EAAW,EACZ,CACKC,GACJN,EAAO,MAAM,CACZ,KAAM,4BACN,QAAS,uCACV,CAAC,EAEF,IAAMO,EAAM,MAAMN,EAAO,SAASG,EAAKC,CAAO,EAM9C,OALA,MAAMR,EAAI,YAAYW,EAAQ,CAC7B,SAAUD,EACV,QAASH,EACT,OAAQH,EAAO,QAAU,SAC1B,CAAC,EACM,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,CAC1C,CAEA,MAAO,CAAE,QAAO,CACjB"}
@@ -16,8 +16,8 @@ declare const timestampFields: {
16
16
  archivedAt: z.ZodOptional<z.ZodNumber>;
17
17
  };
18
18
  type TimestampFields = typeof timestampFields;
19
- declare const TIMESTAMP_FIELDS: readonly ['createdAt', 'updatedAt', 'archivedAt'];
20
- type TableBoundaryOptions<Fields extends FieldSchemas, ServerFields extends readonly FieldKey<Fields>[], CommandFields extends readonly FieldKey<Fields>[], PublicFields extends readonly FieldKey<Fields & TimestampFields>[]> = {
19
+ export declare const TIMESTAMP_FIELDS: readonly ['createdAt', 'updatedAt', 'archivedAt'];
20
+ export type TableBoundaryOptions<Fields extends FieldSchemas, ServerFields extends readonly FieldKey<Fields>[], CommandFields extends readonly FieldKey<Fields>[], PublicFields extends readonly FieldKey<Fields & TimestampFields>[]> = {
21
21
  serverFields?: ServerFields;
22
22
  commandFields?: CommandFields;
23
23
  publicFields?: PublicFields;
@@ -26,7 +26,7 @@ type StrictObject<Fields extends FieldSchemas> = z.ZodObject<Fields, z.core.$str
26
26
  type WriteFields<Fields extends FieldSchemas, ServerFields extends readonly FieldKey<Fields>[]> = Omit<Fields & TimestampFields, ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number]>;
27
27
  type PartialFields<Fields extends FieldSchemas> = { [Key in keyof Fields]: z.ZodOptional<Fields[Key]>; };
28
28
  /** Named schema relationships keep generated declarations bounded and consumers fully typed. */
29
- type TableBoundary<Table extends string, Fields extends FieldSchemas, ServerFields extends readonly FieldKey<Fields>[], CommandFields extends readonly FieldKey<Fields>[], PublicFields extends readonly FieldKey<Fields & TimestampFields>[]> = {
29
+ export type TableBoundary<Table extends string, Fields extends FieldSchemas, ServerFields extends readonly FieldKey<Fields>[], CommandFields extends readonly FieldKey<Fields>[], PublicFields extends readonly FieldKey<Fields & TimestampFields>[]> = {
30
30
  tableName: Table;
31
31
  storage: StrictObject<Fields & TimestampFields>;
32
32
  schema: ReturnType<typeof documentSchema<Fields & TimestampFields, Table>>;
@@ -52,7 +52,7 @@ type TableBoundary<Table extends string, Fields extends FieldSchemas, ServerFiel
52
52
  };
53
53
  };
54
54
  /** Defines one table and derives its storage, document, write, and DTO boundaries. */
55
- declare function zodTable<Table extends string, Fields extends FieldSchemas, const ServerFields extends readonly FieldKey<Fields>[] = readonly [], const CommandFields extends readonly FieldKey<Fields>[] = readonly [], const PublicFields extends readonly FieldKey<Fields & TimestampFields>[] = readonly []>(tableName: Table, fields: (id: typeof zid$1) => Fields, options?: TableBoundaryOptions<Fields, ServerFields, CommandFields, PublicFields>): TableBoundary<Table, Fields, ServerFields, CommandFields, PublicFields>;
55
+ export declare function zodTable<Table extends string, Fields extends FieldSchemas, const ServerFields extends readonly FieldKey<Fields>[] = readonly [], const CommandFields extends readonly FieldKey<Fields>[] = readonly [], const PublicFields extends readonly FieldKey<Fields & TimestampFields>[] = readonly []>(tableName: Table, fields: (id: typeof zid$1) => Fields, options?: TableBoundaryOptions<Fields, ServerFields, CommandFields, PublicFields>): TableBoundary<Table, Fields, ServerFields, CommandFields, PublicFields>;
56
56
  declare const tenantFields: {
57
57
  tenant: z.ZodString;
58
58
  };
@@ -65,7 +65,7 @@ type TenantFields = typeof tenantFields;
65
65
  * trigger forbids reassigning it. Expose it via publicFields only when a DTO
66
66
  * genuinely needs it.
67
67
  */
68
- declare function tenantTable<Table extends string, Fields extends FieldSchemas, const ServerFields extends readonly FieldKey<Fields>[] = readonly [], const CommandFields extends readonly FieldKey<Fields>[] = readonly [], const PublicFields extends readonly FieldKey<Fields & TenantFields & TimestampFields>[] = readonly []>(tableName: Table, fields: (id: typeof zid$1) => Fields, options?: TableBoundaryOptions<Fields & TenantFields, readonly (ServerFields[number] | 'tenant')[], CommandFields, PublicFields>): TableBoundary<Table, Fields & {
68
+ export declare function tenantTable<Table extends string, Fields extends FieldSchemas, const ServerFields extends readonly FieldKey<Fields>[] = readonly [], const CommandFields extends readonly FieldKey<Fields>[] = readonly [], const PublicFields extends readonly FieldKey<Fields & TenantFields & TimestampFields>[] = readonly []>(tableName: Table, fields: (id: typeof zid$1) => Fields, options?: TableBoundaryOptions<Fields & TenantFields, readonly (ServerFields[number] | 'tenant')[], CommandFields, PublicFields>): TableBoundary<Table, Fields & {
69
69
  tenant: z.ZodString;
70
70
  }, readonly ("tenant" | ServerFields[number])[], CommandFields, PublicFields>;
71
71
  /**
@@ -73,7 +73,7 @@ declare function tenantTable<Table extends string, Fields extends FieldSchemas,
73
73
  * duplicate table names across modules — the module-registry combinator for
74
74
  * domain/table.ts: `defineSchema(createModule(catalogTables, salesTables))`.
75
75
  */
76
- declare function createModule<const Maps extends readonly Record<string, TableDefinition<any>>[]>(...maps: Maps): UnionToIntersection<Maps[number]>;
76
+ export declare function createModule<const Maps extends readonly Record<string, TableDefinition<any>>[]>(...maps: Maps): UnionToIntersection<Maps[number]>;
77
77
  type UnionToIntersection<Union> = (Union extends unknown ? (member: Union) => void : never) extends ((member: infer Intersection) => void) ? Intersection : never;
78
78
  /**
79
79
  * Cursor-pagination boundary schemas bridged from Convex's own validators:
@@ -84,7 +84,7 @@ type UnionToIntersection<Union> = (Union extends unknown ? (member: Union) => vo
84
84
  * Note: under row-level security, pages may be SHORTER than numItems when
85
85
  * rules reject rows mid-page; cursors remain correct.
86
86
  */
87
- declare function paginated<Dto extends z.ZodType>(dto: Dto): {
87
+ export declare function paginated<Dto extends z.ZodType>(dto: Dto): {
88
88
  args: {
89
89
  paginationOpts: z.ZodObject<{
90
90
  numItems: z.ZodNumber;
@@ -107,7 +107,7 @@ declare function paginated<Dto extends z.ZodType>(dto: Dto): {
107
107
  }, z.core.$strip>;
108
108
  };
109
109
  /** Preserves discriminated-union storage while retaining the same table owner. */
110
- declare function zodVariantTable<Table extends string, Schema extends z.ZodType>(tableName: Table, storage: Schema): {
110
+ export declare function zodVariantTable<Table extends string, Schema extends z.ZodType>(tableName: Table, storage: Schema): {
111
111
  tableName: Table;
112
112
  storage: Schema;
113
113
  insertSchema: Schema;
@@ -117,7 +117,7 @@ declare function zodVariantTable<Table extends string, Schema extends z.ZodType>
117
117
  * A zid that presents as a plain string in generated JSON schemas so
118
118
  * LLM-tool inputs stay primitive, while keeping the Id type at compile time.
119
119
  */
120
- declare function jsonSafeZid<Table extends string>(tableName: Table): z.ZodType<GenericId<Table>, string>;
120
+ export declare function jsonSafeZid<Table extends string>(tableName: Table): z.ZodType<GenericId<Table>, string>;
121
121
  type ZodObjectOmit<Schema extends z.ZodObject<any, any>, OmitMask extends z.util.Mask<keyof Schema['shape']>> = Schema extends z.ZodObject<infer ObjectFields, infer Config> ? z.ZodObject<z.util.Flatten<Omit<ObjectFields, Extract<keyof ObjectFields, keyof OmitMask>>>, Config> : never;
122
122
  declare function documentSchema<Fields extends FieldSchemas, Table extends string>(storage: StrictObject<Fields>, tableName: Table): z.ZodObject<(keyof Fields & ("_creationTime" | "_id") extends never ? Fields & {
123
123
  _id: import("convex-helpers/server/zod4").Zid<Table>;
@@ -127,5 +127,5 @@ declare function documentSchema<Fields extends FieldSchemas, Table extends strin
127
127
  _creationTime: z.ZodNumber;
128
128
  }) extends (infer T) ? { [k in keyof T]: T[k]; } : never, z.core.$strict>;
129
129
  //#endregion
130
- export { TIMESTAMP_FIELDS, TableBoundary, TableBoundaryOptions, convexToZod, createModule, jsonSafeZid, paginated, tenantTable, zid, zodTable, zodToConvex, zodVariantTable };
130
+ export { convexToZod, zid, zodToConvex };
131
131
  //# sourceMappingURL=zod-table.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"zod-table.mjs","names":["zid","zodToConvex","convexToZod"],"sources":["../src/zod-table.ts"],"sourcesContent":["import {\n\tconvexToZod,\n\tzid,\n\tzodToConvex,\n\ttype ConvexValidatorFromZod,\n} from 'convex-helpers/server/zod4'\nimport { defineTable, paginationOptsValidator, type TableDefinition } from 'convex/server'\nimport type { GenericId } from 'convex/values'\nimport { z } from 'zod'\n\n// Use this entry point when deriving validators from kit-owned schemas so\n// the producer and converter consult the same convex-helpers ID registry.\nexport { zid, zodToConvex, convexToZod } from 'convex-helpers/server/zod4'\n\ntype FieldSchemas = Record<string, z.ZodType>\ntype FieldKey<Fields extends FieldSchemas> = Extract<keyof Fields, string>\n\n/**\n * Opinionated lifecycle timestamps — every table has them, server-owned,\n * always excluded from insert/command boundaries. Maintained by the\n * `timestamps` trigger helper (see cvx-kit/triggers).\n */\nconst timestampFields = {\n\tcreatedAt: z.number().optional(),\n\tupdatedAt: z.number().optional(),\n\tarchivedAt: z.number().optional(),\n} satisfies FieldSchemas\n\ntype TimestampFields = typeof timestampFields\n\nexport const TIMESTAMP_FIELDS = ['createdAt', 'updatedAt', 'archivedAt'] as const\n\nexport type TableBoundaryOptions<\n\tFields extends FieldSchemas,\n\tServerFields extends readonly FieldKey<Fields>[],\n\tCommandFields extends readonly FieldKey<Fields>[],\n\tPublicFields extends readonly FieldKey<Fields & TimestampFields>[],\n> = {\n\tserverFields?: ServerFields\n\tcommandFields?: CommandFields\n\tpublicFields?: PublicFields\n}\n\ntype StrictObject<Fields extends FieldSchemas> = z.ZodObject<Fields, z.core.$strict>\ntype WriteFields<\n\tFields extends FieldSchemas,\n\tServerFields extends readonly FieldKey<Fields>[],\n> = Omit<Fields & TimestampFields, ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number]>\ntype PartialFields<Fields extends FieldSchemas> = {\n\t[Key in keyof Fields]: z.ZodOptional<Fields[Key]>\n}\n\n/** Named schema relationships keep generated declarations bounded and consumers fully typed. */\nexport type TableBoundary<\n\tTable extends string,\n\tFields extends FieldSchemas,\n\tServerFields extends readonly FieldKey<Fields>[],\n\tCommandFields extends readonly FieldKey<Fields>[],\n\tPublicFields extends readonly FieldKey<Fields & TimestampFields>[],\n> = {\n\ttableName: Table\n\tstorage: StrictObject<Fields & TimestampFields>\n\tschema: ReturnType<typeof documentSchema<Fields & TimestampFields, Table>>\n\tinsertSchema: StrictObject<WriteFields<Fields, ServerFields>>\n\tupdateSchema: StrictObject<PartialFields<WriteFields<Fields, ServerFields>>>\n\tcommandInput: StrictObject<Pick<Fields & TimestampFields, CommandFields[number]>>\n\tpublicDto: StrictObject<Pick<Fields & TimestampFields, PublicFields[number]>>\n\ttoPublicDto: (\n\t\trow: z.input<StrictObject<Fields & TimestampFields>>,\n\t) => z.output<StrictObject<Pick<Fields & TimestampFields, PublicFields[number]>>>\n\ttable: TableDefinition<ConvexValidatorFromZod<StrictObject<Fields & TimestampFields>, 'required'>>\n\tinsert(): StrictObject<WriteFields<Fields, ServerFields>>\n\tinsert<Mask extends z.util.Mask<keyof WriteFields<Fields, ServerFields>>>(\n\t\tomit: Mask,\n\t): ZodObjectOmit<StrictObject<WriteFields<Fields, ServerFields>>, Mask>\n\tupdate(): StrictObject<PartialFields<WriteFields<Fields, ServerFields>>>\n\tupdate<Mask extends z.util.Mask<keyof WriteFields<Fields, ServerFields>>>(\n\t\tomit: Mask,\n\t): ZodObjectOmit<StrictObject<PartialFields<WriteFields<Fields, ServerFields>>>, Mask>\n\ttools: {\n\t\tinsert: StrictObject<Pick<Fields & TimestampFields, CommandFields[number]>>\n\t\tupdate: StrictObject<{\n\t\t\tdata: StrictObject<PartialFields<Pick<Fields & TimestampFields, CommandFields[number]>>>\n\t\t\tid: z.ZodType<GenericId<Table>, string>\n\t\t}>\n\t\tid: StrictObject<{ id: z.ZodType<GenericId<Table>, string> }>\n\t}\n}\n\n/** Defines one table and derives its storage, document, write, and DTO boundaries. */\nexport function zodTable<\n\tTable extends string,\n\tFields extends FieldSchemas,\n\tconst ServerFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly FieldKey<Fields & TimestampFields>[] = readonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<Fields, ServerFields, CommandFields, PublicFields> = {},\n): TableBoundary<Table, Fields, ServerFields, CommandFields, PublicFields> {\n\tconst columns: Fields & TimestampFields = { ...fields(zid), ...timestampFields }\n\tconst storage: z.ZodObject<Fields & TimestampFields, z.core.$strict> = z.object(columns).strict()\n\tconst schema = documentSchema(storage, tableName)\n\tconst omittedKeys: readonly (ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number])[] = [\n\t\t...TIMESTAMP_FIELDS,\n\t\t...(options.serverFields ?? []),\n\t]\n\tconst commandKeys: readonly CommandFields[number][] = options.commandFields ?? []\n\tconst publicKeys: readonly PublicFields[number][] = options.publicFields ?? []\n\tconst insertSchema: z.ZodObject<WriteFields<Fields, ServerFields>, z.core.$strict> = z\n\t\t.object(omitFields(columns, omittedKeys))\n\t\t.strict()\n\tconst updateSchema = insertSchema.partial()\n\tconst commandInput: z.ZodObject<\n\t\tPick<Fields & TimestampFields, CommandFields[number]>,\n\t\tz.core.$strict\n\t> = z.object(pickFields(columns, commandKeys)).strict()\n\tconst publicDto: z.ZodObject<\n\t\tPick<Fields & TimestampFields, PublicFields[number]>,\n\t\tz.core.$strict\n\t> = z.object(pickFields(columns, publicKeys)).strict()\n\tconst toPublicDto = (row: z.input<typeof storage>) =>\n\t\tpublicDto.parse(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.keys(publicDto.shape).map((key) => {\n\t\t\t\t\t// SAFETY: publicDto contains only keys selected from the storage fields.\n\t\t\t\t\treturn [key, row[key as keyof typeof row]]\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\n\ttype InsertSchema = typeof insertSchema\n\ttype UpdateSchema = typeof updateSchema\n\ttype InsertMask = z.util.Mask<keyof InsertSchema['shape']>\n\ttype UpdateMask = z.util.Mask<keyof UpdateSchema['shape']>\n\n\tfunction insert(): InsertSchema\n\tfunction insert<OmitMask extends InsertMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<InsertSchema, OmitMask>\n\tfunction insert(omit?: InsertMask) {\n\t\t// SAFETY: InsertMask contains only insertSchema keys; the overload retains Zod's exact mask relationship.\n\t\treturn omit ? insertSchema.omit(omit as never) : insertSchema\n\t}\n\n\tfunction update(): UpdateSchema\n\tfunction update<OmitMask extends UpdateMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<UpdateSchema, OmitMask>\n\tfunction update(omit?: UpdateMask) {\n\t\t// SAFETY: UpdateMask contains only updateSchema keys; the overload retains Zod's exact mask relationship.\n\t\treturn omit ? updateSchema.omit(omit as never) : updateSchema\n\t}\n\n\tconst table: TableDefinition<ConvexValidatorFromZod<typeof storage, 'required'>> = defineTable(\n\t\tzodToConvex(storage),\n\t)\n\treturn {\n\t\ttableName,\n\t\tschema,\n\t\tstorage,\n\t\tinsertSchema,\n\t\tupdateSchema,\n\t\tcommandInput,\n\t\tpublicDto,\n\t\ttoPublicDto,\n\t\ttable,\n\t\tinsert,\n\t\tupdate,\n\t\ttools: {\n\t\t\t// jsonSafeZid (not zid): tool masks feed generated JSON schemas, so\n\t\t\t// ids must present as plain strings while keeping the Id<...> type.\n\t\t\tinsert: commandInput,\n\t\t\tupdate: z.object({ data: commandInput.partial(), id: jsonSafeZid(tableName) }).strict(),\n\t\t\tid: z.object({ id: jsonSafeZid(tableName) }).strict(),\n\t\t},\n\t}\n}\n\nconst tenantFields = {\n\ttenant: z.string().min(1),\n} satisfies FieldSchemas\n\ntype TenantFields = typeof tenantFields\n\n/**\n * A zodTable whose rows are tenant-owned: injects the server-owned `tenant`\n * field (the tenancy boundary — see cvx-kit/tenancy). Like timestamps, it is\n * excluded from insert/update/command boundaries; handlers stamp it from\n * ctx.tenant, row-level security matches on it, and the tenantOwnership\n * trigger forbids reassigning it. Expose it via publicFields only when a DTO\n * genuinely needs it.\n */\nexport function tenantTable<\n\tTable extends string,\n\tFields extends FieldSchemas,\n\tconst ServerFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly FieldKey<Fields & TenantFields & TimestampFields>[] =\n\t\treadonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<\n\t\tFields & TenantFields,\n\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\tCommandFields,\n\t\tPublicFields\n\t> = {},\n) {\n\treturn zodTable<\n\t\tTable,\n\t\tFields & TenantFields,\n\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\tCommandFields,\n\t\tPublicFields\n\t>(tableName, (id) => ({ ...fields(id), ...tenantFields }), {\n\t\t...options,\n\t\tserverFields: [...(options.serverFields ?? []), 'tenant'],\n\t})\n}\n\n/**\n * Merges per-module table maps into the application schema map, rejecting\n * duplicate table names across modules — the module-registry combinator for\n * domain/table.ts: `defineSchema(createModule(catalogTables, salesTables))`.\n */\nexport function createModule<const Maps extends readonly Record<string, TableDefinition<any>>[]>(\n\t...maps: Maps\n): UnionToIntersection<Maps[number]> {\n\tconst combined: Record<string, TableDefinition<any>> = {}\n\tfor (const map of maps) {\n\t\tfor (const [tableName, definition] of Object.entries(map)) {\n\t\t\tif (tableName in combined) {\n\t\t\t\tthrow new Error(`Table \"${tableName}\" is declared by more than one module`)\n\t\t\t}\n\t\t\tcombined[tableName] = definition\n\t\t}\n\t}\n\t// SAFETY: every input entry is copied, and duplicate keys are rejected above.\n\treturn combined as UnionToIntersection<Maps[number]>\n}\n\ntype UnionToIntersection<Union> = (\n\tUnion extends unknown ? (member: Union) => void : never\n) extends (member: infer Intersection) => void\n\t? Intersection\n\t: never\n\n/**\n * Cursor-pagination boundary schemas bridged from Convex's own validators:\n * `args.paginationOpts` for function args, `result` for the page shape.\n * Pair with `include(...).paginate(opts)` (cvx-kit/auth). The result object\n * is deliberately non-strict — Convex may attach engine fields (splitCursor,\n * pageStatus); the page items themselves are validated against the dto.\n * Note: under row-level security, pages may be SHORTER than numItems when\n * rules reject rows mid-page; cursors remain correct.\n */\nexport function paginated<Dto extends z.ZodType>(dto: Dto) {\n\treturn {\n\t\targs: { paginationOpts: convexToZod(paginationOptsValidator) },\n\t\tresult: z.object({\n\t\t\tpage: z.array(dto),\n\t\t\tisDone: z.boolean(),\n\t\t\tcontinueCursor: z.string(),\n\t\t\tsplitCursor: z.string().nullable().optional(),\n\t\t\tpageStatus: z.enum(['SplitRecommended', 'SplitRequired']).nullable().optional(),\n\t\t}),\n\t}\n}\n\n/** Preserves discriminated-union storage while retaining the same table owner. */\nexport function zodVariantTable<Table extends string, Schema extends z.ZodType>(\n\ttableName: Table,\n\tstorage: Schema,\n) {\n\treturn {\n\t\ttableName,\n\t\tstorage,\n\t\tinsertSchema: storage,\n\t\ttable: defineTable(zodToConvex(storage)),\n\t}\n}\n\n/**\n * A zid that presents as a plain string in generated JSON schemas so\n * LLM-tool inputs stay primitive, while keeping the Id type at compile time.\n */\nexport function jsonSafeZid<Table extends string>(\n\ttableName: Table,\n): z.ZodType<GenericId<Table>, string> {\n\tconst id = zid(tableName)\n\treturn z\n\t\t.string()\n\t\t.refine((value): value is GenericId<Table> => id.safeParse(value).success)\n\t\t.describe(`Convex document id for table \"${tableName}\"; validated against the table by Convex`)\n}\n\ntype ZodObjectOmit<\n\tSchema extends z.ZodObject<any, any>,\n\tOmitMask extends z.util.Mask<keyof Schema['shape']>,\n> =\n\tSchema extends z.ZodObject<infer ObjectFields, infer Config>\n\t\t? z.ZodObject<\n\t\t\t\tz.util.Flatten<Omit<ObjectFields, Extract<keyof ObjectFields, keyof OmitMask>>>,\n\t\t\t\tConfig\n\t\t\t>\n\t\t: never\n\nfunction documentSchema<Fields extends FieldSchemas, Table extends string>(\n\tstorage: StrictObject<Fields>,\n\ttableName: Table,\n) {\n\treturn storage.extend({ _id: zid(tableName), _creationTime: z.number() })\n}\n\nfunction pickFields<Fields extends FieldSchemas, const Keys extends readonly FieldKey<Fields>[]>(\n\tfields: Fields,\n\tkeys: Keys,\n) {\n\t// SAFETY: each entry uses a requested key and its original validator, preserving Pick.\n\treturn Object.fromEntries(keys.map((key) => [key, fields[key]])) as Pick<Fields, Keys[number]>\n}\n\nfunction omitFields<Fields extends FieldSchemas, const Keys extends readonly FieldKey<Fields>[]>(\n\tfields: Fields,\n\tkeys: Keys,\n) {\n\tconst omitted = new Set<string>(keys)\n\t// SAFETY: filtering removes exactly the supplied keys and keeps every remaining validator.\n\treturn Object.fromEntries(Object.entries(fields).filter(([key]) => !omitted.has(key))) as Omit<\n\t\tFields,\n\t\tKeys[number]\n\t>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"zod-table.mjs","names":["zid","zodToConvex","convexToZod","convexToZod$1","zid$1","zodToConvex$1","timestampFields","z","TIMESTAMP_FIELDS","zodTable","columns","fields","storage","schema","documentSchema","tableName","omittedKeys","options","commandKeys","publicKeys","insertSchema","omitFields","updateSchema","commandInput","pickFields","publicDto","toPublicDto","key","row","insert","omit","update","defineTable","jsonSafeZid","tenantFields","tenantTable","id","createModule","maps","combined","map","definition","paginated","paginationOptsValidator","dto","zodVariantTable","value","keys","omitted","convexToZod","zid","zodToConvex"],"sources":["../src/zod-table.ts"],"sourcesContent":["import {\n\tconvexToZod,\n\tzid,\n\tzodToConvex,\n\ttype ConvexValidatorFromZod,\n} from 'convex-helpers/server/zod4'\nimport { defineTable, paginationOptsValidator, type TableDefinition } from 'convex/server'\nimport type { GenericId } from 'convex/values'\nimport { z } from 'zod'\n\n// Use this entry point when deriving validators from kit-owned schemas so\n// the producer and converter consult the same convex-helpers ID registry.\nexport { zid, zodToConvex, convexToZod } from 'convex-helpers/server/zod4'\n\ntype FieldSchemas = Record<string, z.ZodType>\ntype FieldKey<Fields extends FieldSchemas> = Extract<keyof Fields, string>\n\n/**\n * Opinionated lifecycle timestamps — every table has them, server-owned,\n * always excluded from insert/command boundaries. Maintained by the\n * `timestamps` trigger helper (see cvx-kit/triggers).\n */\nconst timestampFields = {\n\tcreatedAt: z.number().optional(),\n\tupdatedAt: z.number().optional(),\n\tarchivedAt: z.number().optional(),\n} satisfies FieldSchemas\n\ntype TimestampFields = typeof timestampFields\n\nexport const TIMESTAMP_FIELDS = ['createdAt', 'updatedAt', 'archivedAt'] as const\n\nexport type TableBoundaryOptions<\n\tFields extends FieldSchemas,\n\tServerFields extends readonly FieldKey<Fields>[],\n\tCommandFields extends readonly FieldKey<Fields>[],\n\tPublicFields extends readonly FieldKey<Fields & TimestampFields>[],\n> = {\n\tserverFields?: ServerFields\n\tcommandFields?: CommandFields\n\tpublicFields?: PublicFields\n}\n\ntype StrictObject<Fields extends FieldSchemas> = z.ZodObject<Fields, z.core.$strict>\ntype WriteFields<\n\tFields extends FieldSchemas,\n\tServerFields extends readonly FieldKey<Fields>[],\n> = Omit<Fields & TimestampFields, ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number]>\ntype PartialFields<Fields extends FieldSchemas> = {\n\t[Key in keyof Fields]: z.ZodOptional<Fields[Key]>\n}\n\n/** Named schema relationships keep generated declarations bounded and consumers fully typed. */\nexport type TableBoundary<\n\tTable extends string,\n\tFields extends FieldSchemas,\n\tServerFields extends readonly FieldKey<Fields>[],\n\tCommandFields extends readonly FieldKey<Fields>[],\n\tPublicFields extends readonly FieldKey<Fields & TimestampFields>[],\n> = {\n\ttableName: Table\n\tstorage: StrictObject<Fields & TimestampFields>\n\tschema: ReturnType<typeof documentSchema<Fields & TimestampFields, Table>>\n\tinsertSchema: StrictObject<WriteFields<Fields, ServerFields>>\n\tupdateSchema: StrictObject<PartialFields<WriteFields<Fields, ServerFields>>>\n\tcommandInput: StrictObject<Pick<Fields & TimestampFields, CommandFields[number]>>\n\tpublicDto: StrictObject<Pick<Fields & TimestampFields, PublicFields[number]>>\n\ttoPublicDto: (\n\t\trow: z.input<StrictObject<Fields & TimestampFields>>,\n\t) => z.output<StrictObject<Pick<Fields & TimestampFields, PublicFields[number]>>>\n\ttable: TableDefinition<ConvexValidatorFromZod<StrictObject<Fields & TimestampFields>, 'required'>>\n\tinsert(): StrictObject<WriteFields<Fields, ServerFields>>\n\tinsert<Mask extends z.util.Mask<keyof WriteFields<Fields, ServerFields>>>(\n\t\tomit: Mask,\n\t): ZodObjectOmit<StrictObject<WriteFields<Fields, ServerFields>>, Mask>\n\tupdate(): StrictObject<PartialFields<WriteFields<Fields, ServerFields>>>\n\tupdate<Mask extends z.util.Mask<keyof WriteFields<Fields, ServerFields>>>(\n\t\tomit: Mask,\n\t): ZodObjectOmit<StrictObject<PartialFields<WriteFields<Fields, ServerFields>>>, Mask>\n\ttools: {\n\t\tinsert: StrictObject<Pick<Fields & TimestampFields, CommandFields[number]>>\n\t\tupdate: StrictObject<{\n\t\t\tdata: StrictObject<PartialFields<Pick<Fields & TimestampFields, CommandFields[number]>>>\n\t\t\tid: z.ZodType<GenericId<Table>, string>\n\t\t}>\n\t\tid: StrictObject<{ id: z.ZodType<GenericId<Table>, string> }>\n\t}\n}\n\n/** Defines one table and derives its storage, document, write, and DTO boundaries. */\nexport function zodTable<\n\tTable extends string,\n\tFields extends FieldSchemas,\n\tconst ServerFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly FieldKey<Fields & TimestampFields>[] = readonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<Fields, ServerFields, CommandFields, PublicFields> = {},\n): TableBoundary<Table, Fields, ServerFields, CommandFields, PublicFields> {\n\tconst columns: Fields & TimestampFields = { ...fields(zid), ...timestampFields }\n\tconst storage: z.ZodObject<Fields & TimestampFields, z.core.$strict> = z.object(columns).strict()\n\tconst schema = documentSchema(storage, tableName)\n\tconst omittedKeys: readonly (ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number])[] = [\n\t\t...TIMESTAMP_FIELDS,\n\t\t...(options.serverFields ?? []),\n\t]\n\tconst commandKeys: readonly CommandFields[number][] = options.commandFields ?? []\n\tconst publicKeys: readonly PublicFields[number][] = options.publicFields ?? []\n\tconst insertSchema: z.ZodObject<WriteFields<Fields, ServerFields>, z.core.$strict> = z\n\t\t.object(omitFields(columns, omittedKeys))\n\t\t.strict()\n\tconst updateSchema = insertSchema.partial()\n\tconst commandInput: z.ZodObject<\n\t\tPick<Fields & TimestampFields, CommandFields[number]>,\n\t\tz.core.$strict\n\t> = z.object(pickFields(columns, commandKeys)).strict()\n\tconst publicDto: z.ZodObject<\n\t\tPick<Fields & TimestampFields, PublicFields[number]>,\n\t\tz.core.$strict\n\t> = z.object(pickFields(columns, publicKeys)).strict()\n\tconst toPublicDto = (row: z.input<typeof storage>) =>\n\t\tpublicDto.parse(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.keys(publicDto.shape).map((key) => {\n\t\t\t\t\t// SAFETY: publicDto contains only keys selected from the storage fields.\n\t\t\t\t\treturn [key, row[key as keyof typeof row]]\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\n\ttype InsertSchema = typeof insertSchema\n\ttype UpdateSchema = typeof updateSchema\n\ttype InsertMask = z.util.Mask<keyof InsertSchema['shape']>\n\ttype UpdateMask = z.util.Mask<keyof UpdateSchema['shape']>\n\n\tfunction insert(): InsertSchema\n\tfunction insert<OmitMask extends InsertMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<InsertSchema, OmitMask>\n\tfunction insert(omit?: InsertMask) {\n\t\t// SAFETY: InsertMask contains only insertSchema keys; the overload retains Zod's exact mask relationship.\n\t\treturn omit ? insertSchema.omit(omit as never) : insertSchema\n\t}\n\n\tfunction update(): UpdateSchema\n\tfunction update<OmitMask extends UpdateMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<UpdateSchema, OmitMask>\n\tfunction update(omit?: UpdateMask) {\n\t\t// SAFETY: UpdateMask contains only updateSchema keys; the overload retains Zod's exact mask relationship.\n\t\treturn omit ? updateSchema.omit(omit as never) : updateSchema\n\t}\n\n\tconst table: TableDefinition<ConvexValidatorFromZod<typeof storage, 'required'>> = defineTable(\n\t\tzodToConvex(storage),\n\t)\n\treturn {\n\t\ttableName,\n\t\tschema,\n\t\tstorage,\n\t\tinsertSchema,\n\t\tupdateSchema,\n\t\tcommandInput,\n\t\tpublicDto,\n\t\ttoPublicDto,\n\t\ttable,\n\t\tinsert,\n\t\tupdate,\n\t\ttools: {\n\t\t\t// jsonSafeZid (not zid): tool masks feed generated JSON schemas, so\n\t\t\t// ids must present as plain strings while keeping the Id<...> type.\n\t\t\tinsert: commandInput,\n\t\t\tupdate: z.object({ data: commandInput.partial(), id: jsonSafeZid(tableName) }).strict(),\n\t\t\tid: z.object({ id: jsonSafeZid(tableName) }).strict(),\n\t\t},\n\t}\n}\n\nconst tenantFields = {\n\ttenant: z.string().min(1),\n} satisfies FieldSchemas\n\ntype TenantFields = typeof tenantFields\n\n/**\n * A zodTable whose rows are tenant-owned: injects the server-owned `tenant`\n * field (the tenancy boundary — see cvx-kit/tenancy). Like timestamps, it is\n * excluded from insert/update/command boundaries; handlers stamp it from\n * ctx.tenant, row-level security matches on it, and the tenantOwnership\n * trigger forbids reassigning it. Expose it via publicFields only when a DTO\n * genuinely needs it.\n */\nexport function tenantTable<\n\tTable extends string,\n\tFields extends FieldSchemas,\n\tconst ServerFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly FieldKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly FieldKey<Fields & TenantFields & TimestampFields>[] =\n\t\treadonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<\n\t\tFields & TenantFields,\n\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\tCommandFields,\n\t\tPublicFields\n\t> = {},\n) {\n\treturn zodTable<\n\t\tTable,\n\t\tFields & TenantFields,\n\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\tCommandFields,\n\t\tPublicFields\n\t>(tableName, (id) => ({ ...fields(id), ...tenantFields }), {\n\t\t...options,\n\t\tserverFields: [...(options.serverFields ?? []), 'tenant'],\n\t})\n}\n\n/**\n * Merges per-module table maps into the application schema map, rejecting\n * duplicate table names across modules — the module-registry combinator for\n * domain/table.ts: `defineSchema(createModule(catalogTables, salesTables))`.\n */\nexport function createModule<const Maps extends readonly Record<string, TableDefinition<any>>[]>(\n\t...maps: Maps\n): UnionToIntersection<Maps[number]> {\n\tconst combined: Record<string, TableDefinition<any>> = {}\n\tfor (const map of maps) {\n\t\tfor (const [tableName, definition] of Object.entries(map)) {\n\t\t\tif (tableName in combined) {\n\t\t\t\tthrow new Error(`Table \"${tableName}\" is declared by more than one module`)\n\t\t\t}\n\t\t\tcombined[tableName] = definition\n\t\t}\n\t}\n\t// SAFETY: every input entry is copied, and duplicate keys are rejected above.\n\treturn combined as UnionToIntersection<Maps[number]>\n}\n\ntype UnionToIntersection<Union> = (\n\tUnion extends unknown ? (member: Union) => void : never\n) extends (member: infer Intersection) => void\n\t? Intersection\n\t: never\n\n/**\n * Cursor-pagination boundary schemas bridged from Convex's own validators:\n * `args.paginationOpts` for function args, `result` for the page shape.\n * Pair with `include(...).paginate(opts)` (cvx-kit/auth). The result object\n * is deliberately non-strict — Convex may attach engine fields (splitCursor,\n * pageStatus); the page items themselves are validated against the dto.\n * Note: under row-level security, pages may be SHORTER than numItems when\n * rules reject rows mid-page; cursors remain correct.\n */\nexport function paginated<Dto extends z.ZodType>(dto: Dto) {\n\treturn {\n\t\targs: { paginationOpts: convexToZod(paginationOptsValidator) },\n\t\tresult: z.object({\n\t\t\tpage: z.array(dto),\n\t\t\tisDone: z.boolean(),\n\t\t\tcontinueCursor: z.string(),\n\t\t\tsplitCursor: z.string().nullable().optional(),\n\t\t\tpageStatus: z.enum(['SplitRecommended', 'SplitRequired']).nullable().optional(),\n\t\t}),\n\t}\n}\n\n/** Preserves discriminated-union storage while retaining the same table owner. */\nexport function zodVariantTable<Table extends string, Schema extends z.ZodType>(\n\ttableName: Table,\n\tstorage: Schema,\n) {\n\treturn {\n\t\ttableName,\n\t\tstorage,\n\t\tinsertSchema: storage,\n\t\ttable: defineTable(zodToConvex(storage)),\n\t}\n}\n\n/**\n * A zid that presents as a plain string in generated JSON schemas so\n * LLM-tool inputs stay primitive, while keeping the Id type at compile time.\n */\nexport function jsonSafeZid<Table extends string>(\n\ttableName: Table,\n): z.ZodType<GenericId<Table>, string> {\n\tconst id = zid(tableName)\n\treturn z\n\t\t.string()\n\t\t.refine((value): value is GenericId<Table> => id.safeParse(value).success)\n\t\t.describe(`Convex document id for table \"${tableName}\"; validated against the table by Convex`)\n}\n\ntype ZodObjectOmit<\n\tSchema extends z.ZodObject<any, any>,\n\tOmitMask extends z.util.Mask<keyof Schema['shape']>,\n> =\n\tSchema extends z.ZodObject<infer ObjectFields, infer Config>\n\t\t? z.ZodObject<\n\t\t\t\tz.util.Flatten<Omit<ObjectFields, Extract<keyof ObjectFields, keyof OmitMask>>>,\n\t\t\t\tConfig\n\t\t\t>\n\t\t: never\n\nfunction documentSchema<Fields extends FieldSchemas, Table extends string>(\n\tstorage: StrictObject<Fields>,\n\ttableName: Table,\n) {\n\treturn storage.extend({ _id: zid(tableName), _creationTime: z.number() })\n}\n\nfunction pickFields<Fields extends FieldSchemas, const Keys extends readonly FieldKey<Fields>[]>(\n\tfields: Fields,\n\tkeys: Keys,\n) {\n\t// SAFETY: each entry uses a requested key and its original validator, preserving Pick.\n\treturn Object.fromEntries(keys.map((key) => [key, fields[key]])) as Pick<Fields, Keys[number]>\n}\n\nfunction omitFields<Fields extends FieldSchemas, const Keys extends readonly FieldKey<Fields>[]>(\n\tfields: Fields,\n\tkeys: Keys,\n) {\n\tconst omitted = new Set<string>(keys)\n\t// SAFETY: filtering removes exactly the supplied keys and keeps every remaining validator.\n\treturn Object.fromEntries(Object.entries(fields).filter(([key]) => !omitted.has(key))) as Omit<\n\t\tFields,\n\t\tKeys[number]\n\t>\n}\n"],"mappings":"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"}
package/docs/oxlint.md CHANGED
@@ -27,7 +27,7 @@ Loading a plugin does not enable its rules. For example:
27
27
 
28
28
  With Vite+, place those fields under `lint` in `vite.config.ts`. The plugin
29
29
  exports a default plugin object, named `rules`, and the `RuleName` type. It is
30
- tested with Oxlint 1.79.0 and Vite+ 0.3.0.
30
+ tested with Oxlint 1.87.0 and Vite+ 1.1.0.
31
31
 
32
32
  All rules default to `convex/` relative to the linter's working directory.
33
33
  For another location, set `convexDir` on **each enabled rule**:
package/docs/upgrading.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Upgrading to 0.1.0 — the new way of things
2
2
 
3
+ ## Current dependency requirements
4
+
5
+ Install Convex 1.46 or newer and convex-helpers 0.1.127 or newer (below 0.2.0).
6
+ These minimums match workflow 0.4.10. Regenerate your lockfile after updating.
7
+ Auth declarations retain native Convex context types across package versions;
8
+ the regression fixture also checks Convex 1.45, which is below the current
9
+ full-package dependency minimum.
10
+
3
11
  ## Current command API — Effect declarations
4
12
 
5
13
  `Foundation.Command` and its separate `exec({ operation, handler })` API are
@@ -67,7 +75,7 @@ Install `convex-helpers` as a direct peer, using one compatible instance:
67
75
  bun add cvx-kit@0.1.3 convex-helpers@0.1.124
68
76
  ```
69
77
 
70
- The peer range is `>=0.1.123 <0.2.0`. Remove overrides that force a
78
+ The peer range was `>=0.1.123 <0.2.0`. Remove overrides that force a
71
79
  separate helper copy and regenerate the installation without discarding
72
80
  unrelated dependency choices. This release is validated with Convex 1.45.0,
73
81
  Zod 4.5.4, and convex-test 0.0.56.
package/docs/zod-table.md CHANGED
@@ -29,7 +29,7 @@ export const documents = zodTable(
29
29
 
30
30
  ### Shared ID validation (0.1.3)
31
31
 
32
- Install one compatible `convex-helpers` peer (`>=0.1.123 <0.2.0`) alongside
32
+ Install one compatible `convex-helpers` peer (`>=0.1.127 <0.2.0`) alongside
33
33
  cvx-kit. ID metadata lives in a helper-instance registry: copying schemas
34
34
  between separately installed helper instances can silently turn an ID into
35
35
  `v.any()`. An exact nested dependency previously caused that failure even
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cvx-kit",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Reusable Convex application kit: foundation kernels, approvals component, zod table boundaries, WorkOS-backed auth function constructors, and audited command protocol.",
5
5
  "keywords": [
6
6
  "component",
@@ -133,6 +133,7 @@
133
133
  "test:package:ids": "bun run build && node scripts/test-packed-ids.mjs",
134
134
  "test:package:oxlint": "bun run build && node scripts/test-packed-oxlint.mjs",
135
135
  "test:package:effect": "bun run build && node scripts/test-packed-effect.mjs",
136
+ "test:package:auth": "bun run build && node scripts/test-packed-auth.mjs",
136
137
  "test:package:smoke:effect": "bun run build && node scripts/smoke-packed-effect.mjs",
137
138
  "test:package:smoke:bun": "bun scripts/smoke-packed-components.mjs bun",
138
139
  "test:package:smoke:npm": "node scripts/smoke-packed-components.mjs npm",
@@ -143,28 +144,30 @@
143
144
  "release:minor": "npm version minor && git push --follow-tags"
144
145
  },
145
146
  "dependencies": {
146
- "@convex-dev/workflow": "0.4.6",
147
- "@oxlint/plugins": "1.79.0",
147
+ "@convex-dev/workflow": "0.4.10",
148
+ "@oxlint/plugins": "1.87.0",
149
+ "@standard-schema/spec": "^1.1.0",
148
150
  "convex-audit-log": "^0.2.0",
149
- "oxc-parser": "0.150.0",
151
+ "oxc-parser": "0.153.0",
150
152
  "oxc-resolver": "11.24.2"
151
153
  },
152
154
  "devDependencies": {
153
155
  "@edge-runtime/vm": "^5.0.0",
154
- "@types/node": "^26.6.1",
155
- "convex": "1.45.0",
156
- "convex-helpers": "0.1.124",
157
- "convex-test": "^0.0.56",
158
- "effect": "4.0.1",
159
- "oxlint": "1.79.0",
160
- "publint": "^0.3.24",
156
+ "@types/node": "^26.6.5",
157
+ "convex": "1.46.0",
158
+ "convex-helpers": "0.1.128",
159
+ "convex-test": "^0.0.61",
160
+ "effect": "4.0.2",
161
+ "oxlint": "1.87.0",
162
+ "publint": "^0.3.25",
161
163
  "typescript": "7.0.2",
162
- "vite-plus": "0.3.0",
163
- "zod": "4.5.4"
164
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
165
+ "vite-plus": "1.1.0",
166
+ "zod": "4.6.5"
164
167
  },
165
168
  "peerDependencies": {
166
- "convex": "^1.45.0",
167
- "convex-helpers": ">=0.1.123 <0.2.0",
169
+ "convex": "^1.46.0",
170
+ "convex-helpers": ">=0.1.127 <0.2.0",
168
171
  "effect": ">=4.0.1 <5",
169
172
  "zod": "^4.5.4"
170
173
  },
@@ -173,6 +176,9 @@
173
176
  "optional": true
174
177
  }
175
178
  },
179
+ "overrides": {
180
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.1.0"
181
+ },
176
182
  "devEngines": {
177
183
  "packageManager": {
178
184
  "name": "bun",
@@ -0,0 +1,68 @@
1
+ import { execFileSync } from 'node:child_process'
2
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
3
+ import { tmpdir } from 'node:os'
4
+ import { basename, join, resolve } from 'node:path'
5
+
6
+ const root = resolve(import.meta.dirname, '..')
7
+ const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
8
+ const temporary = mkdtempSync(join(tmpdir(), 'cvx-kit-auth-'))
9
+ function run(command, args, cwd) {
10
+ try {
11
+ return execFileSync(command, args, { cwd, encoding: 'utf8', stdio: 'pipe' })
12
+ } catch (error) {
13
+ if (!args.includes('install')) {
14
+ process.stderr.write(error.stdout ?? '')
15
+ process.stderr.write(error.stderr ?? '')
16
+ }
17
+ throw new Error(`${command} ${args.join(' ')} failed in ${basename(cwd)}`, {
18
+ cause: error.code,
19
+ })
20
+ }
21
+ }
22
+ try {
23
+ run('bun', ['pm', 'pack', '--ignore-scripts', '--destination', temporary], root)
24
+ const tarball = join(temporary, `${manifest.name}-${manifest.version}.tgz`)
25
+ // 1.45 is a declaration regression target, below the current workflow peer floor.
26
+ for (const convex of ['1.45.0', '1.46.0']) {
27
+ const fixture = mkdtempSync(join(temporary, `convex-${convex}-`))
28
+ writeFileSync(
29
+ join(fixture, 'package.json'),
30
+ JSON.stringify({
31
+ private: true,
32
+ type: 'module',
33
+ dependencies: {
34
+ 'cvx-kit': `file:${tarball}`,
35
+ convex,
36
+ 'convex-helpers': manifest.devDependencies['convex-helpers'],
37
+ effect: manifest.devDependencies.effect,
38
+ zod: manifest.devDependencies.zod,
39
+ typescript: manifest.devDependencies.typescript,
40
+ '@types/node': manifest.devDependencies['@types/node'],
41
+ },
42
+ }),
43
+ )
44
+ writeFileSync(
45
+ join(fixture, 'auth.ts'),
46
+ readFileSync(join(root, 'test/auth-native-context-types.ts')),
47
+ )
48
+ writeFileSync(
49
+ join(fixture, 'tsconfig.json'),
50
+ JSON.stringify({
51
+ compilerOptions: {
52
+ strict: true,
53
+ noEmit: true,
54
+ skipLibCheck: true,
55
+ target: 'ES2025',
56
+ module: 'ESNext',
57
+ moduleResolution: 'bundler',
58
+ },
59
+ include: ['auth.ts'],
60
+ }),
61
+ )
62
+ run('bun', ['install', '--ignore-scripts'], fixture)
63
+ run('bun', ['x', '--no-install', 'tsc', '--project', 'tsconfig.json'], fixture)
64
+ console.log(`Packed auth native contexts pass on Convex ${convex}`)
65
+ }
66
+ } finally {
67
+ rmSync(temporary, { recursive: true, force: true })
68
+ }
@@ -15,7 +15,7 @@ const root = resolve(import.meta.dirname, '..')
15
15
  const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
16
16
  const temporary = mkdtempSync(join(tmpdir(), 'cvx-kit-effect-'))
17
17
  const effectVersion = '4.0.1'
18
- const helpersVersion = '0.1.123'
18
+ const helpersVersion = '0.1.127'
19
19
  const nodeTypesVersion = '26.6.1'
20
20
  const domainGuidePath = join(root, 'src/docs/domain-operations.md')
21
21
  const domainGuideImports = new Set([
@@ -25,7 +25,7 @@ try {
25
25
  type: 'module',
26
26
  dependencies: {
27
27
  'cvx-kit': `file:${join(temporary, `cvx-kit-${manifest.version}.tgz`)}`,
28
- 'convex-helpers': '0.1.123',
28
+ 'convex-helpers': '0.1.127',
29
29
  convex: manifest.devDependencies.convex,
30
30
  zod: manifest.devDependencies.zod,
31
31
  },
@@ -142,7 +142,7 @@ it('registers concrete and generic backends from the packed package', () => {
142
142
  fixture,
143
143
  )
144
144
  console.log(
145
- `${installer}: packed IDs, table/tool types, registration, and command lifecycle pass with host convex-helpers 0.1.123`,
145
+ `${installer}: packed IDs, table/tool types, registration, and command lifecycle pass with host convex-helpers 0.1.127`,
146
146
  )
147
147
  }
148
148
  } finally {