primitive-admin 1.0.61 → 1.0.63

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 (320) hide show
  1. package/README.md +125 -69
  2. package/assets/skill/skills/primitive-platform/SKILL.md +336 -37
  3. package/dist/bin/primitive.js +25 -14
  4. package/dist/bin/primitive.js.map +1 -1
  5. package/dist/src/commands/analytics.js +36 -16
  6. package/dist/src/commands/analytics.js.map +1 -1
  7. package/dist/src/commands/apps.js +52 -188
  8. package/dist/src/commands/apps.js.map +1 -1
  9. package/dist/src/commands/blob-buckets.js +13 -153
  10. package/dist/src/commands/blob-buckets.js.map +1 -1
  11. package/dist/src/commands/collection-type-configs.js +14 -100
  12. package/dist/src/commands/collection-type-configs.js.map +1 -1
  13. package/dist/src/commands/collections.js +47 -8
  14. package/dist/src/commands/collections.js.map +1 -1
  15. package/dist/src/commands/config.d.ts +46 -0
  16. package/dist/src/commands/config.js +479 -0
  17. package/dist/src/commands/config.js.map +1 -0
  18. package/dist/src/commands/connections.js +12 -7
  19. package/dist/src/commands/connections.js.map +1 -1
  20. package/dist/src/commands/cron-triggers.js +84 -176
  21. package/dist/src/commands/cron-triggers.js.map +1 -1
  22. package/dist/src/commands/database-type-configs.d.ts +2 -0
  23. package/dist/src/commands/database-type-configs.js +171 -0
  24. package/dist/src/commands/database-type-configs.js.map +1 -0
  25. package/dist/src/commands/database-types.js +4 -4
  26. package/dist/src/commands/database-types.js.map +1 -1
  27. package/dist/src/commands/databases.d.ts +63 -0
  28. package/dist/src/commands/databases.js +649 -87
  29. package/dist/src/commands/databases.js.map +1 -1
  30. package/dist/src/commands/documents.js +869 -12
  31. package/dist/src/commands/documents.js.map +1 -1
  32. package/dist/src/commands/email-templates.js +21 -128
  33. package/dist/src/commands/email-templates.js.map +1 -1
  34. package/dist/src/commands/env.d.ts +12 -1
  35. package/dist/src/commands/env.js +115 -26
  36. package/dist/src/commands/env.js.map +1 -1
  37. package/dist/src/commands/feature-flags.d.ts +14 -0
  38. package/dist/src/commands/feature-flags.js +116 -0
  39. package/dist/src/commands/feature-flags.js.map +1 -0
  40. package/dist/src/commands/group-type-configs.js +14 -109
  41. package/dist/src/commands/group-type-configs.js.map +1 -1
  42. package/dist/src/commands/init.d.ts +24 -1
  43. package/dist/src/commands/init.js +1194 -279
  44. package/dist/src/commands/init.js.map +1 -1
  45. package/dist/src/commands/integrations.js +166 -435
  46. package/dist/src/commands/integrations.js.map +1 -1
  47. package/dist/src/commands/metadata-category-configs.d.ts +12 -0
  48. package/dist/src/commands/metadata-category-configs.js +112 -0
  49. package/dist/src/commands/metadata-category-configs.js.map +1 -0
  50. package/dist/src/commands/metadata.js +33 -141
  51. package/dist/src/commands/metadata.js.map +1 -1
  52. package/dist/src/commands/prompts.js +206 -563
  53. package/dist/src/commands/prompts.js.map +1 -1
  54. package/dist/src/commands/rule-sets.d.ts +1 -1
  55. package/dist/src/commands/rule-sets.js +16 -132
  56. package/dist/src/commands/rule-sets.js.map +1 -1
  57. package/dist/src/commands/scripts.d.ts +10 -0
  58. package/dist/src/commands/scripts.js +203 -241
  59. package/dist/src/commands/scripts.js.map +1 -1
  60. package/dist/src/commands/sessions.js +17 -5
  61. package/dist/src/commands/sessions.js.map +1 -1
  62. package/dist/src/commands/sync-app-settings.d.ts +113 -70
  63. package/dist/src/commands/sync-app-settings.js +193 -242
  64. package/dist/src/commands/sync-app-settings.js.map +1 -1
  65. package/dist/src/commands/sync.d.ts +1904 -51
  66. package/dist/src/commands/sync.js +9468 -1790
  67. package/dist/src/commands/sync.js.map +1 -1
  68. package/dist/src/commands/tokens.js +8 -2
  69. package/dist/src/commands/tokens.js.map +1 -1
  70. package/dist/src/commands/users.js +92 -1
  71. package/dist/src/commands/users.js.map +1 -1
  72. package/dist/src/commands/vars.js +44 -58
  73. package/dist/src/commands/vars.js.map +1 -1
  74. package/dist/src/commands/webhooks.js +318 -156
  75. package/dist/src/commands/webhooks.js.map +1 -1
  76. package/dist/src/commands/workflows.d.ts +49 -12
  77. package/dist/src/commands/workflows.js +652 -1086
  78. package/dist/src/commands/workflows.js.map +1 -1
  79. package/dist/src/lib/access-rule-display.d.ts +21 -0
  80. package/dist/src/lib/access-rule-display.js +34 -0
  81. package/dist/src/lib/access-rule-display.js.map +1 -0
  82. package/dist/src/lib/api-client.d.ts +541 -61
  83. package/dist/src/lib/api-client.js +559 -102
  84. package/dist/src/lib/api-client.js.map +1 -1
  85. package/dist/src/lib/app-settings-descriptor.d.ts +180 -25
  86. package/dist/src/lib/app-settings-descriptor.js +403 -82
  87. package/dist/src/lib/app-settings-descriptor.js.map +1 -1
  88. package/dist/src/lib/canonical-json.d.ts +12 -0
  89. package/dist/src/lib/canonical-json.js +35 -0
  90. package/dist/src/lib/canonical-json.js.map +1 -0
  91. package/dist/src/lib/cli-manifest.d.ts +8 -0
  92. package/dist/src/lib/cli-manifest.js +1 -0
  93. package/dist/src/lib/cli-manifest.js.map +1 -1
  94. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +6 -2
  95. package/dist/src/lib/codegen-shared/generatedFiles.js +9 -2
  96. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
  97. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +6 -6
  98. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +8 -8
  99. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -1
  100. package/dist/src/lib/config-object-descriptor.d.ts +127 -0
  101. package/dist/src/lib/config-object-descriptor.js +658 -0
  102. package/dist/src/lib/config-object-descriptor.js.map +1 -0
  103. package/dist/src/lib/config-payload.d.ts +85 -0
  104. package/dist/src/lib/config-payload.js +116 -0
  105. package/dist/src/lib/config-payload.js.map +1 -0
  106. package/dist/src/lib/config-surface.d.ts +130 -0
  107. package/dist/src/lib/config-surface.js +300 -0
  108. package/dist/src/lib/config-surface.js.map +1 -0
  109. package/dist/src/lib/credentials-store.d.ts +10 -0
  110. package/dist/src/lib/credentials-store.js +23 -0
  111. package/dist/src/lib/credentials-store.js.map +1 -1
  112. package/dist/src/lib/data-input.d.ts +23 -0
  113. package/dist/src/lib/data-input.js +50 -0
  114. package/dist/src/lib/data-input.js.map +1 -0
  115. package/dist/src/lib/db-codegen/dbTemplates.js +1 -1
  116. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
  117. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +1 -1
  118. package/dist/src/lib/env-resolver-core.d.ts +187 -0
  119. package/dist/src/lib/env-resolver-core.js +325 -0
  120. package/dist/src/lib/env-resolver-core.js.map +1 -0
  121. package/dist/src/lib/env-resolver.d.ts +28 -6
  122. package/dist/src/lib/env-resolver.js +44 -32
  123. package/dist/src/lib/env-resolver.js.map +1 -1
  124. package/dist/src/lib/generated-allowlist.js +12 -0
  125. package/dist/src/lib/generated-allowlist.js.map +1 -1
  126. package/dist/src/lib/generated-config-surfaces.d.ts +682 -0
  127. package/dist/src/lib/generated-config-surfaces.js +4058 -0
  128. package/dist/src/lib/generated-config-surfaces.js.map +1 -0
  129. package/dist/src/lib/generated-template-lint.d.ts +212 -0
  130. package/dist/src/lib/generated-template-lint.js +624 -0
  131. package/dist/src/lib/generated-template-lint.js.map +1 -0
  132. package/dist/src/lib/init-adopt.d.ts +16 -0
  133. package/dist/src/lib/init-adopt.js +34 -0
  134. package/dist/src/lib/init-adopt.js.map +1 -0
  135. package/dist/src/lib/init-assets.d.ts +39 -0
  136. package/dist/src/lib/init-assets.js +97 -0
  137. package/dist/src/lib/init-assets.js.map +1 -0
  138. package/dist/src/lib/init-client-platforms.d.ts +14 -0
  139. package/dist/src/lib/init-client-platforms.js +71 -0
  140. package/dist/src/lib/init-client-platforms.js.map +1 -0
  141. package/dist/src/lib/init-config.d.ts +44 -5
  142. package/dist/src/lib/init-config.js +80 -7
  143. package/dist/src/lib/init-config.js.map +1 -1
  144. package/dist/src/lib/init-email-redirect-uris.d.ts +37 -0
  145. package/dist/src/lib/init-email-redirect-uris.js +46 -0
  146. package/dist/src/lib/init-email-redirect-uris.js.map +1 -0
  147. package/dist/src/lib/init-ios-links.d.ts +90 -0
  148. package/dist/src/lib/init-ios-links.js +218 -0
  149. package/dist/src/lib/init-ios-links.js.map +1 -0
  150. package/dist/src/lib/init-plan.d.ts +80 -0
  151. package/dist/src/lib/init-plan.js +95 -0
  152. package/dist/src/lib/init-plan.js.map +1 -0
  153. package/dist/src/lib/init-production-env.d.ts +48 -0
  154. package/dist/src/lib/init-production-env.js +59 -0
  155. package/dist/src/lib/init-production-env.js.map +1 -0
  156. package/dist/src/lib/init-schema.d.ts +74 -0
  157. package/dist/src/lib/init-schema.js +358 -0
  158. package/dist/src/lib/init-schema.js.map +1 -0
  159. package/dist/src/lib/init-xcode.d.ts +33 -0
  160. package/dist/src/lib/init-xcode.js +114 -0
  161. package/dist/src/lib/init-xcode.js.map +1 -0
  162. package/dist/src/lib/integration-request-config.d.ts +30 -0
  163. package/dist/src/lib/integration-request-config.js +145 -0
  164. package/dist/src/lib/integration-request-config.js.map +1 -0
  165. package/dist/src/lib/integration-selector.d.ts +42 -0
  166. package/dist/src/lib/integration-selector.js +46 -0
  167. package/dist/src/lib/integration-selector.js.map +1 -0
  168. package/dist/src/lib/local-state.d.ts +55 -0
  169. package/dist/src/lib/local-state.js +167 -0
  170. package/dist/src/lib/local-state.js.map +1 -0
  171. package/dist/src/lib/local-test-cases.d.ts +63 -0
  172. package/dist/src/lib/local-test-cases.js +135 -0
  173. package/dist/src/lib/local-test-cases.js.map +1 -0
  174. package/dist/src/lib/log-inspection.d.ts +568 -0
  175. package/dist/src/lib/log-inspection.js +639 -0
  176. package/dist/src/lib/log-inspection.js.map +1 -0
  177. package/dist/src/lib/migration-nag.d.ts +2 -2
  178. package/dist/src/lib/migration-nag.js +3 -3
  179. package/dist/src/lib/object-status-filter.d.ts +22 -0
  180. package/dist/src/lib/object-status-filter.js +45 -0
  181. package/dist/src/lib/object-status-filter.js.map +1 -0
  182. package/dist/src/lib/output.d.ts +24 -1
  183. package/dist/src/lib/output.js +42 -1
  184. package/dist/src/lib/output.js.map +1 -1
  185. package/dist/src/lib/package-manager.d.ts +140 -0
  186. package/dist/src/lib/package-manager.js +305 -0
  187. package/dist/src/lib/package-manager.js.map +1 -0
  188. package/dist/src/lib/paginate.d.ts +50 -0
  189. package/dist/src/lib/paginate.js +53 -0
  190. package/dist/src/lib/paginate.js.map +1 -1
  191. package/dist/src/lib/platform-owned.d.ts +29 -3
  192. package/dist/src/lib/platform-owned.js +42 -3
  193. package/dist/src/lib/platform-owned.js.map +1 -1
  194. package/dist/src/lib/project-config.d.ts +8 -0
  195. package/dist/src/lib/project-config.js +30 -5
  196. package/dist/src/lib/project-config.js.map +1 -1
  197. package/dist/src/lib/query-operators.d.ts +1 -1
  198. package/dist/src/lib/query-operators.js +1 -1
  199. package/dist/src/lib/record-filter.d.ts +18 -0
  200. package/dist/src/lib/record-filter.js +55 -0
  201. package/dist/src/lib/record-filter.js.map +1 -0
  202. package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
  203. package/dist/src/lib/resolve-init-dev-port.js +55 -0
  204. package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
  205. package/dist/src/lib/resolve-init-server.d.ts +64 -0
  206. package/dist/src/lib/resolve-init-server.js +77 -0
  207. package/dist/src/lib/resolve-init-server.js.map +1 -0
  208. package/dist/src/lib/resolve-platform.d.ts +43 -14
  209. package/dist/src/lib/resolve-platform.js +74 -12
  210. package/dist/src/lib/resolve-platform.js.map +1 -1
  211. package/dist/src/lib/run-status.d.ts +19 -0
  212. package/dist/src/lib/run-status.generated.d.ts +39 -0
  213. package/dist/src/lib/run-status.generated.js +66 -0
  214. package/dist/src/lib/run-status.generated.js.map +1 -0
  215. package/dist/src/lib/run-status.js +19 -0
  216. package/dist/src/lib/run-status.js.map +1 -0
  217. package/dist/src/lib/server-text-normalization.d.ts +51 -0
  218. package/dist/src/lib/server-text-normalization.js +90 -0
  219. package/dist/src/lib/server-text-normalization.js.map +1 -0
  220. package/dist/src/lib/server-url.d.ts +22 -0
  221. package/dist/src/lib/server-url.js +33 -0
  222. package/dist/src/lib/server-url.js.map +1 -0
  223. package/dist/src/lib/signing-secret-status.d.ts +81 -0
  224. package/dist/src/lib/signing-secret-status.js +116 -0
  225. package/dist/src/lib/signing-secret-status.js.map +1 -0
  226. package/dist/src/lib/skill-installer.js +6 -3
  227. package/dist/src/lib/skill-installer.js.map +1 -1
  228. package/dist/src/lib/snapshots.d.ts +2 -2
  229. package/dist/src/lib/snapshots.js +2 -2
  230. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +15 -2
  231. package/dist/src/lib/swift-codegen/dbGenerator.js +250 -18
  232. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
  233. package/dist/src/lib/swift-codegen/generator.d.ts +11 -2
  234. package/dist/src/lib/swift-codegen/generator.js +181 -7
  235. package/dist/src/lib/swift-codegen/generator.js.map +1 -1
  236. package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +7 -3
  237. package/dist/src/lib/swift-codegen/siblingSymbols.js +4 -1
  238. package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -1
  239. package/dist/src/lib/sync-dir-selector.d.ts +21 -0
  240. package/dist/src/lib/sync-dir-selector.js +30 -0
  241. package/dist/src/lib/sync-dir-selector.js.map +1 -0
  242. package/dist/src/lib/sync-paths.d.ts +39 -0
  243. package/dist/src/lib/sync-paths.js +70 -2
  244. package/dist/src/lib/sync-paths.js.map +1 -1
  245. package/dist/src/lib/sync-resource-types.d.ts +138 -31
  246. package/dist/src/lib/sync-resource-types.js +241 -47
  247. package/dist/src/lib/sync-resource-types.js.map +1 -1
  248. package/dist/src/lib/sync-selectors.d.ts +95 -0
  249. package/dist/src/lib/sync-selectors.js +228 -0
  250. package/dist/src/lib/sync-selectors.js.map +1 -0
  251. package/dist/src/lib/template.d.ts +62 -4
  252. package/dist/src/lib/template.js +147 -39
  253. package/dist/src/lib/template.js.map +1 -1
  254. package/dist/src/lib/test-case-file-names.d.ts +40 -0
  255. package/dist/src/lib/test-case-file-names.js +91 -0
  256. package/dist/src/lib/test-case-file-names.js.map +1 -0
  257. package/dist/src/lib/test-case-keys.d.ts +29 -0
  258. package/dist/src/lib/test-case-keys.js +55 -0
  259. package/dist/src/lib/test-case-keys.js.map +1 -0
  260. package/dist/src/lib/test-case-variables.d.ts +15 -0
  261. package/dist/src/lib/test-case-variables.js +29 -0
  262. package/dist/src/lib/test-case-variables.js.map +1 -0
  263. package/dist/src/lib/toml-database-config.d.ts +4 -4
  264. package/dist/src/lib/toml-database-config.js +27 -34
  265. package/dist/src/lib/toml-database-config.js.map +1 -1
  266. package/dist/src/lib/toml-metadata-config.d.ts +9 -5
  267. package/dist/src/lib/toml-metadata-config.js +40 -45
  268. package/dist/src/lib/toml-metadata-config.js.map +1 -1
  269. package/dist/src/lib/toml-native-form.d.ts +1 -1
  270. package/dist/src/lib/toml-native-form.js +1 -1
  271. package/dist/src/lib/toml-params-validator.d.ts +1 -1
  272. package/dist/src/lib/toml-params-validator.js +1 -1
  273. package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
  274. package/dist/src/lib/toml-scalar-edit.js +283 -0
  275. package/dist/src/lib/toml-scalar-edit.js.map +1 -0
  276. package/dist/src/lib/user-selector.d.ts +24 -0
  277. package/dist/src/lib/user-selector.js +33 -0
  278. package/dist/src/lib/user-selector.js.map +1 -0
  279. package/dist/src/lib/version-check.d.ts +25 -0
  280. package/dist/src/lib/version-check.js +77 -8
  281. package/dist/src/lib/version-check.js.map +1 -1
  282. package/dist/src/lib/watch.d.ts +121 -0
  283. package/dist/src/lib/watch.js +169 -0
  284. package/dist/src/lib/watch.js.map +1 -0
  285. package/dist/src/lib/web-url.d.ts +40 -0
  286. package/dist/src/lib/web-url.js +76 -0
  287. package/dist/src/lib/web-url.js.map +1 -0
  288. package/dist/src/lib/workflow-apply.d.ts +69 -45
  289. package/dist/src/lib/workflow-apply.js +99 -72
  290. package/dist/src/lib/workflow-apply.js.map +1 -1
  291. package/dist/src/lib/workflow-codegen/generator.js +4 -4
  292. package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
  293. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +11 -0
  294. package/dist/src/lib/workflow-codegen/invokerIR.js +5 -0
  295. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
  296. package/dist/src/lib/workflow-config-apply.d.ts +70 -0
  297. package/dist/src/lib/workflow-config-apply.js +137 -0
  298. package/dist/src/lib/workflow-config-apply.js.map +1 -0
  299. package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
  300. package/dist/src/lib/workflow-config-sidecar.js +96 -0
  301. package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
  302. package/dist/src/lib/workflow-defaults.d.ts +29 -0
  303. package/dist/src/lib/workflow-defaults.js +41 -0
  304. package/dist/src/lib/workflow-defaults.js.map +1 -0
  305. package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
  306. package/dist/src/lib/workflow-include-preserve.js +286 -0
  307. package/dist/src/lib/workflow-include-preserve.js.map +1 -0
  308. package/dist/src/lib/workflow-payload.d.ts +31 -20
  309. package/dist/src/lib/workflow-payload.js +100 -110
  310. package/dist/src/lib/workflow-payload.js.map +1 -1
  311. package/dist/src/lib/workflow-toml-validator.d.ts +37 -16
  312. package/dist/src/lib/workflow-toml-validator.js +141 -16
  313. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  314. package/dist/src/types/index.d.ts +78 -22
  315. package/dist/src/validators.d.ts +7 -7
  316. package/dist/src/validators.js +6 -6
  317. package/package.json +11 -6
  318. package/dist/src/commands/settings.d.ts +0 -15
  319. package/dist/src/commands/settings.js +0 -102
  320. package/dist/src/commands/settings.js.map +0 -1
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The `CFBundleURLName` the library and the template agree on. Changing it
3
+ * breaks the manager's scheme resolution, so it is pinned by a test that reads
4
+ * both files.
5
+ */
6
+ export declare const PRIMITIVE_AUTH_URL_TYPE_NAME = "PrimitiveAuth";
7
+ /** The library's fallback scheme — reserved, never a derived result. */
8
+ export declare const FALLBACK_CALLBACK_SCHEME = "primitiveapp";
9
+ /**
10
+ * The custom URL scheme for one scaffolded client: a readable prefix from the
11
+ * app name, then the full lowercased app ID.
12
+ *
13
+ * Deterministic and injective over server-generated app IDs — two apps never
14
+ * share a scheme, whatever they are called.
15
+ */
16
+ export declare function deriveCallbackScheme(appName: string, appId: string, clientDirName?: string): string;
17
+ /** The redirect target the emailed sign-in link uses, for one scheme. */
18
+ export declare function iosMagicLinkRedirectUri(scheme: string): string;
19
+ export interface StampResult {
20
+ /** `not-found` when the plist predates the `PrimitiveAuth` URL type. */
21
+ outcome: "stamped" | "already" | "not-found";
22
+ }
23
+ /**
24
+ * Replaces the scheme registered under the `PrimitiveAuth` URL type in a
25
+ * scaffolded client's `Info-Partial.plist`.
26
+ *
27
+ * A targeted text substitution, not a plist rewrite: the file's comments are
28
+ * the checklist a developer reads when their emailed link does nothing, and a
29
+ * serializer would drop them.
30
+ */
31
+ export declare function stampCallbackScheme(clientDir: string, scheme: string): StampResult;
32
+ /**
33
+ * How to point an environment at the app's web origin — in the shape that
34
+ * actually works (#2982).
35
+ *
36
+ * `primitive env add` CREATES an environment: it requires `--api-url` and
37
+ * refuses a name the config already has. Every environment anyone reads this
38
+ * about already exists (init wrote `dev`; `prod` was added when the app was
39
+ * deployed), so the instruction for those has to be the file, which is also
40
+ * what the docs describe as the way to edit one. The flag is worth naming only
41
+ * where a new environment is being created.
42
+ *
43
+ * `origin` is the placeholder or real origin to show.
44
+ */
45
+ export declare function setWebUrlInstruction(origin: string, indent?: string): string[];
46
+ /**
47
+ * What init prints about email sign-in on the iOS path: it already works, and
48
+ * this is the one opt-in that adds the link.
49
+ *
50
+ * The allow-list step is phrased as a MERGE into the existing array — `app.toml`
51
+ * is the whole app-settings truth on push (#2880), so a replacement array would
52
+ * silently drop the web callback the app was seeded with.
53
+ *
54
+ * `registered` is false when the scheme could not be stamped (an older template
55
+ * with no `PrimitiveAuth` URL type). The allow-list entry and the flag are the
56
+ * same either way, but the closing line must not claim a registration that is
57
+ * not there: a developer who did only the two steps would be left with a dead
58
+ * tap and nothing naming the missing piece.
59
+ */
60
+ export declare function linkOptInGuidance(scheme: string, options?: {
61
+ registered?: boolean;
62
+ }): string[];
63
+ /**
64
+ * What init prints for an app with BOTH clients: the emailed sign-in link is
65
+ * already the web app's https callback, and this is what it takes to make that
66
+ * link open the iOS app in production (#2982).
67
+ *
68
+ * Everything here is a post-deploy step by necessity — the production web
69
+ * domain does not exist at init time, and the deploy script never learns the
70
+ * URL it deployed to. The order is load-bearing: the `applinks:` entitlement
71
+ * is the ONLY thing that makes Apple fetch the association document, and
72
+ * Apple's CDN caches what it gets, so it goes last — after the deployed
73
+ * document has been verified.
74
+ *
75
+ * `webOrigin` is the dev origin the run seeded — set only when the run also
76
+ * confirmed that origin's `/oauth/callback` is on the app's allow-list, since
77
+ * that is what makes the seeded link work.
78
+ *
79
+ * `unverifiedDevCallback` is the opposite case: a combined app whose allow-list
80
+ * this run could not vouch for (an adopted app, or no credentials to ask with).
81
+ * Nothing was seeded — the app keeps its working code-only email — and the two
82
+ * steps that turn the link on are printed instead. Neither is set for an
83
+ * adopting run, which does not own the project config at all.
84
+ */
85
+ export declare function universalLinkGuidance(options?: {
86
+ webOrigin?: string;
87
+ unverifiedDevCallback?: string;
88
+ }): string[];
89
+ /** What init says when the template predates the `PrimitiveAuth` URL type. */
90
+ export declare function missingUrlTypeGuidance(scheme: string): string[];
@@ -0,0 +1,218 @@
1
+ /**
2
+ * The iOS app's own URL scheme, and the documented opt-in that turns the
3
+ * sign-in email's LINK on (#2969).
4
+ *
5
+ * The Swift template registers one custom URL scheme, under the
6
+ * `PrimitiveAuth` URL-type name that `PrimitiveAuthManager` reads its callback
7
+ * scheme from. Shipped, that scheme is `primitiveapp` — which every app
8
+ * scaffolded from the template would otherwise share, making iOS link routing
9
+ * between two of them on one device ambiguous. So init stamps an app-unique
10
+ * scheme in its place.
11
+ *
12
+ * The suffix is the WHOLE app ID, not a tail or a hash of it: app IDs are
13
+ * 26-character ULIDs, and any fixed-width squeeze of them is many-to-one — two
14
+ * distinct apps could still be handed the same scheme, which is the failure
15
+ * this stamping exists to prevent.
16
+ *
17
+ * Init writes no app settings here. Email sign-in works out of the box because
18
+ * the manager sends NO redirect target by default, so the email carries the
19
+ * code alone and the allow-list is not consulted at all. Sending the link is
20
+ * the developer's opt-in — one allow-list entry plus one flag — and init only
21
+ * prints what those are.
22
+ */
23
+ import { existsSync, readFileSync, writeFileSync } from "fs";
24
+ import { join } from "path";
25
+ /**
26
+ * The `CFBundleURLName` the library and the template agree on. Changing it
27
+ * breaks the manager's scheme resolution, so it is pinned by a test that reads
28
+ * both files.
29
+ */
30
+ export const PRIMITIVE_AUTH_URL_TYPE_NAME = "PrimitiveAuth";
31
+ /** The library's fallback scheme — reserved, never a derived result. */
32
+ export const FALLBACK_CALLBACK_SCHEME = "primitiveapp";
33
+ /** How much of the app name survives into the readable prefix. */
34
+ const PREFIX_MAX_LENGTH = 20;
35
+ /** The app-name placeholder init carries when it never asked for one. */
36
+ const APP_NAME_PLACEHOLDER = "(existing app)";
37
+ /**
38
+ * A URL-scheme-safe, letter-first fragment of `value`, or `""` when nothing
39
+ * usable survives (an empty name, an emoji-only one, digits alone).
40
+ */
41
+ function schemeFragment(value, maxLength) {
42
+ const sanitized = (value ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
43
+ // RFC 3986 schemes start with a letter, and the prefix is what leads.
44
+ const letterFirst = sanitized.replace(/^[0-9]+/, "");
45
+ return letterFirst.slice(0, maxLength);
46
+ }
47
+ /**
48
+ * The custom URL scheme for one scaffolded client: a readable prefix from the
49
+ * app name, then the full lowercased app ID.
50
+ *
51
+ * Deterministic and injective over server-generated app IDs — two apps never
52
+ * share a scheme, whatever they are called.
53
+ */
54
+ export function deriveCallbackScheme(appName, appId, clientDirName) {
55
+ const named = appName === APP_NAME_PLACEHOLDER ? "" : appName;
56
+ const prefix = schemeFragment(named, PREFIX_MAX_LENGTH) ||
57
+ schemeFragment(clientDirName, PREFIX_MAX_LENGTH) ||
58
+ "app";
59
+ // No truncation: the whole ID is what makes the scheme unique.
60
+ const suffix = (appId ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
61
+ const scheme = suffix ? `${prefix}-${suffix}` : prefix;
62
+ // Only reachable with no app ID at all, but the reserved scheme is exactly
63
+ // what a derived one must never collide with.
64
+ return scheme === FALLBACK_CALLBACK_SCHEME ? `${scheme}-app` : scheme;
65
+ }
66
+ /** The redirect target the emailed sign-in link uses, for one scheme. */
67
+ export function iosMagicLinkRedirectUri(scheme) {
68
+ return `${scheme}://auth/magic-link`;
69
+ }
70
+ /**
71
+ * Replaces the scheme registered under the `PrimitiveAuth` URL type in a
72
+ * scaffolded client's `Info-Partial.plist`.
73
+ *
74
+ * A targeted text substitution, not a plist rewrite: the file's comments are
75
+ * the checklist a developer reads when their emailed link does nothing, and a
76
+ * serializer would drop them.
77
+ */
78
+ export function stampCallbackScheme(clientDir, scheme) {
79
+ const plistPath = join(clientDir, "Info-Partial.plist");
80
+ if (!existsSync(plistPath))
81
+ return { outcome: "not-found" };
82
+ let text;
83
+ try {
84
+ text = readFileSync(plistPath, "utf-8");
85
+ }
86
+ catch {
87
+ return { outcome: "not-found" };
88
+ }
89
+ const marker = `<string>${PRIMITIVE_AUTH_URL_TYPE_NAME}</string>`;
90
+ const markerIndex = text.indexOf(marker);
91
+ if (markerIndex === -1)
92
+ return { outcome: "not-found" };
93
+ // Stay inside the URL-type dict the marker names: an app may register other
94
+ // schemes (a Google reversed client ID, say) and those are not ours to move.
95
+ const dictStart = text.lastIndexOf("<dict>", markerIndex);
96
+ const dictEnd = text.indexOf("</dict>", markerIndex);
97
+ if (dictStart === -1 || dictEnd === -1)
98
+ return { outcome: "not-found" };
99
+ const block = text.slice(dictStart, dictEnd);
100
+ if (!block.includes("<key>CFBundleURLName</key>"))
101
+ return { outcome: "not-found" };
102
+ const schemesPattern = /(<key>CFBundleURLSchemes<\/key>\s*<array>\s*<string>)([^<]*)(<\/string>)/;
103
+ const match = schemesPattern.exec(block);
104
+ if (!match)
105
+ return { outcome: "not-found" };
106
+ if (match[2] === scheme)
107
+ return { outcome: "already" };
108
+ const rewritten = block.replace(schemesPattern, `$1${scheme}$3`);
109
+ writeFileSync(plistPath, text.slice(0, dictStart) + rewritten + text.slice(dictEnd));
110
+ return { outcome: "stamped" };
111
+ }
112
+ /**
113
+ * How to point an environment at the app's web origin — in the shape that
114
+ * actually works (#2982).
115
+ *
116
+ * `primitive env add` CREATES an environment: it requires `--api-url` and
117
+ * refuses a name the config already has. Every environment anyone reads this
118
+ * about already exists (init wrote `dev`; `prod` was added when the app was
119
+ * deployed), so the instruction for those has to be the file, which is also
120
+ * what the docs describe as the way to edit one. The flag is worth naming only
121
+ * where a new environment is being created.
122
+ *
123
+ * `origin` is the placeholder or real origin to show.
124
+ */
125
+ export function setWebUrlInstruction(origin, indent = " ") {
126
+ return [
127
+ `${indent}add "webUrl": "${origin}" to that environment in .primitive/config.json`,
128
+ `${indent}(\`primitive env add <name> --api-url … --web-url <origin>\` does this only for an`,
129
+ `${indent}environment it CREATES — it cannot edit one that already exists).`,
130
+ ];
131
+ }
132
+ /**
133
+ * What init prints about email sign-in on the iOS path: it already works, and
134
+ * this is the one opt-in that adds the link.
135
+ *
136
+ * The allow-list step is phrased as a MERGE into the existing array — `app.toml`
137
+ * is the whole app-settings truth on push (#2880), so a replacement array would
138
+ * silently drop the web callback the app was seeded with.
139
+ *
140
+ * `registered` is false when the scheme could not be stamped (an older template
141
+ * with no `PrimitiveAuth` URL type). The allow-list entry and the flag are the
142
+ * same either way, but the closing line must not claim a registration that is
143
+ * not there: a developer who did only the two steps would be left with a dead
144
+ * tap and nothing naming the missing piece.
145
+ */
146
+ export function linkOptInGuidance(scheme, options = {}) {
147
+ const registered = options.registered !== false;
148
+ const redirectUri = iosMagicLinkRedirectUri(scheme);
149
+ return [
150
+ "Email sign-in works now: the email carries a 6-digit code and needs no app settings.",
151
+ "When this app gets a web client, prefer the https link: set the environment's `webUrl` —",
152
+ ...setWebUrlInstruction("https://<your-web-domain>"),
153
+ " The email then carries that origin's /oauth/callback instead — one link that opens this",
154
+ " app where it is installed and signs in through the browser anywhere else.",
155
+ `Until then, to ALSO send a sign-in link that opens this app, add "${redirectUri}" to the existing`,
156
+ " [auth].emailRedirectUris array in config/app.toml (merge it in — run",
157
+ " `primitive config pull --only app` first if that file is not there yet), then run",
158
+ " `primitive config push --only app` and set `authManager.sendsEmailSignInLink = true`.",
159
+ registered
160
+ ? "The scheme registration and the link routing are already in this client."
161
+ : `The scheme "${scheme}" is NOT registered in this client yet — register it first (see`
162
+ + ` the ${PRIMITIVE_AUTH_URL_TYPE_NAME} warning above), or the link stays a dead tap.`,
163
+ " Full checklist: `primitive guides get authentication --language swift`, or the",
164
+ " authentication.md docs page — 'Make the emailed sign-in link open your app'.",
165
+ ];
166
+ }
167
+ /**
168
+ * What init prints for an app with BOTH clients: the emailed sign-in link is
169
+ * already the web app's https callback, and this is what it takes to make that
170
+ * link open the iOS app in production (#2982).
171
+ *
172
+ * Everything here is a post-deploy step by necessity — the production web
173
+ * domain does not exist at init time, and the deploy script never learns the
174
+ * URL it deployed to. The order is load-bearing: the `applinks:` entitlement
175
+ * is the ONLY thing that makes Apple fetch the association document, and
176
+ * Apple's CDN caches what it gets, so it goes last — after the deployed
177
+ * document has been verified.
178
+ *
179
+ * `webOrigin` is the dev origin the run seeded — set only when the run also
180
+ * confirmed that origin's `/oauth/callback` is on the app's allow-list, since
181
+ * that is what makes the seeded link work.
182
+ *
183
+ * `unverifiedDevCallback` is the opposite case: a combined app whose allow-list
184
+ * this run could not vouch for (an adopted app, or no credentials to ask with).
185
+ * Nothing was seeded — the app keeps its working code-only email — and the two
186
+ * steps that turn the link on are printed instead. Neither is set for an
187
+ * adopting run, which does not own the project config at all.
188
+ */
189
+ export function universalLinkGuidance(options = {}) {
190
+ const lines = [
191
+ "This app has both an iOS and a web client, so its sign-in email carries ONE https link:",
192
+ " the web app's /oauth/callback. Tapped where the iOS app is installed it opens the app;",
193
+ " anywhere else it is an ordinary web sign-in. The 6-digit code still works everywhere.",
194
+ ];
195
+ if (options.webOrigin) {
196
+ lines.push(`In dev that link is ${options.webOrigin}/oauth/callback — seeded as the environment's`, " `webUrl`, and allow-listed on the app. Nothing to do.");
197
+ }
198
+ else if (options.unverifiedDevCallback) {
199
+ const origin = options.unverifiedDevCallback.replace(/\/oauth\/callback$/, "");
200
+ lines.push("The link is NOT on yet in dev: this app already existed, and its sign-in link allow-list", ` does not (or could not be checked to) carry ${options.unverifiedDevCallback}. Nothing was`, " changed — the email stays code-only, which works. To turn the link on:", ` 1. Merge "${options.unverifiedDevCallback}" into the existing [auth].emailRedirectUris`, " array in config/app.toml (run `primitive config pull --only app` first if that file is", " not there yet), then `primitive config push --only app`.", ` 2. Then ${setWebUrlInstruction(origin, "")[0]}`, " — the same edit the production steps below describe.");
201
+ }
202
+ else {
203
+ lines.push("Set `webUrl` on this app's environment to turn it on (this run did not write the", " project config):", ...setWebUrlInstruction("https://<your-web-domain>"), " Allow-list that origin's /oauth/callback in [auth].emailRedirectUris too.");
204
+ }
205
+ lines.push("For production, once the web app is deployed:", " 1. Set that environment's `webUrl` to the deployed origin —", ...setWebUrlInstruction("https://<your-web-domain>", " "), " 2. Merge \"https://<your-web-domain>/oauth/callback\" into the existing", " [auth].emailRedirectUris array in config/app.toml (run `primitive config pull --only app`", " first if that file is not there yet), then `primitive config push --only app`.", " 3. In the web client, fill your Apple team and bundle ids into", " public/.well-known/apple-app-site-association.example and rename it (drop .example).", " 4. Deploy, then verify: `curl -i https://<your-web-domain>/.well-known/apple-app-site-association`", " must return 200, application/json and the JSON body — HTML means the SPA fallback answered.", " 5. Only then enable the `applinks:<your-web-domain>` entitlement in the iOS client's", " project.yml (it needs a real DEVELOPMENT_TEAM) and reinstall the app.", " Full checklist: the web client's public/.well-known/README.md, or the authentication", " docs page — 'Make the emailed sign-in link open your app'.");
206
+ return lines;
207
+ }
208
+ /** What init says when the template predates the `PrimitiveAuth` URL type. */
209
+ export function missingUrlTypeGuidance(scheme) {
210
+ return [
211
+ `This template's Info-Partial.plist registers no ${PRIMITIVE_AUTH_URL_TYPE_NAME} URL type, so the`,
212
+ ` scheme "${scheme}" was not stamped and an emailed sign-in link cannot open this app.`,
213
+ ` Add a CFBundleURLTypes entry named ${PRIMITIVE_AUTH_URL_TYPE_NAME} with that scheme, then follow`,
214
+ " the authentication.md docs page — 'Make the emailed sign-in link open your app'",
215
+ " (or `primitive guides get authentication --language swift`).",
216
+ ];
217
+ }
218
+ //# sourceMappingURL=init-ios-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-ios-links.js","sourceRoot":"","sources":["../../../src/lib/init-ios-links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import type { Platform } from "./resolve-platform.js";
2
+ export type InitMode = "standalone" | "multi-client" | "add-client";
3
+ export interface ClientPlan {
4
+ platform: Platform;
5
+ /** Absolute directory this client's template is installed into. */
6
+ dir: string;
7
+ }
8
+ export interface InitPlan {
9
+ mode: InitMode;
10
+ /** Every client this run scaffolds, in processing order (web first). */
11
+ clients: ClientPlan[];
12
+ /** The directory holding the ONE `.primitive/` for this app. */
13
+ projectRoot: string;
14
+ /**
15
+ * Whether this run writes `.primitive/config.json` and seeds the
16
+ * environment's credential slot. False when adopting: the config is already
17
+ * there and is not this run's to rewrite.
18
+ */
19
+ writesProjectConfig: boolean;
20
+ /**
21
+ * Whether this run may create the app and point the CLI's global "current
22
+ * app" at it. False when adopting — the app already exists and belongs to the
23
+ * project config.
24
+ */
25
+ ownsApp: boolean;
26
+ /**
27
+ * Whether this run creates the git repository and its initial commit. False
28
+ * when adopting: the surrounding repository owns the history, and the new
29
+ * files are left for the user to review and commit.
30
+ */
31
+ initsGit: boolean;
32
+ /**
33
+ * The one schema both clients read, or `null` in standalone mode where the
34
+ * client keeps the template's own copy (spec decision 1: single-client output
35
+ * is unchanged).
36
+ */
37
+ canonicalSchemaPath: string | null;
38
+ /**
39
+ * Whether this run may write an app-wide `AGENTS.md` at the project root.
40
+ * Never over an existing file — client `AGENTS.md` files ship with their
41
+ * templates and an adopted repo's root file is the user's.
42
+ */
43
+ writesRootAgents: boolean;
44
+ }
45
+ export interface PlanInitInput {
46
+ /** The resolved platforms, in canonical order. */
47
+ platforms: Platform[];
48
+ /** Absolute directory the command was pointed at. */
49
+ targetDir: string;
50
+ /**
51
+ * The nearest ancestor project root, passed ONLY when it differs from
52
+ * `targetDir` — `primitive init .` at an existing project root keeps its
53
+ * merge behavior rather than adopting itself.
54
+ */
55
+ ancestorProjectRoot?: string | null;
56
+ }
57
+ /** Resolve the layout for this run. */
58
+ export declare function planInit(input: PlanInitInput): InitPlan;
59
+ export interface InitGuardInput {
60
+ /** The init config's `action`, when a non-interactive config is in effect. */
61
+ action?: "create" | "use-existing" | "add-client";
62
+ /** Whether an init config is in effect at all. */
63
+ hasConfig: boolean;
64
+ /** The nearest ancestor project root, or null/undefined outside a project. */
65
+ ancestorProjectRoot?: string | null;
66
+ /** `app_id` from a `use-existing` init config. */
67
+ configAppId?: string;
68
+ /** `--use-existing-app-id`. */
69
+ flagAppId?: string;
70
+ /** The adopted environment's `appId`, when adopting. */
71
+ environmentAppId?: string;
72
+ /** The adopted environment's name, for the error messages. */
73
+ environmentName?: string;
74
+ }
75
+ /**
76
+ * Everything a run must be refused for, checked before any download, any API
77
+ * call and any write. Returns the user-facing message, or `null` when the run
78
+ * may proceed.
79
+ */
80
+ export declare function checkInitGuards(input: InitGuardInput): string | null;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * What shape does this `primitive init` run produce? (#2890)
3
+ *
4
+ * One Primitive app can have several clients — a web client and a native
5
+ * client — and they are one app: one appId, one project config, one exported
6
+ * server-config tree, one model schema. `init` therefore composes one of three
7
+ * layouts, and the choice decides everything downstream:
8
+ *
9
+ * standalone — one client, no surrounding project. Today's flat scaffold,
10
+ * unchanged: the client IS the project root.
11
+ * multi-client — several clients, no surrounding project. Each client gets a
12
+ * platform-named subdirectory; the project config, the git
13
+ * repository and the canonical schema live at the root.
14
+ * add-client — an ancestor `.primitive/config.json` already exists. The run
15
+ * ADOPTS it: no nested project config, no nested git
16
+ * repository, no app creation, no commit. This is the path the
17
+ * issue opens with — running init in a subdirectory used to
18
+ * scaffold a second project that had to be partly undone.
19
+ *
20
+ * Pure on purpose: the layout and the guards are the part that is expensive to
21
+ * get wrong and impossible to assert from a half-written scaffold.
22
+ */
23
+ import { join } from "path";
24
+ import { PROJECT_CONFIG_DISPLAY_NAME } from "./project-config.js";
25
+ /** Resolve the layout for this run. */
26
+ export function planInit(input) {
27
+ const adopting = Boolean(input.ancestorProjectRoot);
28
+ const projectRoot = adopting ? input.ancestorProjectRoot : input.targetDir;
29
+ const composed = adopting || input.platforms.length > 1;
30
+ // One client in an adopt run lands exactly where the user pointed the
31
+ // command; anything else gets a platform-named subdirectory, which is what
32
+ // makes `web/` and `ios/` siblings under one root.
33
+ const useSubdirectories = input.platforms.length > 1;
34
+ const clients = input.platforms.map((platform) => ({
35
+ platform,
36
+ dir: useSubdirectories ? join(input.targetDir, platform) : input.targetDir,
37
+ }));
38
+ const mode = adopting
39
+ ? "add-client"
40
+ : input.platforms.length > 1
41
+ ? "multi-client"
42
+ : "standalone";
43
+ return {
44
+ mode,
45
+ clients,
46
+ projectRoot,
47
+ writesProjectConfig: !adopting,
48
+ ownsApp: !adopting,
49
+ initsGit: !adopting,
50
+ canonicalSchemaPath: composed
51
+ ? join(projectRoot, "models", "models.toml")
52
+ : null,
53
+ writesRootAgents: composed,
54
+ };
55
+ }
56
+ /**
57
+ * Everything a run must be refused for, checked before any download, any API
58
+ * call and any write. Returns the user-facing message, or `null` when the run
59
+ * may proceed.
60
+ */
61
+ export function checkInitGuards(input) {
62
+ const root = input.ancestorProjectRoot;
63
+ if (!root) {
64
+ // `add-client` describes adopting a project that is not there.
65
+ if (input.action === "add-client") {
66
+ return (`action = "add-client" adds a client to an existing Primitive app, but there is no ` +
67
+ `${PROJECT_CONFIG_DISPLAY_NAME} in this directory or any parent. ` +
68
+ `Run init from inside the app's repository, or use action = "create" to start a new app.`);
69
+ }
70
+ return null;
71
+ }
72
+ const envName = input.environmentName ?? "the selected environment";
73
+ // Creating a SECOND app inside a repository that already has one is the
74
+ // nesting this change removes.
75
+ if (input.action === "create") {
76
+ return (`action = "create" would create a second Primitive app inside the existing project at ${root}. ` +
77
+ `Use action = "add-client" to add this client to the app that project already targets.`);
78
+ }
79
+ if (!input.environmentAppId) {
80
+ return (`The "${envName}" environment in ${root}/${PROJECT_CONFIG_DISPLAY_NAME} has no appId, so there is no app to add this client to. ` +
81
+ `Set one with \`primitive env add <name> --api-url <url> --app-id <appId>\`, or select an environment that has one with \`primitive env use <name>\`.`);
82
+ }
83
+ // Naming an app explicitly is allowed only when it is the SAME app: a
84
+ // different one would wire this client to a backend the rest of the repo does
85
+ // not target.
86
+ const named = input.flagAppId ?? (input.action === "use-existing" ? input.configAppId : undefined);
87
+ if (named && named !== input.environmentAppId) {
88
+ const source = input.flagAppId ? "--use-existing-app-id" : '`app_id` (action = "use-existing")';
89
+ return (`${source} names app ${named}, but the "${envName}" environment of the project at ${root} targets app ${input.environmentAppId}. ` +
90
+ `A client added to this repository belongs to that app — drop the app id and use action = "add-client", ` +
91
+ `or run init outside this project to scaffold a separate app.`);
92
+ }
93
+ return null;
94
+ }
95
+ //# sourceMappingURL=init-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-plan.js","sourceRoot":"","sources":["../../../src/lib/init-plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Which backend a scaffolded client's PRODUCTION build talks to (#2890).
3
+ *
4
+ * `init` used to write the hosted production URL into `.env.production`
5
+ * unconditionally, alongside the app ID of the app it had just created. On the
6
+ * hosted server those agree. On any other one — a self-hosted server, a staging
7
+ * server, an alpha channel — they do not: the production build points at a
8
+ * backend the app ID does not exist on, and the mismatch only shows up after a
9
+ * deploy.
10
+ *
11
+ * So a composed run derives the pair from the project's own environments
12
+ * instead, and only falls back to the old behavior where it was right anyway:
13
+ *
14
+ * 1. a `prod` environment, then a `production` one — the team already said
15
+ * which backend/app pair production means
16
+ * 2. otherwise, when this run targets the hosted production server, the
17
+ * hosted production URL with this app (today's output)
18
+ * 3. otherwise the selected environment's own server and app — one backend,
19
+ * one app, consistently
20
+ *
21
+ * Standalone runs keep calling the old path verbatim, so a single-client
22
+ * scaffold's output is unchanged (spec decision 1).
23
+ */
24
+ export interface ProductionRuntime {
25
+ serverUrl: string;
26
+ appId: string;
27
+ appName?: string;
28
+ /** Which rule chose it, for the message init prints. */
29
+ source: "environment" | "hosted-production" | "selected";
30
+ /** The environment name, when one was used. */
31
+ environmentName?: string;
32
+ }
33
+ export interface ResolveProductionRuntimeInput {
34
+ /** The project's environments, keyed by name. */
35
+ environments?: Record<string, {
36
+ apiUrl?: string;
37
+ appId?: string;
38
+ appName?: string;
39
+ } | undefined>;
40
+ /** The server this run targets. */
41
+ selectedServerUrl: string;
42
+ /** The app this run scaffolds against. */
43
+ selectedAppId: string;
44
+ selectedAppName?: string;
45
+ /** The hosted production server URL. */
46
+ productionServerUrl: string;
47
+ }
48
+ export declare function resolveProductionRuntime(input: ResolveProductionRuntimeInput): ProductionRuntime;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Which backend a scaffolded client's PRODUCTION build talks to (#2890).
3
+ *
4
+ * `init` used to write the hosted production URL into `.env.production`
5
+ * unconditionally, alongside the app ID of the app it had just created. On the
6
+ * hosted server those agree. On any other one — a self-hosted server, a staging
7
+ * server, an alpha channel — they do not: the production build points at a
8
+ * backend the app ID does not exist on, and the mismatch only shows up after a
9
+ * deploy.
10
+ *
11
+ * So a composed run derives the pair from the project's own environments
12
+ * instead, and only falls back to the old behavior where it was right anyway:
13
+ *
14
+ * 1. a `prod` environment, then a `production` one — the team already said
15
+ * which backend/app pair production means
16
+ * 2. otherwise, when this run targets the hosted production server, the
17
+ * hosted production URL with this app (today's output)
18
+ * 3. otherwise the selected environment's own server and app — one backend,
19
+ * one app, consistently
20
+ *
21
+ * Standalone runs keep calling the old path verbatim, so a single-client
22
+ * scaffold's output is unchanged (spec decision 1).
23
+ */
24
+ /** Trailing-slash-insensitive comparison, as elsewhere in init. */
25
+ function sameServer(a, b) {
26
+ return a.replace(/\/+$/, "") === b.replace(/\/+$/, "");
27
+ }
28
+ export function resolveProductionRuntime(input) {
29
+ const environments = input.environments ?? {};
30
+ // `prod` first, deliberately: a repo with both names has one of them as the
31
+ // real one, and `prod` is what `primitive init` and the templates use.
32
+ for (const name of ["prod", "production"]) {
33
+ const env = environments[name];
34
+ if (env?.apiUrl && env?.appId) {
35
+ return {
36
+ serverUrl: env.apiUrl,
37
+ appId: env.appId,
38
+ appName: env.appName ?? input.selectedAppName,
39
+ source: "environment",
40
+ environmentName: name,
41
+ };
42
+ }
43
+ }
44
+ if (sameServer(input.selectedServerUrl, input.productionServerUrl)) {
45
+ return {
46
+ serverUrl: input.productionServerUrl,
47
+ appId: input.selectedAppId,
48
+ appName: input.selectedAppName,
49
+ source: "hosted-production",
50
+ };
51
+ }
52
+ return {
53
+ serverUrl: input.selectedServerUrl,
54
+ appId: input.selectedAppId,
55
+ appName: input.selectedAppName,
56
+ source: "selected",
57
+ };
58
+ }
59
+ //# sourceMappingURL=init-production-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-production-env.js","sourceRoot":"","sources":["../../../src/lib/init-production-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA2BH,mEAAmE;AACnE,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,KAAoC;IAEpC,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;IAC9C,4EAA4E;IAC5E,uEAAuE;IACvE,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,GAAG,EAAE,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,CAAC;YAC9B,OAAO;gBACL,SAAS,EAAE,GAAG,CAAC,MAAM;gBACrB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,KAAK,CAAC,eAAe;gBAC7C,MAAM,EAAE,aAAa;gBACrB,eAAe,EAAE,IAAI;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACnE,OAAO;YACL,SAAS,EAAE,KAAK,CAAC,mBAAmB;YACpC,KAAK,EAAE,KAAK,CAAC,aAAa;YAC1B,OAAO,EAAE,KAAK,CAAC,eAAe;YAC9B,MAAM,EAAE,mBAAmB;SAC5B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,iBAAiB;QAClC,KAAK,EAAE,KAAK,CAAC,aAAa;QAC1B,OAAO,EAAE,KAAK,CAAC,eAAe;QAC9B,MAAM,EAAE,UAAU;KACnB,CAAC;AACJ,CAAC"}