@prisma-next/target-postgres 0.14.0 → 0.15.0-dev.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 (269) hide show
  1. package/dist/authoring-BtsOWgI3.mjs +674 -0
  2. package/dist/authoring-BtsOWgI3.mjs.map +1 -0
  3. package/dist/canonicalize-D1F4vUnP.d.mts +48 -0
  4. package/dist/canonicalize-D1F4vUnP.d.mts.map +1 -0
  5. package/dist/{codec-ids-BvytN2P8.mjs → codec-ids-B10ZZHzw.mjs} +4 -2
  6. package/dist/codec-ids-B10ZZHzw.mjs.map +1 -0
  7. package/dist/{codec-ids-CnXu9Qy3.d.mts → codec-ids-CI144z7g.d.mts} +4 -2
  8. package/dist/codec-ids-CI144z7g.d.mts.map +1 -0
  9. package/dist/codec-ids.d.mts +2 -2
  10. package/dist/codec-ids.mjs +2 -2
  11. package/dist/{codec-types-DHCkwPKE.d.mts → codec-types-CTfT4U4O.d.mts} +4 -2
  12. package/dist/codec-types-CTfT4U4O.d.mts.map +1 -0
  13. package/dist/codec-types.d.mts +1 -1
  14. package/dist/{codecs--0A5_4Bq.d.mts → codecs-D7W9O0Xx.d.mts} +134 -4
  15. package/dist/codecs-D7W9O0Xx.d.mts.map +1 -0
  16. package/dist/codecs-DRbb26Or.mjs +955 -0
  17. package/dist/codecs-DRbb26Or.mjs.map +1 -0
  18. package/dist/codecs.d.mts +2 -2
  19. package/dist/codecs.mjs +3 -757
  20. package/dist/codecs.mjs.map +1 -1
  21. package/dist/contract-free.d.mts +33 -3
  22. package/dist/contract-free.d.mts.map +1 -1
  23. package/dist/contract-free.mjs +23 -7
  24. package/dist/contract-free.mjs.map +1 -1
  25. package/dist/control-policy-DaLdbcYE.mjs +1254 -0
  26. package/dist/control-policy-DaLdbcYE.mjs.map +1 -0
  27. package/dist/control.d.mts +2 -2
  28. package/dist/control.d.mts.map +1 -1
  29. package/dist/control.mjs +880 -36
  30. package/dist/control.mjs.map +1 -1
  31. package/dist/data-transform-BOWpliq8.mjs.map +1 -1
  32. package/dist/{data-transform-DDgWdB5o.d.mts → data-transform-DPzwBMhW.d.mts} +3 -3
  33. package/dist/{data-transform-DDgWdB5o.d.mts.map → data-transform-DPzwBMhW.d.mts.map} +1 -1
  34. package/dist/data-transform.d.mts +1 -1
  35. package/dist/{ddl-QDyOSeLc.mjs → ddl-GTTOvjx_.mjs} +128 -6
  36. package/dist/ddl-GTTOvjx_.mjs.map +1 -0
  37. package/dist/ddl.d.mts +3 -2
  38. package/dist/ddl.mjs +2 -2
  39. package/dist/default-normalizer.d.mts +1 -1
  40. package/dist/default-normalizer.d.mts.map +1 -1
  41. package/dist/default-normalizer.mjs +247 -1
  42. package/dist/default-normalizer.mjs.map +1 -0
  43. package/dist/descriptor-meta-CFQKLXl_.mjs +23 -0
  44. package/dist/descriptor-meta-CFQKLXl_.mjs.map +1 -0
  45. package/dist/descriptor-meta-runtime-UwYdV8fD.mjs +12 -0
  46. package/dist/descriptor-meta-runtime-UwYdV8fD.mjs.map +1 -0
  47. package/dist/diff-database-schema-oY3brcLj.mjs +368 -0
  48. package/dist/diff-database-schema-oY3brcLj.mjs.map +1 -0
  49. package/dist/diff-database-schema.d.mts +37 -0
  50. package/dist/diff-database-schema.d.mts.map +1 -0
  51. package/dist/diff-database-schema.mjs +2 -0
  52. package/dist/errors-DO-w0EUS.mjs +35 -0
  53. package/dist/errors-DO-w0EUS.mjs.map +1 -0
  54. package/dist/errors.d.mts +11 -9
  55. package/dist/errors.d.mts.map +1 -1
  56. package/dist/errors.mjs +1 -1
  57. package/dist/issue-planner.d.mts +35 -11
  58. package/dist/issue-planner.d.mts.map +1 -1
  59. package/dist/issue-planner.mjs +2 -2
  60. package/dist/migration.d.mts +5 -5
  61. package/dist/migration.d.mts.map +1 -1
  62. package/dist/migration.mjs +4 -4
  63. package/dist/namespace-ids-xtp_ZY86.mjs +7 -0
  64. package/dist/namespace-ids-xtp_ZY86.mjs.map +1 -0
  65. package/dist/native-type-normalizer.mjs +37 -1
  66. package/dist/native-type-normalizer.mjs.map +1 -0
  67. package/dist/nodes-DJC5B8Id.mjs +192 -0
  68. package/dist/nodes-DJC5B8Id.mjs.map +1 -0
  69. package/dist/nodes-Dv5eCSq9.d.mts +170 -0
  70. package/dist/nodes-Dv5eCSq9.d.mts.map +1 -0
  71. package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-CvH0JeBW.d.mts} +119 -17
  72. package/dist/op-factory-call-CvH0JeBW.d.mts.map +1 -0
  73. package/dist/{op-factory-call-D_p5vxwt.mjs → op-factory-call-tBcLo_B8.mjs} +496 -38
  74. package/dist/op-factory-call-tBcLo_B8.mjs.map +1 -0
  75. package/dist/op-factory-call.d.mts +2 -2
  76. package/dist/op-factory-call.mjs +2 -2
  77. package/dist/pack.d.mts +321 -3
  78. package/dist/pack.d.mts.map +1 -1
  79. package/dist/pack.mjs +1 -1
  80. package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-BKlmGDJl.mjs} +17 -28
  81. package/dist/planner-ddl-builders-BKlmGDJl.mjs.map +1 -0
  82. package/dist/planner-ddl-builders.d.mts +4 -6
  83. package/dist/planner-ddl-builders.d.mts.map +1 -1
  84. package/dist/planner-ddl-builders.mjs +2 -2
  85. package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -1
  86. package/dist/planner-identity-values.d.mts +8 -2
  87. package/dist/planner-identity-values.d.mts.map +1 -1
  88. package/dist/planner-identity-values.mjs +2 -2
  89. package/dist/planner-ootbxGty.mjs +390 -0
  90. package/dist/planner-ootbxGty.mjs.map +1 -0
  91. package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-DVsaobdV.mjs} +3 -3
  92. package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-DVsaobdV.mjs.map} +1 -1
  93. package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts → planner-produced-postgres-migration-k33bb9Sm.d.mts} +4 -4
  94. package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-k33bb9Sm.d.mts.map} +1 -1
  95. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  96. package/dist/planner-produced-postgres-migration.mjs +1 -1
  97. package/dist/{planner-sql-checks-jqUUGyQR.mjs → planner-sql-checks-zqNfs2XZ.mjs} +5 -5
  98. package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-zqNfs2XZ.mjs.map} +1 -1
  99. package/dist/planner-sql-checks.d.mts +2 -2
  100. package/dist/planner-sql-checks.d.mts.map +1 -1
  101. package/dist/planner-sql-checks.mjs +1 -1
  102. package/dist/{planner-target-details-CIY6tLeo.d.mts → planner-target-details-HkP4RrRO.d.mts} +2 -2
  103. package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -0
  104. package/dist/planner-target-details.d.mts +1 -1
  105. package/dist/planner.d.mts +72 -17
  106. package/dist/planner.d.mts.map +1 -1
  107. package/dist/planner.mjs +3 -2
  108. package/dist/postgres-contract-view-C9j5cqP0.d.mts +34 -0
  109. package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +1 -0
  110. package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-view-DM8sN1Ot.mjs} +45 -41
  111. package/dist/postgres-contract-view-DM8sN1Ot.mjs.map +1 -0
  112. package/dist/postgres-database-schema-node-DJOTywwQ.d.mts +271 -0
  113. package/dist/postgres-database-schema-node-DJOTywwQ.d.mts.map +1 -0
  114. package/dist/postgres-migration-Bt3sXoID.mjs +194 -0
  115. package/dist/postgres-migration-Bt3sXoID.mjs.map +1 -0
  116. package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-ulfqRGA1.d.mts} +83 -9
  117. package/dist/postgres-migration-ulfqRGA1.d.mts.map +1 -0
  118. package/dist/postgres-native-enum-BwVNf0Ku.mjs +45 -0
  119. package/dist/postgres-native-enum-BwVNf0Ku.mjs.map +1 -0
  120. package/dist/postgres-rls-policy--CSddvMR.d.mts +52 -0
  121. package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
  122. package/dist/postgres-role-schema-node-bg32e7I-.mjs +191 -0
  123. package/dist/postgres-role-schema-node-bg32e7I-.mjs.map +1 -0
  124. package/dist/{postgres-schema-COGZ1ark.mjs → postgres-schema-Cm4DtiGA.mjs} +200 -11
  125. package/dist/postgres-schema-Cm4DtiGA.mjs.map +1 -0
  126. package/dist/postgres-schema-DKq6s1P3.d.mts +269 -0
  127. package/dist/postgres-schema-DKq6s1P3.d.mts.map +1 -0
  128. package/dist/postgres-table-schema-node-D6LvInCe.mjs +216 -0
  129. package/dist/postgres-table-schema-node-D6LvInCe.mjs.map +1 -0
  130. package/dist/render-ops.d.mts +1 -1
  131. package/dist/{render-typescript-KMgosran.mjs → render-typescript-7yqILcwr.mjs} +45 -16
  132. package/dist/render-typescript-7yqILcwr.mjs.map +1 -0
  133. package/dist/render-typescript.mjs +1 -1
  134. package/dist/resolve-ddl-schema-XyBaaenM.mjs +22 -0
  135. package/dist/resolve-ddl-schema-XyBaaenM.mjs.map +1 -0
  136. package/dist/rls-canonicalize.d.mts +27 -0
  137. package/dist/rls-canonicalize.d.mts.map +1 -0
  138. package/dist/rls-canonicalize.mjs +2 -0
  139. package/dist/runtime.d.mts +6 -8
  140. package/dist/runtime.d.mts.map +1 -1
  141. package/dist/runtime.mjs +3 -3
  142. package/dist/runtime.mjs.map +1 -1
  143. package/dist/schema-ir-annotations.d.mts +15 -0
  144. package/dist/schema-ir-annotations.d.mts.map +1 -0
  145. package/dist/schema-ir-annotations.mjs +2 -0
  146. package/dist/schema-node-kinds-CrYzCR1i.d.mts +30 -0
  147. package/dist/schema-node-kinds-CrYzCR1i.d.mts.map +1 -0
  148. package/dist/{sql-utils-DcfMz4MQ.mjs → sql-utils-SU4FDvIV.mjs} +12 -3
  149. package/dist/{sql-utils-DcfMz4MQ.mjs.map → sql-utils-SU4FDvIV.mjs.map} +1 -1
  150. package/dist/sql-utils.d.mts +8 -1
  151. package/dist/sql-utils.d.mts.map +1 -1
  152. package/dist/sql-utils.mjs +2 -2
  153. package/dist/types.d.mts +5 -140
  154. package/dist/types.mjs +5 -2
  155. package/dist/wire-name-DbQ9-2RC.mjs +104 -0
  156. package/dist/wire-name-DbQ9-2RC.mjs.map +1 -0
  157. package/package.json +26 -20
  158. package/src/contract-free/checks.ts +153 -0
  159. package/src/contract-free/control-bootstrap.ts +20 -3
  160. package/src/contract-free/ddl.ts +98 -1
  161. package/src/core/authoring.ts +726 -2
  162. package/src/core/codec-helpers.ts +33 -7
  163. package/src/core/codec-ids.ts +2 -0
  164. package/src/core/codec-type-map.ts +4 -0
  165. package/src/core/codecs.ts +236 -12
  166. package/src/core/ddl/nodes.ts +185 -2
  167. package/src/core/default-normalizer.ts +122 -2
  168. package/src/core/descriptor-meta.ts +12 -1
  169. package/src/core/entity-kinds.ts +41 -0
  170. package/src/core/errors.ts +14 -12
  171. package/src/core/migrations/bound-schema.ts +10 -0
  172. package/src/core/migrations/column-ddl-rendering.ts +117 -0
  173. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +269 -0
  174. package/src/core/migrations/control-policy.ts +218 -65
  175. package/src/core/migrations/diff-database-schema.ts +252 -0
  176. package/src/core/migrations/issue-planner.ts +782 -660
  177. package/src/core/migrations/op-factory-call.ts +415 -31
  178. package/src/core/migrations/operations/columns.ts +24 -12
  179. package/src/core/migrations/operations/constraints.ts +1 -1
  180. package/src/core/migrations/operations/data-transform.ts +6 -0
  181. package/src/core/migrations/operations/dependencies.ts +2 -1
  182. package/src/core/migrations/operations/native-enum-types.ts +62 -0
  183. package/src/core/migrations/operations/rls.ts +187 -0
  184. package/src/core/migrations/operations/shared.ts +0 -30
  185. package/src/core/migrations/planner-ddl-builders.ts +31 -59
  186. package/src/core/migrations/planner-identity-values.ts +1 -1
  187. package/src/core/migrations/planner-recipes.ts +31 -13
  188. package/src/core/migrations/planner-sql-checks.ts +1 -1
  189. package/src/core/migrations/planner-strategies.ts +250 -218
  190. package/src/core/migrations/planner-target-details.ts +3 -1
  191. package/src/core/migrations/planner.ts +358 -67
  192. package/src/core/migrations/postgres-migration.ts +178 -86
  193. package/src/core/migrations/render-typescript.ts +42 -15
  194. package/src/core/migrations/resolve-ddl-schema.ts +26 -0
  195. package/src/core/migrations/runner.ts +11 -16
  196. package/src/core/migrations/verify-postgres-namespaces.ts +45 -31
  197. package/src/core/namespace-ids.ts +3 -0
  198. package/src/core/postgres-contract-serializer.ts +46 -71
  199. package/src/core/postgres-contract-view.ts +48 -0
  200. package/src/core/postgres-native-enum.ts +61 -0
  201. package/src/core/postgres-rls-enablement.ts +34 -0
  202. package/src/core/postgres-rls-policy.ts +64 -0
  203. package/src/core/postgres-role.ts +59 -0
  204. package/src/core/postgres-schema-verifier.ts +10 -7
  205. package/src/core/postgres-schema.ts +85 -15
  206. package/src/core/postgres-validators.ts +34 -0
  207. package/src/core/psl-infer/infer-psl-contract.ts +1290 -0
  208. package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
  209. package/src/core/psl-infer/postgres-type-map.ts +129 -0
  210. package/src/core/rls/canonicalize.ts +65 -0
  211. package/src/core/rls/wire-name.ts +33 -0
  212. package/src/core/schema-ir/node-storage-coordinate.ts +26 -0
  213. package/src/core/schema-ir/postgres-database-schema-node.ts +61 -0
  214. package/src/core/schema-ir/postgres-namespace-schema-node.ts +70 -0
  215. package/src/core/schema-ir/postgres-native-enum-schema-node.ts +83 -0
  216. package/src/core/schema-ir/postgres-policy-schema-node.ts +100 -0
  217. package/src/core/schema-ir/postgres-role-schema-node.ts +65 -0
  218. package/src/core/schema-ir/postgres-table-schema-node.ts +140 -0
  219. package/src/core/schema-ir/schema-node-kinds.ts +122 -0
  220. package/src/core/sql-utils.ts +15 -2
  221. package/src/exports/codecs.ts +5 -0
  222. package/src/exports/contract-free.ts +11 -1
  223. package/src/exports/control.ts +27 -33
  224. package/src/exports/ddl.ts +8 -0
  225. package/src/exports/diff-database-schema.ts +7 -0
  226. package/src/exports/issue-planner.ts +1 -1
  227. package/src/exports/migration.ts +1 -0
  228. package/src/exports/op-factory-call.ts +9 -0
  229. package/src/exports/planner-ddl-builders.ts +0 -2
  230. package/src/exports/planner-identity-values.ts +4 -1
  231. package/src/exports/planner.ts +1 -0
  232. package/src/exports/rls-canonicalize.ts +12 -0
  233. package/src/exports/runtime.ts +1 -0
  234. package/src/exports/schema-ir-annotations.ts +1 -0
  235. package/src/exports/sql-utils.ts +1 -0
  236. package/src/exports/types.ts +38 -0
  237. package/dist/codec-ids-BvytN2P8.mjs.map +0 -1
  238. package/dist/codec-ids-CnXu9Qy3.d.mts.map +0 -1
  239. package/dist/codec-types-DHCkwPKE.d.mts.map +0 -1
  240. package/dist/codecs--0A5_4Bq.d.mts.map +0 -1
  241. package/dist/ddl-QDyOSeLc.mjs.map +0 -1
  242. package/dist/default-normalizer-DyyCHQWs.mjs +0 -131
  243. package/dist/default-normalizer-DyyCHQWs.mjs.map +0 -1
  244. package/dist/descriptor-meta-CpGygXpI.mjs +0 -140
  245. package/dist/descriptor-meta-CpGygXpI.mjs.map +0 -1
  246. package/dist/errors-CUk87ByX.mjs +0 -33
  247. package/dist/errors-CUk87ByX.mjs.map +0 -1
  248. package/dist/issue-planner-DL6g3CmE.mjs +0 -824
  249. package/dist/issue-planner-DL6g3CmE.mjs.map +0 -1
  250. package/dist/native-type-normalizer-Bc9XJzWC.mjs +0 -38
  251. package/dist/native-type-normalizer-Bc9XJzWC.mjs.map +0 -1
  252. package/dist/nodes-Bbhs2rwj.mjs +0 -76
  253. package/dist/nodes-Bbhs2rwj.mjs.map +0 -1
  254. package/dist/nodes-pLeLgdis.d.mts +0 -67
  255. package/dist/nodes-pLeLgdis.d.mts.map +0 -1
  256. package/dist/op-factory-call-D_p5vxwt.mjs.map +0 -1
  257. package/dist/op-factory-call-DmQEc3XV.d.mts.map +0 -1
  258. package/dist/planner-Bs_baQax.mjs +0 -324
  259. package/dist/planner-Bs_baQax.mjs.map +0 -1
  260. package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
  261. package/dist/planner-target-details-CIY6tLeo.d.mts.map +0 -1
  262. package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +0 -1
  263. package/dist/postgres-migration-B5jKrXv3.mjs +0 -145
  264. package/dist/postgres-migration-B5jKrXv3.mjs.map +0 -1
  265. package/dist/postgres-migration-Y4YBJqkS.d.mts.map +0 -1
  266. package/dist/postgres-schema-COGZ1ark.mjs.map +0 -1
  267. package/dist/render-typescript-KMgosran.mjs.map +0 -1
  268. package/dist/types.d.mts.map +0 -1
  269. package/src/core/postgres-enum-type-schema.ts +0 -17
@@ -9,7 +9,9 @@ export type OperationClass =
9
9
  | 'unique'
10
10
  | 'index'
11
11
  | 'foreignKey'
12
- | 'checkConstraint';
12
+ | 'checkConstraint'
13
+ | 'rlsPolicy'
14
+ | 'rowLevelSecurity';
13
15
 
14
16
  export interface PostgresPlanTargetDetails {
15
17
  readonly schema: string;
@@ -13,28 +13,44 @@ import {
13
13
  plannerFailure,
14
14
  } from '@prisma-next/family-sql/control';
15
15
  import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
16
- import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
17
16
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
18
17
  import type {
19
18
  MigrationPlanner,
20
19
  MigrationPlanWithAuthoringSurface,
21
20
  MigrationScaffoldContext,
22
- SchemaIssue,
21
+ SchemaDiffIssue,
22
+ SchemaOwnership,
23
23
  } from '@prisma-next/framework-components/control';
24
+ import { issueOutcome } from '@prisma-next/framework-components/control';
24
25
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
26
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
27
+ import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
25
28
  import { blindCast } from '@prisma-next/utils/casts';
26
- import { parsePostgresDefault } from '../default-normalizer';
27
- import { normalizeSchemaNativeType } from '../native-type-normalizer';
29
+ import { ifDefined } from '@prisma-next/utils/defined';
30
+ import { PostgresRlsPolicy } from '../postgres-rls-policy';
31
+ import { parseRlsPolicyWireName } from '../rls/wire-name';
32
+ import { postgresNodeStorageCoordinate } from '../schema-ir/node-storage-coordinate';
33
+ import { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
34
+ import { PostgresPolicySchemaNode } from '../schema-ir/postgres-policy-schema-node';
35
+ import type { SqlSchemaDiffNode } from '../schema-ir/schema-node-kinds';
28
36
  import {
29
- formatPostgresControlPolicySubjectLabel,
37
+ renderPostgresSuppression,
38
+ resolveNamespaceIdForDdlSchema,
30
39
  resolvePostgresCallControlPolicySubject,
31
- resolvePostgresIssueControlPolicySubject,
32
- resolvePostgresIssueCreationFactoryName,
40
+ resolvePostgresNodeIssueControlPolicySubject,
41
+ resolvePostgresNodeIssueCreationFactoryName,
33
42
  } from './control-policy';
34
- import { planIssues } from './issue-planner';
43
+ import { buildPostgresPlanDiff } from './diff-database-schema';
44
+ import { coalesceSubtreeIssues, issueNode, issueSchemaName, planIssues } from './issue-planner';
35
45
  import type { PostgresOpFactoryCall } from './op-factory-call';
46
+ import {
47
+ CreatePostgresRlsPolicyCall,
48
+ DropPostgresRlsPolicyCall,
49
+ RenamePostgresRlsPolicyCall,
50
+ } from './op-factory-call';
36
51
  import { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';
37
52
  import { postgresPlannerStrategies } from './planner-strategies';
53
+ import { resolveDdlSchemaForNamespaceStorage } from './resolve-ddl-schema';
38
54
  import { verifyPostgresNamespacePresence } from './verify-postgres-namespaces';
39
55
 
40
56
  type PlannerFrameworkComponents = SqlMigrationPlannerPlanOptions extends {
@@ -47,10 +63,6 @@ type PlannerOptionsWithComponents = SqlMigrationPlannerPlanOptions & {
47
63
  readonly frameworkComponents: PlannerFrameworkComponents;
48
64
  };
49
65
 
50
- type VerifySqlSchemaOptionsWithComponents = Parameters<typeof verifySqlSchema>[0] & {
51
- readonly frameworkComponents: PlannerFrameworkComponents;
52
- };
53
-
54
66
  export function createPostgresMigrationPlanner(
55
67
  lowerer: ExecuteRequestLowerer,
56
68
  ): PostgresMigrationPlanner {
@@ -75,18 +87,20 @@ export type PostgresPlanResult =
75
87
  /**
76
88
  * Postgres migration planner — a thin wrapper over `planIssues`.
77
89
  *
78
- * `plan()` verifies the live schema against the target contract (producing
79
- * `SchemaIssue[]`) and delegates to `planIssues` with the unified
80
- * `postgresPlannerStrategies` list: enum-change, NOT-NULL backfill,
81
- * type-change, nullable-tightening, codec-hook storage types,
82
- * component-declared dependency installs, and shared-temp-default /
83
- * empty-table-guarded NOT-NULL add-column. The same strategy list runs for
84
- * `migration plan`, `db update`, and `db init`; behavior diverges purely on
85
- * `policy.allowedOperationClasses` (the data-safe strategies short-circuit
86
- * when `'data'` is excluded). The issue planner applies operation-class
87
- * policy gates and emits a single `PostgresOpFactoryCall[]` that drives both
88
- * the runtime-ops view (via `renderOps`) and the `renderTypeScript()`
89
- * authoring surface.
90
+ * `plan()` diffs the target contract against the live schema via the one
91
+ * differ (`buildPostgresPlanDiff`, producing node-typed `SchemaDiffIssue[]`)
92
+ * and delegates to `planIssues` with the unified `postgresPlannerStrategies`
93
+ * list: NOT-NULL backfill, type-change, nullable-tightening, codec-hook
94
+ * storage types, component-declared dependency installs, and
95
+ * shared-temp-default / empty-table-guarded NOT-NULL add-column. The same
96
+ * strategy list runs for `migration plan`, `db update`, and `db init`;
97
+ * behavior diverges purely on `policy.allowedOperationClasses` (the
98
+ * data-safe strategies short-circuit when `'data'` is excluded). The issue
99
+ * planner applies operation-class policy gates and emits a single
100
+ * `PostgresOpFactoryCall[]` that drives both the runtime-ops view (via
101
+ * `renderOps`) and the `renderTypeScript()` authoring surface. RLS policy
102
+ * drift (the structural half of the same one-differ tree) is handled
103
+ * separately via `planPostgresSchemaDiff`.
90
104
  */
91
105
  export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgres'> {
92
106
  readonly #lowerer: ExecuteRequestLowerer | undefined;
@@ -122,6 +136,11 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
122
136
  * the marker row by the right space.
123
137
  */
124
138
  readonly spaceId: string;
139
+ /**
140
+ * Ownership oracle over the contract-space composition — see
141
+ * {@link SqlMigrationPlannerPlanOptions.ownership}.
142
+ */
143
+ readonly ownership?: SchemaOwnership;
125
144
  }): PostgresPlanResult {
126
145
  return this.planSql(options as SqlMigrationPlannerPlanOptions);
127
146
  }
@@ -151,9 +170,73 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
151
170
  return policyResult;
152
171
  }
153
172
 
154
- const schemaIssues = this.collectSchemaIssues(options);
173
+ // The one combined tree diff drives the whole plan: relational findings
174
+ // become structural DDL via `planIssues`, policy findings become RLS ops
175
+ // via `planPostgresSchemaDiff`. Verify runs its own full-tree node diff
176
+ // (`diffSchema`) over the same schema and rejects on a
177
+ // surviving failure.
178
+ PostgresDatabaseSchemaNode.assert(options.schema);
179
+ const { issues: rawIssues } = buildPostgresPlanDiff({
180
+ contract: options.contract,
181
+ actualSchema: options.schema,
182
+ frameworkComponents: options.frameworkComponents,
183
+ });
184
+ const policyDiffIssues = rawIssues.filter((issue) => isPolicyDiffIssue(issue));
185
+ // Role diff issues resolve to the `external` control policy (see
186
+ // `resolvePostgresNodeIssueControlPolicySubject`'s role branch), so the
187
+ // control-policy partition below suppresses them to zero ops on its own,
188
+ // before `mapNodeIssueToCall` ever sees them — no separate exclusion
189
+ // needed here.
190
+ const relationalDiffIssues = rawIssues.filter((issue) => !isPolicyDiffIssue(issue));
191
+
192
+ // The generic differ is total and un-gated: strict-mode extras filtering
193
+ // (dropping `not-expected` findings outside strict mode, mirroring the
194
+ // retired coordinate walk's `if (strict) { ...extra_* } }` guards),
195
+ // subtree coalescing (a missing/extra table also emits an issue for
196
+ // every child under it — redundant once the table-level Create/Drop call
197
+ // already accounts for the whole subtree), and ownership are all post-diff
198
+ // planner steps.
199
+ //
200
+ // Ownership: a live extra is only this plan's to drop when no contract
201
+ // space owns it. The differ ran against THIS space's contract, so a table
202
+ // a sibling space owns surfaces here as `not-expected`; the planner asks
203
+ // the ownership oracle (the passive aggregate) whether any space declares
204
+ // it and, if so, leaves it alone — it is a sibling's table, not an orphan.
205
+ // A table no space owns stays a genuine extra to drop under a destructive
206
+ // policy. Ownership lives in the aggregate; the planner only asks. Absent
207
+ // oracle (single-space, none handed) keeps every extra. Coalescing MUST
208
+ // run before this so a sibling-owned table's child issues have already
209
+ // collapsed into the one table-level issue that carries the table name.
210
+ const strict =
211
+ options.policy.allowedOperationClasses.includes('widening') ||
212
+ options.policy.allowedOperationClasses.includes('destructive');
213
+ const coalesced = coalesceSubtreeIssues(relationalDiffIssues);
214
+ const owned = retainUnownedExtras(coalesced, options.ownership, options.contract);
215
+ const gated = strict ? owned : owned.filter((issue) => issueOutcome(issue) !== 'not-expected');
216
+
217
+ // Namespace presence (`CREATE SCHEMA`) is a planner-only op-generation
218
+ // concern stitched in here rather than inside the shared diff — verify
219
+ // never needs it (a missing schema already surfaces as a `not-found`
220
+ // table in the relational findings). These synthesized issues are added
221
+ // AFTER coalescing/scoping (never coalesced against the table diff —
222
+ // their path is an ancestor of every table path under that schema, so
223
+ // running them through the same coalesce would swallow the table-level
224
+ // `not-found` issues that drive `CREATE TABLE`) and are NOT subject to
225
+ // sibling-space scoping, matching the retired coordinate walk exactly.
226
+ const namespaceIssues = verifyPostgresNamespacePresence({
227
+ contract: options.contract,
228
+ schema: options.schema,
229
+ });
230
+ const schemaIssues = [...namespaceIssues, ...gated];
231
+
155
232
  const codecHooks = extractCodecControlHooks(options.frameworkComponents);
156
233
  const storageTypes = options.contract.storage.types ?? {};
234
+ // The strategy layer reads the live schema by bare table name for existence
235
+ // checks (shared-temp-default safety, FK/unique probes), so it takes one
236
+ // per-schema namespace node — never the whole tree root, and never a flat
237
+ // merge of every namespace (which would collide same-named tables across
238
+ // schemas). Probing more than one namespace at once is future work.
239
+ const relationalSchema = relationalNamespaceNode(options.schema, schemaName);
157
240
 
158
241
  // Input-side control-policy partition. `external` / `observed` subjects
159
242
  // — and non-creation issues for `tolerated` subjects — are dropped from
@@ -166,10 +249,8 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
166
249
  issues: schemaIssues,
167
250
  contract: options.contract,
168
251
  resolveControlPolicySubject: (issue) =>
169
- resolvePostgresIssueControlPolicySubject(issue, options.contract),
170
- resolveCreationFactoryName: resolvePostgresIssueCreationFactoryName,
171
- formatSubjectLabel: (factoryName, subject) =>
172
- formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),
252
+ resolvePostgresNodeIssueControlPolicySubject(issue, options.contract),
253
+ resolveCreationFactoryName: resolvePostgresNodeIssueCreationFactoryName,
173
254
  });
174
255
 
175
256
  const result = planIssues({
@@ -179,12 +260,12 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
179
260
  // `db update` / `db init`, which means data-safety strategies needing
180
261
  // from/to comparisons (unsafe type change, nullable tightening) are
181
262
  // inapplicable there — reconciliation falls through to
182
- // `mapIssueToCall`'s direct destructive handlers.
263
+ // `mapNodeIssueToCall`'s direct destructive handlers.
183
264
  fromContract: options.fromContract,
184
265
  schemaName,
185
266
  codecHooks,
186
267
  storageTypes,
187
- schema: options.schema,
268
+ ...ifDefined('schema', relationalSchema),
188
269
  policy: options.policy,
189
270
  frameworkComponents: options.frameworkComponents,
190
271
  strategies: postgresPlannerStrategies,
@@ -194,6 +275,15 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
194
275
  return plannerFailure(result.failure);
195
276
  }
196
277
 
278
+ const schemaDiffCalls = this.planPostgresSchemaDiff(options, policyDiffIssues);
279
+ const schemaDiffPartition = partitionCallsByControlPolicy({
280
+ calls: schemaDiffCalls,
281
+ contract: options.contract,
282
+ resolveControlPolicySubject: (call) =>
283
+ resolvePostgresCallControlPolicySubject(call, options.contract),
284
+ resolveFactoryName: (call) => call.factoryName,
285
+ });
286
+
197
287
  // Inline `onFieldEvent`-emitted ops after structural DDL. The fixed
198
288
  // ordering is `structural → added → dropped → altered`, with
199
289
  // within-group sorting by `(tableName, fieldName)` so re-emits are
@@ -216,14 +306,13 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
216
306
  resolveControlPolicySubject: (call) =>
217
307
  resolvePostgresCallControlPolicySubject(call, options.contract),
218
308
  resolveFactoryName: (call) => call.factoryName,
219
- formatSubjectLabel: (factoryName, subject) =>
220
- formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),
221
309
  });
222
- const calls = [...result.value.calls, ...fieldEventPartition.kept];
310
+ const calls = [...result.value.calls, ...schemaDiffPartition.kept, ...fieldEventPartition.kept];
223
311
  const warnings: SqlPlannerConflict[] = [
224
- ...issuePartition.warnings,
225
- ...fieldEventPartition.warnings,
226
- ];
312
+ ...issuePartition.suppressions,
313
+ ...schemaDiffPartition.suppressions,
314
+ ...fieldEventPartition.suppressions,
315
+ ].map((record) => renderPostgresSuppression(record, options.contract));
227
316
 
228
317
  return Object.freeze({
229
318
  kind: 'success' as const,
@@ -240,6 +329,142 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
240
329
  });
241
330
  }
242
331
 
332
+ /**
333
+ * Maps the RLS policy presence findings of the one combined tree diff
334
+ * (`buildPostgresPlanDiff`, already ownership-filtered) into
335
+ * `CREATE POLICY` / `DROP POLICY` / `ALTER POLICY … RENAME TO` ops. It
336
+ * does not re-diff — it consumes exactly the policy-node subset of the
337
+ * shared diff's issues. Enablement is NOT decided here: `ENABLE`/`DISABLE
338
+ * ROW LEVEL SECURITY` derive from the table's marker-driven `rlsEnabled`
339
+ * attribute diff on the relational side.
340
+ *
341
+ * Rename post-pass: a `not-found` and a `not-expected` policy on the SAME
342
+ * table whose wire-name content hashes match but prefixes differ are one
343
+ * prefix-only rename, collapsed into a single non-destructive
344
+ * `RenamePostgresRlsPolicyCall`. Multi-candidate hash groups pair
345
+ * deterministically by sorted wire name; leftovers proceed as
346
+ * create/drop. Unparseable wire names never pair.
347
+ *
348
+ * The pairing runs only when the policy allows `widening` (rename's
349
+ * class). Without it (db-init's additive-only set), pairing degrades
350
+ * deliberately to the additive half: the new name is CREATEd and the old
351
+ * policy survives live until a widening/destructive-allowed plan runs —
352
+ * emitting an ungated widening rename would only fail at the runner's
353
+ * class re-enforcement.
354
+ */
355
+ private planPostgresSchemaDiff(
356
+ options: PlannerOptionsWithComponents,
357
+ filteredDiffIssues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[],
358
+ ): readonly PostgresOpFactoryCall[] {
359
+ const allowsDestructive = options.policy.allowedOperationClasses.includes('destructive');
360
+ const allowsWidening = options.policy.allowedOperationClasses.includes('widening');
361
+
362
+ interface PolicyFinding {
363
+ readonly node: PostgresPolicySchemaNode;
364
+ readonly schemaForTable: string;
365
+ }
366
+ const missing: PolicyFinding[] = [];
367
+ const extra: PolicyFinding[] = [];
368
+
369
+ for (const issue of filteredDiffIssues) {
370
+ // 'not-equal' is unreachable for content-addressed policies: the wire name
371
+ // encodes the body hash, so two policies sharing a local key (same name)
372
+ // are always equal and isEqualTo never returns false.
373
+ if (issueOutcome(issue) === 'not-found') {
374
+ const expected = issue.expected;
375
+ PostgresPolicySchemaNode.assert(expected);
376
+ // expected.namespaceId is the DDL schema name (resolved during projection);
377
+ // this re-resolution is a no-op as long as PostgresSchema.ddlSchemaName() returns this.id.
378
+ missing.push({
379
+ node: expected,
380
+ schemaForTable: resolveDdlSchemaForNamespaceStorage(
381
+ options.contract.storage,
382
+ expected.namespaceId,
383
+ ),
384
+ });
385
+ } else if (issueOutcome(issue) === 'not-expected') {
386
+ const actual = issue.actual;
387
+ PostgresPolicySchemaNode.assert(actual);
388
+ extra.push({
389
+ node: actual,
390
+ schemaForTable: resolveDdlSchemaForNamespaceStorage(
391
+ options.contract.storage,
392
+ actual.namespaceId,
393
+ ),
394
+ });
395
+ }
396
+ }
397
+
398
+ const calls: PostgresOpFactoryCall[] = [];
399
+ const renamedExtras = new Set<PolicyFinding>();
400
+ const renamedMissing = new Set<PolicyFinding>();
401
+ const pairingKey = (finding: PolicyFinding, hash: string): string =>
402
+ JSON.stringify([finding.schemaForTable, finding.node.tableName, hash]);
403
+
404
+ if (allowsWidening) {
405
+ const extrasByGroup = new Map<string, PolicyFinding[]>();
406
+ for (const finding of extra) {
407
+ const parsed = parseRlsPolicyWireName(finding.node.name);
408
+ if (parsed === undefined) continue;
409
+ const key = pairingKey(finding, parsed.hash);
410
+ const group = extrasByGroup.get(key) ?? [];
411
+ group.push(finding);
412
+ extrasByGroup.set(key, group);
413
+ }
414
+ for (const group of extrasByGroup.values()) {
415
+ group.sort((a, b) => (a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0));
416
+ }
417
+
418
+ const sortedMissing = [...missing].sort((a, b) =>
419
+ a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0,
420
+ );
421
+ for (const missingFinding of sortedMissing) {
422
+ const parsed = parseRlsPolicyWireName(missingFinding.node.name);
423
+ if (parsed === undefined) continue;
424
+ const candidates = extrasByGroup.get(pairingKey(missingFinding, parsed.hash));
425
+ const candidate = candidates?.shift();
426
+ if (candidate === undefined) continue;
427
+ // Same name would never surface as missing+extra (the differ pairs by
428
+ // name), so a matched candidate always differs in prefix only.
429
+ renamedExtras.add(candidate);
430
+ renamedMissing.add(missingFinding);
431
+ calls.push(
432
+ new RenamePostgresRlsPolicyCall(
433
+ missingFinding.schemaForTable,
434
+ missingFinding.node.tableName,
435
+ candidate.node.name,
436
+ missingFinding.node.name,
437
+ ),
438
+ );
439
+ }
440
+ }
441
+
442
+ for (const finding of missing) {
443
+ if (renamedMissing.has(finding)) continue;
444
+ calls.push(
445
+ new CreatePostgresRlsPolicyCall(
446
+ finding.schemaForTable,
447
+ finding.node.tableName,
448
+ policyNodeToContractPolicy(finding.node),
449
+ ),
450
+ );
451
+ }
452
+ if (allowsDestructive) {
453
+ for (const finding of extra) {
454
+ if (renamedExtras.has(finding)) continue;
455
+ calls.push(
456
+ new DropPostgresRlsPolicyCall(
457
+ finding.schemaForTable,
458
+ finding.node.tableName,
459
+ finding.node.name,
460
+ ),
461
+ );
462
+ }
463
+ }
464
+
465
+ return calls;
466
+ }
467
+
243
468
  private ensureAdditivePolicy(policy: MigrationOperationPolicy) {
244
469
  if (!policy.allowedOperationClasses.includes('additive')) {
245
470
  return plannerFailure([
@@ -252,35 +477,101 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
252
477
  }
253
478
  return null;
254
479
  }
480
+ }
255
481
 
256
- private collectSchemaIssues(options: PlannerOptionsWithComponents): readonly SchemaIssue[] {
257
- // `db init` uses additive-only policy and intentionally ignores extra
258
- // schema objects. Any reconciliation-capable policy (widening or
259
- // destructive) must inspect extras to reconcile strict equality.
260
- const allowed = options.policy.allowedOperationClasses;
261
- const strict = allowed.includes('widening') || allowed.includes('destructive');
262
- const verifyOptions: VerifySqlSchemaOptionsWithComponents = {
263
- contract: options.contract,
264
- schema: options.schema,
265
- strict,
266
- typeMetadataRegistry: new Map(),
267
- frameworkComponents: options.frameworkComponents,
268
- normalizeDefault: parsePostgresDefault,
269
- normalizeNativeType: normalizeSchemaNativeType,
270
- };
271
- const verifyResult = verifySqlSchema(verifyOptions);
272
- // Schema presence is a Postgres-specific concern (no equivalent in
273
- // SQLite / Mongo), so the issue emission lives in the target layer
274
- // rather than in the family verifier. Stitch it in here so a single
275
- // `SchemaIssue[]` flows through `planIssues` and the planner emits
276
- // CREATE SCHEMA in the dep bucket before any CreateTableCall.
277
- const namespaceIssues = verifyPostgresNamespacePresence({
278
- contract: options.contract,
279
- schema: options.schema,
280
- });
281
- if (namespaceIssues.length === 0) {
282
- return verifyResult.schema.issues;
283
- }
284
- return [...namespaceIssues, ...verifyResult.schema.issues];
285
- }
482
+ /**
483
+ * A diff issue whose node is an RLS policy the structural half of the one
484
+ * combined tree diff, routed to `planPostgresSchemaDiff` instead of
485
+ * `planIssues`.
486
+ */
487
+ function isPolicyDiffIssue(issue: SchemaDiffIssue<SqlSchemaDiffNode>): boolean {
488
+ const node = issue.expected ?? issue.actual;
489
+ return node !== undefined && PostgresPolicySchemaNode.is(node);
490
+ }
491
+
492
+ /**
493
+ * Drops a `not-expected` issue when it is a whole extra storage entity (a table
494
+ * or a native enum) that some space in the composition owns. Each such node
495
+ * yields its own storage coordinate (see {@link postgresNodeStorageCoordinate}),
496
+ * so `declaresEntity` answers over the whole composition on one uniform
497
+ * coordinate: a positive answer means a sibling owns this entity here — leave
498
+ * it; a negative answer means a genuine orphan — drop it. A node with no storage
499
+ * coordinate (a namespace, or a deeper column/constraint drift on an owned
500
+ * table) is this space's own and is always kept. No oracle ⇒ keep everything.
501
+ */
502
+ function retainUnownedExtras(
503
+ issues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[],
504
+ ownership: SchemaOwnership | undefined,
505
+ contract: Contract<SqlStorage>,
506
+ ): readonly SchemaDiffIssue<SqlSchemaDiffNode>[] {
507
+ if (ownership === undefined) return issues;
508
+ return issues.filter((issue) => {
509
+ if (issueOutcome(issue) !== 'not-expected') return true;
510
+ const node = issueNode(issue);
511
+ if (node === undefined) return true;
512
+ const coordinate = postgresNodeStorageCoordinate(node);
513
+ if (coordinate === undefined) return true;
514
+ const ddlSchemaName = issueSchemaName(issue);
515
+ if (ddlSchemaName === undefined) return true;
516
+ const namespaceId = resolveNamespaceIdForDdlSchema(contract, ddlSchemaName);
517
+ return !ownership.declaresEntity({ namespaceId, ...coordinate });
518
+ });
519
+ }
520
+
521
+ /**
522
+ * Returns the one namespace node the relational strategy layer probes for
523
+ * live-table existence and reads codec-hook context off — the namespace
524
+ * matching the planner's resolved schema name, or the first namespace when
525
+ * none matches. `undefined` when the tree has no namespaces, so the strategy
526
+ * context uses its empty-schema default.
527
+ *
528
+ * The relational strategies key tables by bare name, so they can only probe one
529
+ * namespace at a time; probing across every namespace at once is future work.
530
+ *
531
+ * Returns the real `PostgresNamespaceSchemaNode` reference rather than a
532
+ * projection: `storageTypePlanCallStrategy` hands this same value to codec
533
+ * `planTypeOperations` hooks as `schema`, and hooks read the Postgres-specific
534
+ * `nativeEnums` field off it (via `PostgresNamespaceSchemaNode.is`) to decide
535
+ * whether a native enum type already exists — a projection that only copies
536
+ * `tables` would silently drop that signal. `StrategyContext.schema`'s
537
+ * declared type (`SqlSchemaIR`, shared with SQLite's flat shape) doesn't
538
+ * capture this, so the return is `blindCast` the same way `namespaceSchemaNodes`
539
+ * in the family's relational walk already treats a namespace node as a
540
+ * structurally-`SqlSchemaIR`-shaped value.
541
+ */
542
+ function relationalNamespaceNode(
543
+ schema: PostgresDatabaseSchemaNode,
544
+ schemaName: string,
545
+ ): SqlSchemaIR | undefined {
546
+ const namespaceNodes = Object.values(schema.namespaces);
547
+ const namespaceNode =
548
+ namespaceNodes.find((node) => node.schemaName === schemaName) ?? namespaceNodes[0];
549
+ if (namespaceNode === undefined) return undefined;
550
+ return blindCast<
551
+ SqlSchemaIR,
552
+ 'PostgresNamespaceSchemaNode carries tables (+ nativeEnums, read by codec hooks via PostgresNamespaceSchemaNode.is) structurally compatible with the SqlSchemaIR shape the strategy layer declares'
553
+ >(namespaceNode);
554
+ }
555
+
556
+ /**
557
+ * Rebuilds the `PostgresRlsPolicy` contract entity `CreatePostgresRlsPolicyCall`
558
+ * carries (its `renderTypeScript`/`createRlsPolicy` paths serialize the whole
559
+ * entity, `namespaceId` included). This reconstructs rather than looking the
560
+ * original up in the contract on purpose: the diff node's `namespaceId` is the
561
+ * *resolved DDL schema* (set when the expected tree was built), which is the
562
+ * value the emitted op must carry; the contract-stored entity holds the raw,
563
+ * pre-resolution coordinate, so a lookup would change the migration output.
564
+ */
565
+ function policyNodeToContractPolicy(node: PostgresPolicySchemaNode): PostgresRlsPolicy {
566
+ return new PostgresRlsPolicy({
567
+ name: node.name,
568
+ prefix: node.prefix,
569
+ tableName: node.tableName,
570
+ namespaceId: node.namespaceId,
571
+ operation: node.operation,
572
+ roles: [...node.roles],
573
+ ...ifDefined('using', node.using),
574
+ ...ifDefined('withCheck', node.withCheck),
575
+ permissive: node.permissive,
576
+ });
286
577
  }