create-qpq-app 0.1.11 → 0.1.13

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 (194) hide show
  1. package/lib/commonjs/cli/runCreateQpqApp.js +14 -12
  2. package/lib/commonjs/cli/runCreateQpqApp.js.map +1 -1
  3. package/lib/commonjs/lib/getArgValue.d.ts +1 -0
  4. package/lib/commonjs/lib/getArgValue.js +14 -0
  5. package/lib/commonjs/lib/getArgValue.js.map +1 -0
  6. package/lib/commonjs/lib/{packageRoot.d.ts → getOwnPackageRoot.d.ts} +0 -1
  7. package/lib/commonjs/lib/{packageRoot.js → getOwnPackageRoot.js} +2 -7
  8. package/lib/commonjs/lib/getOwnPackageRoot.js.map +1 -0
  9. package/lib/commonjs/lib/getOwnVersion.d.ts +1 -0
  10. package/lib/commonjs/lib/getOwnVersion.js +15 -0
  11. package/lib/commonjs/lib/getOwnVersion.js.map +1 -0
  12. package/lib/commonjs/lib/{args.d.ts → getPositionalArgs.d.ts} +0 -1
  13. package/lib/commonjs/lib/getPositionalArgs.js +12 -0
  14. package/lib/commonjs/lib/getPositionalArgs.js.map +1 -0
  15. package/lib/commonjs/lib/index.d.ts +11 -0
  16. package/lib/commonjs/lib/index.js +28 -0
  17. package/lib/commonjs/lib/index.js.map +1 -0
  18. package/lib/commonjs/lib/listFilesRecursive.d.ts +1 -0
  19. package/lib/commonjs/lib/listFilesRecursive.js +27 -0
  20. package/lib/commonjs/lib/listFilesRecursive.js.map +1 -0
  21. package/lib/commonjs/lib/{prompts.js → promptSelect.js} +1 -1
  22. package/lib/commonjs/lib/promptSelect.js.map +1 -0
  23. package/lib/commonjs/lib/readJsonFile.d.ts +1 -0
  24. package/lib/commonjs/lib/readJsonFile.js +13 -0
  25. package/lib/commonjs/lib/readJsonFile.js.map +1 -0
  26. package/lib/commonjs/lib/replaceInFileExact.d.ts +1 -0
  27. package/lib/commonjs/lib/replaceInFileExact.js +18 -0
  28. package/lib/commonjs/lib/replaceInFileExact.js.map +1 -0
  29. package/lib/commonjs/lib/replaceInFiles.d.ts +1 -0
  30. package/lib/commonjs/lib/replaceInFiles.js +32 -0
  31. package/lib/commonjs/lib/replaceInFiles.js.map +1 -0
  32. package/lib/commonjs/lib/writeJsonFile.d.ts +1 -0
  33. package/lib/commonjs/lib/writeJsonFile.js +12 -0
  34. package/lib/commonjs/lib/writeJsonFile.js.map +1 -0
  35. package/lib/commonjs/steps/001_preflight.js +3 -3
  36. package/lib/commonjs/steps/001_preflight.js.map +1 -1
  37. package/lib/commonjs/steps/002_copyTemplate.js +1 -1
  38. package/lib/commonjs/steps/002_copyTemplate.js.map +1 -1
  39. package/lib/commonjs/steps/003_deleteDocusaurus.js +5 -4
  40. package/lib/commonjs/steps/003_deleteDocusaurus.js.map +1 -1
  41. package/lib/commonjs/steps/005_applyAppIdentity.js +12 -9
  42. package/lib/commonjs/steps/005_applyAppIdentity.js.map +1 -1
  43. package/lib/commonjs/steps/006_applyDomain.js +6 -4
  44. package/lib/commonjs/steps/006_applyDomain.js.map +1 -1
  45. package/lib/commonjs/steps/007_pinRegistryVersions.js +5 -4
  46. package/lib/commonjs/steps/007_pinRegistryVersions.js.map +1 -1
  47. package/lib/commonjs/steps/008_transpileToJavaScript.js +19 -17
  48. package/lib/commonjs/steps/008_transpileToJavaScript.js.map +1 -1
  49. package/lib/commonjs/steps/009_restoreGitignore.js +2 -2
  50. package/lib/commonjs/steps/009_restoreGitignore.js.map +1 -1
  51. package/lib/commonjs/steps/010_gitInit.js +1 -1
  52. package/lib/commonjs/steps/010_gitInit.js.map +1 -1
  53. package/lib/commonjs/steps/013_printNextSteps.js +1 -1
  54. package/lib/commonjs/steps/013_printNextSteps.js.map +1 -1
  55. package/lib/commonjs/steps/index.js +1 -1
  56. package/lib/commonjs/steps/index.js.map +1 -1
  57. package/lib/commonjs/types/AppLanguage.d.ts +4 -0
  58. package/lib/commonjs/{types.js → types/AppLanguage.js} +1 -1
  59. package/lib/commonjs/types/AppLanguage.js.map +1 -0
  60. package/lib/commonjs/types/CreateQpqAppAnswers.d.ts +8 -0
  61. package/lib/commonjs/types/CreateQpqAppAnswers.js +3 -0
  62. package/lib/commonjs/types/CreateQpqAppAnswers.js.map +1 -0
  63. package/lib/commonjs/types/CreateQpqAppStep.d.ts +7 -0
  64. package/lib/commonjs/types/CreateQpqAppStep.js +3 -0
  65. package/lib/commonjs/types/CreateQpqAppStep.js.map +1 -0
  66. package/lib/commonjs/types/StepContext.d.ts +7 -0
  67. package/lib/commonjs/types/StepContext.js +3 -0
  68. package/lib/commonjs/types/StepContext.js.map +1 -0
  69. package/lib/commonjs/types/index.d.ts +4 -0
  70. package/lib/commonjs/types/index.js +21 -0
  71. package/lib/commonjs/types/index.js.map +1 -0
  72. package/lib/esm/cli/runCreateQpqApp.js +8 -6
  73. package/lib/esm/cli/runCreateQpqApp.js.map +1 -1
  74. package/lib/esm/lib/getArgValue.d.ts +1 -0
  75. package/lib/esm/lib/getArgValue.js +10 -0
  76. package/lib/esm/lib/getArgValue.js.map +1 -0
  77. package/lib/esm/lib/{packageRoot.d.ts → getOwnPackageRoot.d.ts} +0 -1
  78. package/lib/esm/lib/{packageRoot.js → getOwnPackageRoot.js} +1 -5
  79. package/lib/esm/lib/getOwnPackageRoot.js.map +1 -0
  80. package/lib/esm/lib/getOwnVersion.d.ts +1 -0
  81. package/lib/esm/lib/getOwnVersion.js +8 -0
  82. package/lib/esm/lib/getOwnVersion.js.map +1 -0
  83. package/lib/esm/lib/{args.d.ts → getPositionalArgs.d.ts} +0 -1
  84. package/lib/esm/lib/getPositionalArgs.js +8 -0
  85. package/lib/esm/lib/getPositionalArgs.js.map +1 -0
  86. package/lib/esm/lib/index.d.ts +11 -0
  87. package/lib/esm/lib/index.js +12 -0
  88. package/lib/esm/lib/index.js.map +1 -0
  89. package/lib/esm/lib/listFilesRecursive.d.ts +1 -0
  90. package/lib/esm/lib/listFilesRecursive.js +20 -0
  91. package/lib/esm/lib/listFilesRecursive.js.map +1 -0
  92. package/lib/esm/lib/{prompts.js → promptSelect.js} +1 -1
  93. package/lib/esm/lib/promptSelect.js.map +1 -0
  94. package/lib/esm/lib/readJsonFile.d.ts +1 -0
  95. package/lib/esm/lib/readJsonFile.js +6 -0
  96. package/lib/esm/lib/readJsonFile.js.map +1 -0
  97. package/lib/esm/lib/replaceInFileExact.d.ts +1 -0
  98. package/lib/esm/lib/replaceInFileExact.js +11 -0
  99. package/lib/esm/lib/replaceInFileExact.js.map +1 -0
  100. package/lib/esm/lib/replaceInFiles.d.ts +1 -0
  101. package/lib/esm/lib/replaceInFiles.js +25 -0
  102. package/lib/esm/lib/replaceInFiles.js.map +1 -0
  103. package/lib/esm/lib/writeJsonFile.d.ts +1 -0
  104. package/lib/esm/lib/writeJsonFile.js +5 -0
  105. package/lib/esm/lib/writeJsonFile.js.map +1 -0
  106. package/lib/esm/steps/001_preflight.js +3 -3
  107. package/lib/esm/steps/001_preflight.js.map +1 -1
  108. package/lib/esm/steps/002_copyTemplate.js +1 -1
  109. package/lib/esm/steps/002_copyTemplate.js.map +1 -1
  110. package/lib/esm/steps/003_deleteDocusaurus.js +3 -2
  111. package/lib/esm/steps/003_deleteDocusaurus.js.map +1 -1
  112. package/lib/esm/steps/005_applyAppIdentity.js +6 -3
  113. package/lib/esm/steps/005_applyAppIdentity.js.map +1 -1
  114. package/lib/esm/steps/006_applyDomain.js +3 -1
  115. package/lib/esm/steps/006_applyDomain.js.map +1 -1
  116. package/lib/esm/steps/007_pinRegistryVersions.js +3 -2
  117. package/lib/esm/steps/007_pinRegistryVersions.js.map +1 -1
  118. package/lib/esm/steps/008_transpileToJavaScript.js +10 -8
  119. package/lib/esm/steps/008_transpileToJavaScript.js.map +1 -1
  120. package/lib/esm/steps/009_restoreGitignore.js +1 -1
  121. package/lib/esm/steps/009_restoreGitignore.js.map +1 -1
  122. package/lib/esm/steps/010_gitInit.js +1 -1
  123. package/lib/esm/steps/010_gitInit.js.map +1 -1
  124. package/lib/esm/steps/013_printNextSteps.js +1 -1
  125. package/lib/esm/steps/013_printNextSteps.js.map +1 -1
  126. package/lib/esm/steps/index.js +1 -1
  127. package/lib/esm/steps/index.js.map +1 -1
  128. package/lib/esm/types/AppLanguage.d.ts +4 -0
  129. package/lib/esm/{types.js → types/AppLanguage.js} +1 -1
  130. package/lib/esm/types/AppLanguage.js.map +1 -0
  131. package/lib/esm/types/CreateQpqAppAnswers.d.ts +8 -0
  132. package/lib/esm/types/CreateQpqAppAnswers.js +2 -0
  133. package/lib/esm/types/CreateQpqAppAnswers.js.map +1 -0
  134. package/lib/esm/types/CreateQpqAppStep.d.ts +7 -0
  135. package/lib/esm/types/CreateQpqAppStep.js +2 -0
  136. package/lib/esm/types/CreateQpqAppStep.js.map +1 -0
  137. package/lib/esm/types/StepContext.d.ts +7 -0
  138. package/lib/esm/types/StepContext.js +2 -0
  139. package/lib/esm/types/StepContext.js.map +1 -0
  140. package/lib/esm/types/index.d.ts +4 -0
  141. package/lib/esm/types/index.js +5 -0
  142. package/lib/esm/types/index.js.map +1 -0
  143. package/package.json +5 -4
  144. package/template/docusaurus/docs/actions/core/crypto/_category_.json +7 -0
  145. package/template/docusaurus/docs/actions/core/crypto/ask-crypto-decrypt.md +61 -0
  146. package/template/docusaurus/docs/actions/core/crypto/ask-crypto-encrypt.md +66 -0
  147. package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-scan-all-scopes.md +98 -0
  148. package/template/docusaurus/docs/actions/features/_category_.json +1 -1
  149. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-append-server-event.md +2 -2
  150. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-create.md +2 -2
  151. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-event-append.md +17 -15
  152. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-event-list.md +9 -9
  153. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-event-write.md +7 -7
  154. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-get-by-id.md +5 -5
  155. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-get-draft.md +4 -4
  156. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-provide-store.md +6 -0
  157. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-references.md +47 -0
  158. package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-soft-delete.md +48 -10
  159. package/template/docusaurus/docs/actions/features/event-doc-transfer/_category_.json +1 -0
  160. package/template/docusaurus/docs/actions/features/event-doc-transfer/ask-event-doc-bundle-apply.md +67 -0
  161. package/template/docusaurus/docs/actions/features/event-doc-transfer/ask-event-doc-bundle-plan.md +67 -0
  162. package/template/docusaurus/docs/actions/features/event-doc-transfer/ask-event-doc-manifest.md +64 -0
  163. package/template/docusaurus/docs/actions/features/event-doc-transfer/ask-event-doc-transfer-export.md +67 -0
  164. package/template/docusaurus/docs/actions/xstate/ask-state-machine-send-event.md +6 -6
  165. package/template/docusaurus/docs/config/core/crypto-key.md +53 -0
  166. package/template/docusaurus/docs/config/core/key-value-store.md +53 -1
  167. package/template/docusaurus/docs/config/features/event-doc-routes.md +5 -1
  168. package/template/docusaurus/docs/config/features/event-doc-summary.md +7 -6
  169. package/template/docusaurus/docs/config/features/event-doc-transfer.md +81 -0
  170. package/template/docusaurus/docs/config/features/event-doc.md +1 -0
  171. package/template/docusaurus/docs/config/features/tenanted-event-doc-transfer.md +59 -0
  172. package/template/docusaurus/docs/config/features/tenanted-event-doc.md +1 -0
  173. package/template/docusaurus/docs/config/webserver/migration.md +4 -0
  174. package/template/package.json +1 -0
  175. package/lib/commonjs/lib/args.js +0 -21
  176. package/lib/commonjs/lib/args.js.map +0 -1
  177. package/lib/commonjs/lib/files.d.ts +0 -5
  178. package/lib/commonjs/lib/files.js +0 -65
  179. package/lib/commonjs/lib/files.js.map +0 -1
  180. package/lib/commonjs/lib/packageRoot.js.map +0 -1
  181. package/lib/commonjs/lib/prompts.js.map +0 -1
  182. package/lib/commonjs/types.d.ts +0 -22
  183. package/lib/commonjs/types.js.map +0 -1
  184. package/lib/esm/lib/args.js +0 -16
  185. package/lib/esm/lib/args.js.map +0 -1
  186. package/lib/esm/lib/files.d.ts +0 -5
  187. package/lib/esm/lib/files.js +0 -54
  188. package/lib/esm/lib/files.js.map +0 -1
  189. package/lib/esm/lib/packageRoot.js.map +0 -1
  190. package/lib/esm/lib/prompts.js.map +0 -1
  191. package/lib/esm/types.d.ts +0 -22
  192. package/lib/esm/types.js.map +0 -1
  193. /package/lib/commonjs/lib/{prompts.d.ts → promptSelect.d.ts} +0 -0
  194. /package/lib/esm/lib/{prompts.d.ts → promptSelect.d.ts} +0 -0
@@ -35,10 +35,12 @@ All paths are prefixed with the version segment `/v{version}` (default `/v1`):
35
35
  | `GET` | `{basePath}/{id}` | Get one document's summary record. |
36
36
  | `GET` | `{basePath}/{id}/events` | List a document's event log. |
37
37
  | `GET` | `{basePath}/{id}/render` | Render the document to HTML. **Only mounted when `eventRenderer` is set.** |
38
+ | `GET` | `{basePath}/{id}/references` | List the `EventDocLink`s this document depends on. |
38
39
  | `POST` | `{basePath}` | Create a document. |
39
40
  | `POST` | `{basePath}/{id}/events` | Append an event to a document. |
40
41
  | `POST` | `{basePath}/{id}/assets` | Request an asset upload URL. |
41
42
  | `GET` | `{basePath}/{id}/assets/{assetId}` | Download an asset. |
43
+ | `GET` | `{basePath}/{id}/assets` | List a document's stored assets. |
42
44
  | `DELETE` | `{basePath}/{id}` | Remove a document. |
43
45
 
44
46
  ## Signature
@@ -63,7 +65,8 @@ The single `options` argument is an `EventDocRoutesOptions`:
63
65
  | `onPublish` | `string` | – | Name of a registered inline function. When set, every successful append of a Publish event invokes it with `{ docId, event, summary }`, after the event is durably written and the summary re-derived. This is the seam for syncing a folded document into a materialized read model. Errors propagate to the caller: the event has landed but the side effect did not, so the caller learns the read model may be stale. |
64
66
  | `onAppend` | `string` | – | Name of a registered inline function. When set, EVERY successful append (domain events and lifecycle events alike) invokes it with `{ docId, event, summary, events }`, after the event is durably written and the summary re-derived. This is the seam for reacting to any mutation (e.g. broadcasting the doc's fresh fold). Runs after `onPublish` when both fire on the same Publish event. Errors propagate to the caller: the event has landed but the side effect did not. |
65
67
  | `scopeResolver` | `string` | – | Name of a registered inline function. When set, every route invokes it with `{ event }` before running; a non-null result becomes the ambient storage scope for the whole request, transparently partitioning the collection's stores and assets (e.g. per-tenant). Null means unscoped. Omit for collections that never partition. |
66
- | `excludeRoutes` | `EventDocRouteName[]` | `[]` | Route names to leave unmounted (`'list' \| 'get' \| 'listEvents' \| 'render' \| 'create' \| 'appendEvent' \| 'createAsset' \| 'getAsset' \| 'remove'`). For a collection that must own one of these itself instead of using the stock behavior — e.g. a `create` that must also perform some side effect the stock controller doesn't know about. |
68
+ | `referenceResolver` | `string` | – | Name of a registered inline function. When set, `GET {basePath}/{id}/references` invokes it with the document's `{ events, docId }` log, which folds and returns the `EventDocLink`s that view depends on (e.g. a template's layout/style/content links). The transfer feature's manifest walk follows those links recursively. Omit for a leaf collection (a stylesheet, a layout) — the route is still mounted but always resolves to `[]`. |
69
+ | `excludeRoutes` | `EventDocRouteName[]` | `[]` | Route names to leave unmounted (`'list' \| 'get' \| 'listEvents' \| 'render' \| 'references' \| 'create' \| 'appendEvent' \| 'createAsset' \| 'getAsset' \| 'listAssets' \| 'remove'`). For a collection that must own one of these itself instead of using the stock behavior — e.g. a `create` that must also perform some side effect the stock controller doesn't know about. |
67
70
 
68
71
  ### `RouteAuthSettings`
69
72
 
@@ -100,5 +103,6 @@ export default [
100
103
 
101
104
  - [defineEventDocSummary](./event-doc-summary.md) — declares the store these routes serve (required before mounting routes).
102
105
  - [defineEventDoc](./event-doc.md) — declares the store *and* these routes in one call for the single-type case.
106
+ - [defineEventDocTransfer](./event-doc-transfer.md) — mounts the export/import routes that read every registered collection's `referenceResolver` to build a transfer manifest.
103
107
  - [defineRoute](../webserver/route.md) — the underlying webserver route config (source of `RouteAuthSettings`).
104
108
  - **Custom routes over the same store:** wrap your controllers in `askEventDocProvideStore({ storeName, type }, ...)` (from quidproquo-features) so the generic [reads](../../actions/features/event-doc/ask-event-doc-get-by-id.md) resolve the collection, then compose actions like [askEventDocCreate](../../actions/features/event-doc/ask-event-doc-create.md).
@@ -5,15 +5,16 @@ description: Declare the stores for an event-sourced document collection — its
5
5
 
6
6
  # defineEventDocSummary
7
7
 
8
- Declares the **stores** that back an event-document collection, without any routes. It returns a `QPQConfig` (an array of config settings) that expands to three underlying core stores:
8
+ Declares the **stores** that back an event-document collection, without any routes. It returns a `QPQConfig` (an array of config settings) that expands to four underlying core stores:
9
9
 
10
- 1. A **summary key-value store** (partition key `type`, sort key `id`) — the queryable record for each document, derived by folding the identity/lifecycle events of its log. Rows are the [`EventDocSummary`](../../actions/features/event-doc/ask-event-doc-get-by-id.md) shape and carry the version history. A secondary index on `(type, updatedAt)` supports the recently-updated ordering [askEventDocList](../../actions/features/event-doc/ask-event-doc-list.md) returns.
11
- 2. An **append-only events store** (`<storeName>Events`, partition key `pk`, numeric sort key `sk`) — the ordered log every document is folded from. It has **no** secondary index on purpose: the local dev-server query processor can't target one, so all event reads go through the main table.
12
- 3. A **storage drive** (`<storeName>edocs`, lower-cased) — the collection's blob bucket, holding each document's immutable uploaded assets (and later its derived runtime artifacts) under per-document prefixes.
10
+ 1. A **summary key-value store** (partition key `type`, sort key `id`) — the queryable record for each document, derived by folding the identity/lifecycle events of its log. Rows are the [`EventDocSummary`](../../actions/features/event-doc/ask-event-doc-get-by-id.md) shape and carry the version history. A secondary index on `(type, updatedAt)` supports the recently-updated ordering [askEventDocList](../../actions/features/event-doc/ask-event-doc-list.md) returns. It is a pure projection: nothing on the append path writes it directly. The events store below declares an [`onStream`](../core/key-value-store.md#change-data-capture-onstream) handler that rebuilds a document's summary row from its log whenever an event is appended, so the summary is eventually (not immediately) consistent with the log.
11
+ 2. An **append-only events store** (`<storeName>EventLog`, partition key `pk`, string sort key `sk`) — the live ordered log every document is folded from, keyed on a sortable event id (UUIDv7). It has **no** secondary index on purpose: the local dev-server query processor can't target one, so all event reads go through the main table.
12
+ 3. A **legacy events store** (`<storeName>Events`, partition key `pk`, numeric sort key `sk`) — the pre-sortable-id log, kept declared but unread/unwritten at runtime so its data stays reachable until it is migrated into the events store above.
13
+ 4. A **storage drive** (`<storeName>edocs`, lower-cased) — the collection's blob bucket, holding each document's immutable uploaded assets (and later its derived runtime artifacts) under per-document prefixes.
13
14
 
14
- Point-in-time recovery is enabled on both tables.
15
+ Point-in-time recovery is enabled on all three tables.
15
16
 
16
- - **On AWS:** deploys two DynamoDB tables (via [defineKeyValueStore](../core/key-value-store.md)) and one S3 bucket (via [defineStorageDrive](../core/storage-drive.md)). All physical names are derived from `keyValueStoreName`, so a collection needs only that one name.
17
+ - **On AWS:** deploys three DynamoDB tables (via [defineKeyValueStore](../core/key-value-store.md)) and one S3 bucket (via [defineStorageDrive](../core/storage-drive.md)). All physical names are derived from `keyValueStoreName`, so a collection needs only that one name.
17
18
 
18
19
  ```typescript
19
20
  import { defineEventDocSummary } from 'quidproquo-features';
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: defineEventDocTransfer
3
+ description: Mount export/import routes that move event-doc collections between environments as portable bundles.
4
+ ---
5
+
6
+ # defineEventDocTransfer
7
+
8
+ Mounts the export/import **routes** for a service's event-doc collections: a staging storage drive plus five routes that build, stage, plan, and apply portable bundles of one or more documents (and everything they reference). It returns a `QPQConfig` (an array of config settings) that you spread into a service's infrastructure default export, alongside its `defineEventDoc` calls.
9
+
10
+ An export walks a doc's `referenceResolver` (see [defineEventDocRoutes](./event-doc-routes.md#parameters)) to pull in every doc it depends on — a template's layout, styles, and content — so a bundle is self-contained. An import replays that bundle's event log against the target's own collections, either as a fresh doc or appended onto one that already exists there, and reports what it would do (or did) per document.
11
+
12
+ The route controllers ship inside `quidproquo-features` and read the collection registry from per-route globals, so a service needs no controller wiring of its own.
13
+
14
+ - **On AWS:** deploys a storage drive for staged bundle files and the routes below, each backed by a Lambda running the corresponding built-in controller. It creates no data infrastructure for the collections themselves — those come from each collection's own `defineEventDoc`/`defineEventDocSummary`.
15
+
16
+ ```typescript
17
+ import { defineEventDoc, defineEventDocTransfer } from 'quidproquo-features';
18
+
19
+ const collections = [
20
+ { storeName: 'templates', type: 'template', referenceResolver: 'collectTemplateReferences' },
21
+ { storeName: 'content', type: 'article' },
22
+ ];
23
+
24
+ export default [
25
+ ...defineEventDoc({ storeName: 'templates', type: 'template', basePath: '/templates', referenceResolver: 'collectTemplateReferences' }),
26
+ ...defineEventDoc({ storeName: 'content', type: 'article', basePath: '/articles' }),
27
+
28
+ ...defineEventDocTransfer({
29
+ service: 'cms',
30
+ collections,
31
+ routeAuthSettings: { userDirectoryName: 'editors' },
32
+ }),
33
+ ];
34
+ ```
35
+
36
+ ## Routes mounted
37
+
38
+ All paths are prefixed with the version segment `/v{version}` (default `/v1`):
39
+
40
+ | Method | Path | Purpose |
41
+ | --- | --- | --- |
42
+ | `POST` | `/transfer/manifest` | Every doc that would travel with a given `{ docs: [{ service, type, id }, ...] }` selection, merged and deduped across all of them (follows `referenceResolver` links), without building anything. Feeds an export UI's "these will be included" preview. |
43
+ | `POST` | `/transfer/export` | Stage one bundle for `{ docs: [...] }` and return its download url. |
44
+ | `POST` | `/transfer/upload` | A presigned PUT url for uploading a bundle file, plus the id used to plan/import it. |
45
+ | `POST` | `/transfer/plan` | What importing an uploaded bundle (`{ transferId }`) would do to each of its docs (`new`, `fastForward`, `same`, or a blocking `diverged`/`codeConflict`) without writing anything. |
46
+ | `POST` | `/transfer/import` | Apply an uploaded bundle (`{ transferId, force? }`) and report what happened per doc. `force` opts into overwriting docs the target has edited directly (the divergent tail is backed up to the transfer drive and discarded); off unless the caller asks for it explicitly. |
47
+
48
+ ## Signature
49
+
50
+ ```typescript
51
+ function defineEventDocTransfer(options: EventDocTransferOptions): QPQConfig;
52
+ ```
53
+
54
+ ## Parameters
55
+
56
+ The single `options` argument is an `EventDocTransferOptions`:
57
+
58
+ | Property | Type | Default | Description |
59
+ | --- | --- | --- | --- |
60
+ | `service` | `string` | – (required) | The service name `EventDocLink`s use to address this service's collections (`link.eventDocService`). Transfers never leave it: the stores live here, so a reference into another service throws rather than being silently dropped from a manifest. |
61
+ | `collections` | `EventDocTransferCollection[]` | – (required) | The collections a transfer may read and write. Feed this the same array the service maps over for its `defineEventDoc` calls, so the two cannot drift; the extra fields on a routes-options object (`basePath`, auth, `version`) are simply unused here. |
62
+ | `scopeResolver` | `string` | – | Name of a registered inline function. Establishes the ambient storage scope for the whole request, exactly like a collection's own `scopeResolver`. Needed separately because a transfer spans collections, so there is no single store to read the resolver name from. Omit only if none of the collections partition. |
63
+ | `routeAuthSettings` | `RouteAuthSettings` | – | Auth settings applied to every mounted route (from quidproquo-webserver — see [route](../webserver/route.md)). Import writes unvalidated history and export reads across every registered collection, so gate these harder than the collections' own routes. |
64
+ | `version` | `number` | `1` | Version number for the `/v{version}` path prefix on every route. |
65
+
66
+ ### `EventDocTransferCollection`
67
+
68
+ A subset of a collection's own `EventDocRoutesOptions`, so a service can declare its collections once and feed the same array to both `defineEventDoc` and `defineEventDocTransfer`:
69
+
70
+ | Property | Type | Description |
71
+ | --- | --- | --- |
72
+ | `storeName` | `string` | Must match the collection's own `storeName`. |
73
+ | `type` | `string` | Must match the collection's own `type`. |
74
+ | `onPublish` | `string` | Carried so an imported publish behaves exactly as it does on the collection's own routes. |
75
+ | `onAppend` | `string` | Carried so an imported append fires the same reaction as it does on the collection's own routes. |
76
+ | `referenceResolver` | `string` | Carried so the manifest walk can follow this collection's links. `eventValidator` is deliberately **not** carried — replayed history was already validated at its origin, so imports skip it. |
77
+
78
+ ## Related
79
+
80
+ - [defineEventDoc](./event-doc.md) / [defineEventDocRoutes](./event-doc-routes.md) — declare the collections a transfer reads and writes, including the `referenceResolver` the manifest walk follows.
81
+ - [defineTenantedEventDocTransfer](./tenanted-event-doc-transfer.md) — the same routes with the tenant scope resolver pre-wired, so export/import never cross tenant partitions.
@@ -50,6 +50,7 @@ function defineEventDoc(options: EventDocRoutesOptions): QPQConfig;
50
50
  | `onPublish` | `string` | no | Inline-function name invoked with `{ docId, event, summary }` after every successful Publish append: the seam for syncing the folded document into a materialized read model. |
51
51
  | `onAppend` | `string` | no | Inline-function name invoked with `{ docId, event, summary, events }` after EVERY successful append (domain events and lifecycle events alike): the seam for reacting to any mutation. Runs after `onPublish` when both fire on the same Publish event. |
52
52
  | `scopeResolver` | `string` | no | Inline-function name every route invokes with `{ event }` to resolve the request's ambient storage scope (e.g. per-tenant); null means unscoped. |
53
+ | `referenceResolver` | `string` | no | Inline-function name `GET {basePath}/{id}/references` invokes with the document's `{ events, docId }` log to return the `EventDocLink`s that view depends on. Omit for a leaf collection — the route still resolves, just always to `[]`. |
53
54
 
54
55
  ## Examples
55
56
 
@@ -0,0 +1,59 @@
1
+ ---
2
+ title: defineTenantedEventDocTransfer
3
+ description: A defineEventDocTransfer with the tenant scope resolver pre-wired, so export/import never cross tenant partitions.
4
+ ---
5
+
6
+ # defineTenantedEventDocTransfer
7
+
8
+ A [defineEventDocTransfer](./event-doc-transfer.md) with the tenant scope resolver pre-wired as its `scopeResolver`, so an export reads and an import writes inside the caller's own tenant partition, never unscoped and never across tenants. It takes the same arguments as `defineEventDocTransfer` minus `scopeResolver`, which it fills in for you.
9
+
10
+ The pairing matters: transferring collections declared with [defineTenantedEventDoc](./tenanted-event-doc.md) through an unscoped transfer would read an empty collection and write into the wrong partition. The deploying service must still register the resolver implementation by calling [defineTenant](./tenant.md) (with the same `owner` used everywhere else). Use plain [defineEventDocTransfer](./event-doc-transfer.md) for services whose collections never partition by tenant.
11
+
12
+ ```typescript
13
+ import { defineTenant, defineTenantedEventDoc, defineTenantedEventDocTransfer } from 'quidproquo-features';
14
+
15
+ const collections = [{ storeName: 'content', type: 'article' }];
16
+
17
+ export default [
18
+ ...defineTenant({
19
+ owner: { module: 'cms' },
20
+ basePath: '/tenants',
21
+ routeAuthSettings: { userDirectoryName: 'users' },
22
+ }),
23
+
24
+ ...defineTenantedEventDoc({
25
+ storeName: 'content',
26
+ type: 'article',
27
+ basePath: '/articles',
28
+ routeAuthSettings: { userDirectoryName: 'users' },
29
+ }),
30
+
31
+ ...defineTenantedEventDocTransfer({
32
+ service: 'cms',
33
+ collections,
34
+ routeAuthSettings: { userDirectoryName: 'users' },
35
+ }),
36
+ ];
37
+ ```
38
+
39
+ ## Signature
40
+
41
+ ```typescript
42
+ function defineTenantedEventDocTransfer(options: TenantedEventDocTransferOptions): QPQConfig;
43
+ ```
44
+
45
+ `TenantedEventDocTransferOptions` is `EventDocTransferOptions` with `scopeResolver` omitted — see [defineEventDocTransfer](./event-doc-transfer.md#parameters) for the remaining options.
46
+
47
+ ## Parameters
48
+
49
+ Same as [defineEventDocTransfer](./event-doc-transfer.md#parameters): `service`, `collections`, `routeAuthSettings`, `version` (without `scopeResolver`, which this always sets to `TENANT_SCOPE_RESOLVER_FN`).
50
+
51
+ ## Returns
52
+
53
+ Same as [defineEventDocTransfer](./event-doc-transfer.md) — a `QPQConfig` array with the transfer's `scopeResolver` already pointed at the tenant scope resolver.
54
+
55
+ ## Related
56
+
57
+ - [defineEventDocTransfer](./event-doc-transfer.md) — the underlying define this pre-configures.
58
+ - [defineTenant](./tenant.md) — registers `TENANT_SCOPE_RESOLVER_FN`, the inline function this wires in by name.
59
+ - [defineTenantedEventDoc](./tenanted-event-doc.md) — the same pattern applied to a collection's own routes; pair the two so a transfer's partitioning matches its collections'.
@@ -49,3 +49,4 @@ Same as [defineEventDoc](./event-doc.md) — a `QPQConfig` array with the collec
49
49
  - [defineEventDoc](./event-doc.md) — the underlying define this pre-configures.
50
50
  - [defineTenant](./tenant.md) — registers `TENANT_SCOPE_RESOLVER_FN`, the inline function this wires in by name.
51
51
  - [defineTenantedWebSocketQueue](./tenanted-web-socket-queue.md) — the same pattern applied to a WebSocket queue's `connectionScopeResolver`.
52
+ - [defineTenantedEventDocTransfer](./tenanted-event-doc-transfer.md) — the same pattern applied to a collection's export/import routes.
@@ -32,6 +32,10 @@ The generated deploy event fires on each qualifying stack status change:
32
32
  - **On the first (Create) deploy** every migration is recorded as *already run* without executing it — a freshly created service is expected to start from clean [seed](./seed.md) data, so there is nothing to migrate.
33
33
  - **On later (Update) deploys** each migration whose `deployType` matches the stack that changed and that has **not** already been recorded is enqueued on the `qpqMigrations` queue (which runs its story) and then recorded as run. Migrations already recorded are skipped, so re-deploying is safe.
34
34
 
35
+ ## Testing locally
36
+
37
+ Nothing local ever deploys, so a migration never runs on its own while you develop against `go:dev`. Run `qpq migrate` to execute every pending migration against the local dev store, once, through the same queue the deployed path uses. It records what it ran in the same tracking store, so a second call is a no-op until you add a new migration.
38
+
35
39
  ## Signature
36
40
 
37
41
  ```typescript
@@ -109,6 +109,7 @@
109
109
  "overrides": {
110
110
  "js-yaml": "4.3.0",
111
111
  "uuid": "^11.1.1",
112
+ "adm-zip": "^0.6.0",
112
113
  "@chakra-ui/cli": {
113
114
  "esbuild": "^0.28.1"
114
115
  }
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getPositionalArgs = exports.getArgValue = void 0;
4
- // Flag parsing over the command's argv (everything after the command name).
5
- const getArgValue = (argv, flag) => {
6
- const eq = argv.find((a) => a.startsWith(`${flag}=`));
7
- if (eq)
8
- return eq.slice(`${flag}=`.length);
9
- const idx = argv.indexOf(flag);
10
- return idx >= 0 ? argv[idx + 1] : undefined;
11
- };
12
- exports.getArgValue = getArgValue;
13
- // Positional args = non-flag args, skipping values consumed by known flags.
14
- const getPositionalArgs = (argv, valueFlags) => argv.filter((arg, index) => {
15
- if (arg.startsWith('--'))
16
- return false;
17
- const previous = argv[index - 1];
18
- return !(previous && valueFlags.includes(previous));
19
- });
20
- exports.getPositionalArgs = getPositionalArgs;
21
- //# sourceMappingURL=args.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/lib/args.ts"],"names":[],"mappings":";;;AAAA,4EAA4E;AACrE,MAAM,WAAW,GAAG,CAAC,IAAc,EAAE,IAAY,EAAsB,EAAE;IAC9E,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC,CAAC;AALW,QAAA,WAAW,eAKtB;AAEF,4EAA4E;AACrE,MAAM,iBAAiB,GAAG,CAAC,IAAc,EAAE,UAAoB,EAAY,EAAE,CAClF,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;IACzB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AALQ,QAAA,iBAAiB,qBAKzB","sourcesContent":["// Flag parsing over the command's argv (everything after the command name).\nexport const getArgValue = (argv: string[], flag: string): string | undefined => {\n const eq = argv.find((a) => a.startsWith(`${flag}=`));\n if (eq) return eq.slice(`${flag}=`.length);\n const idx = argv.indexOf(flag);\n return idx >= 0 ? argv[idx + 1] : undefined;\n};\n\n// Positional args = non-flag args, skipping values consumed by known flags.\nexport const getPositionalArgs = (argv: string[], valueFlags: string[]): string[] =>\n argv.filter((arg, index) => {\n if (arg.startsWith('--')) return false;\n const previous = argv[index - 1];\n return !(previous && valueFlags.includes(previous));\n });\n"]}
@@ -1,5 +0,0 @@
1
- export declare const listFilesRecursive: (root: string) => string[];
2
- export declare const replaceInFiles: (root: string, replacements: Array<[from: string, to: string]>) => void;
3
- export declare const replaceInFileExact: (filePath: string, from: string, to: string) => void;
4
- export declare const readJsonFile: <T = any>(filePath: string) => T;
5
- export declare const writeJsonFile: (filePath: string, value: unknown) => void;
@@ -1,65 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.writeJsonFile = exports.readJsonFile = exports.replaceInFileExact = exports.replaceInFiles = exports.listFilesRecursive = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- // Extensions the rename sweeps are allowed to touch — everything else
10
- // (images, favicons, fonts) passes through untouched.
11
- const TEXT_FILE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.html', '.css', '.md', '.yml', '.yaml']);
12
- // Depth-first list of every file under root (root must exist).
13
- const listFilesRecursive = (root) => {
14
- const files = [];
15
- const walk = (dir) => {
16
- for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
17
- const fullPath = path_1.default.join(dir, entry.name);
18
- if (entry.isDirectory()) {
19
- walk(fullPath);
20
- }
21
- else {
22
- files.push(fullPath);
23
- }
24
- }
25
- };
26
- walk(root);
27
- return files;
28
- };
29
- exports.listFilesRecursive = listFilesRecursive;
30
- // Apply literal (non-regex) string replacements to every text file under
31
- // root. Replacement tokens are chosen to be collision-free ('@todo/',
32
- // 'apps/todo/'), so this is a mechanical sweep, not a heuristic one.
33
- const replaceInFiles = (root, replacements) => {
34
- for (const filePath of (0, exports.listFilesRecursive)(root)) {
35
- if (!TEXT_FILE_EXTENSIONS.has(path_1.default.extname(filePath))) {
36
- continue;
37
- }
38
- const original = fs_1.default.readFileSync(filePath, 'utf8');
39
- let updated = original;
40
- for (const [from, to] of replacements) {
41
- updated = updated.split(from).join(to);
42
- }
43
- if (updated !== original) {
44
- fs_1.default.writeFileSync(filePath, updated);
45
- }
46
- }
47
- };
48
- exports.replaceInFiles = replaceInFiles;
49
- // Replace an exact string in one file, throwing if it isn't there — targeted
50
- // edits should fail loudly when the template drifts.
51
- const replaceInFileExact = (filePath, from, to) => {
52
- const original = fs_1.default.readFileSync(filePath, 'utf8');
53
- if (!original.includes(from)) {
54
- throw new Error(`Expected to find "${from}" in ${filePath} — has the template changed?`);
55
- }
56
- fs_1.default.writeFileSync(filePath, original.split(from).join(to));
57
- };
58
- exports.replaceInFileExact = replaceInFileExact;
59
- const readJsonFile = (filePath) => JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
60
- exports.readJsonFile = readJsonFile;
61
- const writeJsonFile = (filePath, value) => {
62
- fs_1.default.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
63
- };
64
- exports.writeJsonFile = writeJsonFile;
65
- //# sourceMappingURL=files.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../src/lib/files.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\n// Extensions the rename sweeps are allowed to touch — everything else\n// (images, favicons, fonts) passes through untouched.\nconst TEXT_FILE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.html', '.css', '.md', '.yml', '.yaml']);\n\n// Depth-first list of every file under root (root must exist).\nexport const listFilesRecursive = (root: string): string[] => {\n const files: string[] = [];\n\n const walk = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else {\n files.push(fullPath);\n }\n }\n };\n\n walk(root);\n return files;\n};\n\n// Apply literal (non-regex) string replacements to every text file under\n// root. Replacement tokens are chosen to be collision-free ('@todo/',\n// 'apps/todo/'), so this is a mechanical sweep, not a heuristic one.\nexport const replaceInFiles = (root: string, replacements: Array<[from: string, to: string]>): void => {\n for (const filePath of listFilesRecursive(root)) {\n if (!TEXT_FILE_EXTENSIONS.has(path.extname(filePath))) {\n continue;\n }\n\n const original = fs.readFileSync(filePath, 'utf8');\n let updated = original;\n for (const [from, to] of replacements) {\n updated = updated.split(from).join(to);\n }\n\n if (updated !== original) {\n fs.writeFileSync(filePath, updated);\n }\n }\n};\n\n// Replace an exact string in one file, throwing if it isn't there — targeted\n// edits should fail loudly when the template drifts.\nexport const replaceInFileExact = (filePath: string, from: string, to: string): void => {\n const original = fs.readFileSync(filePath, 'utf8');\n if (!original.includes(from)) {\n throw new Error(`Expected to find \"${from}\" in ${filePath} — has the template changed?`);\n }\n fs.writeFileSync(filePath, original.split(from).join(to));\n};\n\nexport const readJsonFile = <T = any>(filePath: string): T => JSON.parse(fs.readFileSync(filePath, 'utf8'));\n\nexport const writeJsonFile = (filePath: string, value: unknown): void => {\n fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"packageRoot.js","sourceRoot":"","sources":["../../../src/lib/packageRoot.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AAExB,4EAA4E;AACrE,MAAM,iBAAiB,GAAG,GAAW,EAAE;IAC5C,IAAI,GAAG,GAAG,SAAS,CAAC;IACpB,OAAO,CAAC,YAAE,CAAC,UAAU,CAAC,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,MAAM,GAAG,cAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACxF,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AARW,QAAA,iBAAiB,qBAQ5B;AAEK,MAAM,aAAa,GAAG,GAAW,EAAE;IACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,cAAI,CAAC,IAAI,CAAC,IAAA,yBAAiB,GAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACxG,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC,CAAC;AAHW,QAAA,aAAa,iBAGxB","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\n// This package's root (works from src under ts-node and from lib/commonjs).\nexport const getOwnPackageRoot = (): string => {\n let dir = __dirname;\n while (!fs.existsSync(path.join(dir, 'package.json'))) {\n const parent = path.dirname(dir);\n if (parent === dir) throw new Error('Could not locate the create-qpq-app package root');\n dir = parent;\n }\n return dir;\n};\n\nexport const getOwnVersion = (): string => {\n const packageJson = JSON.parse(fs.readFileSync(path.join(getOwnPackageRoot(), 'package.json'), 'utf8'));\n return packageJson.version;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/lib/prompts.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qEAAqE;AACrE,+EAA+E;AACxE,MAAM,YAAY,GAAG,CAAO,OAAe,EAAE,OAAiB,EAAE,aAAsB,EAAmB,EAAE;IAChH,MAAM,EAAE,MAAM,EAAE,GAAG,wDAAa,mBAAmB,GAAC,CAAC;IACrD,OAAO,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;AAC9D,CAAC,CAAA,CAAC;AAHW,QAAA,YAAY,gBAGvB","sourcesContent":["// Present a single-choice list prompt and return the selected value.\n// @inquirer/prompts is imported lazily so fully-flagged runs never pay for it.\nexport const promptSelect = async (message: string, choices: string[], defaultChoice?: string): Promise<string> => {\n const { select } = await import('@inquirer/prompts');\n return select({ message, choices, default: defaultChoice });\n};\n"]}
@@ -1,22 +0,0 @@
1
- export declare enum AppLanguage {
2
- typescript = "typescript",
3
- javascript = "javascript"
4
- }
5
- export interface CreateQpqAppAnswers {
6
- appName: string;
7
- language: AppLanguage;
8
- domain: string;
9
- initialiseGit: boolean;
10
- installDependencies: boolean;
11
- }
12
- export interface StepContext {
13
- targetDirectory: string;
14
- templateDirectory: string;
15
- ownVersion: string;
16
- answers: CreateQpqAppAnswers;
17
- }
18
- export interface CreateQpqAppStep {
19
- name: string;
20
- shouldRun?: (answers: CreateQpqAppAnswers) => boolean;
21
- run: (context: StepContext) => Promise<void>;
22
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";;;AAAA,IAAY,WAGX;AAHD,WAAY,WAAW;IACrB,wCAAyB,CAAA;IACzB,wCAAyB,CAAA;AAC3B,CAAC,EAHW,WAAW,2BAAX,WAAW,QAGtB","sourcesContent":["export enum AppLanguage {\n typescript = 'typescript',\n javascript = 'javascript',\n}\n\n// Everything the pipeline needs to know, collected ONCE (flags first, prompts\n// for whatever is still missing) before any step runs — steps never prompt.\nexport interface CreateQpqAppAnswers {\n appName: string;\n language: AppLanguage;\n domain: string;\n initialiseGit: boolean;\n installDependencies: boolean;\n}\n\nexport interface StepContext {\n // Absolute path of the directory being scaffolded (<cwd>/<appName>).\n targetDirectory: string;\n // Absolute path of the bundled template snapshot (a pruned quidproquojs.com).\n templateDirectory: string;\n // create-qpq-app's own version — the generated app pins its quidproquo-*\n // dependencies to this (the packages are published in lockstep).\n ownVersion: string;\n answers: CreateQpqAppAnswers;\n}\n\n// A self-contained scaffolding step. The pipeline always runs the same list\n// top to bottom; each step owns its applicability via shouldRun.\nexport interface CreateQpqAppStep {\n name: string;\n shouldRun?: (answers: CreateQpqAppAnswers) => boolean;\n run: (context: StepContext) => Promise<void>;\n}\n"]}
@@ -1,16 +0,0 @@
1
- // Flag parsing over the command's argv (everything after the command name).
2
- export const getArgValue = (argv, flag) => {
3
- const eq = argv.find((a) => a.startsWith(`${flag}=`));
4
- if (eq)
5
- return eq.slice(`${flag}=`.length);
6
- const idx = argv.indexOf(flag);
7
- return idx >= 0 ? argv[idx + 1] : undefined;
8
- };
9
- // Positional args = non-flag args, skipping values consumed by known flags.
10
- export const getPositionalArgs = (argv, valueFlags) => argv.filter((arg, index) => {
11
- if (arg.startsWith('--'))
12
- return false;
13
- const previous = argv[index - 1];
14
- return !(previous && valueFlags.includes(previous));
15
- });
16
- //# sourceMappingURL=args.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/lib/args.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAc,EAAE,IAAY,EAAsB,EAAE;IAC9E,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAc,EAAE,UAAoB,EAAY,EAAE,CAClF,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;IACzB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC","sourcesContent":["// Flag parsing over the command's argv (everything after the command name).\nexport const getArgValue = (argv: string[], flag: string): string | undefined => {\n const eq = argv.find((a) => a.startsWith(`${flag}=`));\n if (eq) return eq.slice(`${flag}=`.length);\n const idx = argv.indexOf(flag);\n return idx >= 0 ? argv[idx + 1] : undefined;\n};\n\n// Positional args = non-flag args, skipping values consumed by known flags.\nexport const getPositionalArgs = (argv: string[], valueFlags: string[]): string[] =>\n argv.filter((arg, index) => {\n if (arg.startsWith('--')) return false;\n const previous = argv[index - 1];\n return !(previous && valueFlags.includes(previous));\n });\n"]}
@@ -1,5 +0,0 @@
1
- export declare const listFilesRecursive: (root: string) => string[];
2
- export declare const replaceInFiles: (root: string, replacements: Array<[from: string, to: string]>) => void;
3
- export declare const replaceInFileExact: (filePath: string, from: string, to: string) => void;
4
- export declare const readJsonFile: <T = any>(filePath: string) => T;
5
- export declare const writeJsonFile: (filePath: string, value: unknown) => void;
@@ -1,54 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- // Extensions the rename sweeps are allowed to touch — everything else
4
- // (images, favicons, fonts) passes through untouched.
5
- const TEXT_FILE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.html', '.css', '.md', '.yml', '.yaml']);
6
- // Depth-first list of every file under root (root must exist).
7
- export const listFilesRecursive = (root) => {
8
- const files = [];
9
- const walk = (dir) => {
10
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
11
- const fullPath = path.join(dir, entry.name);
12
- if (entry.isDirectory()) {
13
- walk(fullPath);
14
- }
15
- else {
16
- files.push(fullPath);
17
- }
18
- }
19
- };
20
- walk(root);
21
- return files;
22
- };
23
- // Apply literal (non-regex) string replacements to every text file under
24
- // root. Replacement tokens are chosen to be collision-free ('@todo/',
25
- // 'apps/todo/'), so this is a mechanical sweep, not a heuristic one.
26
- export const replaceInFiles = (root, replacements) => {
27
- for (const filePath of listFilesRecursive(root)) {
28
- if (!TEXT_FILE_EXTENSIONS.has(path.extname(filePath))) {
29
- continue;
30
- }
31
- const original = fs.readFileSync(filePath, 'utf8');
32
- let updated = original;
33
- for (const [from, to] of replacements) {
34
- updated = updated.split(from).join(to);
35
- }
36
- if (updated !== original) {
37
- fs.writeFileSync(filePath, updated);
38
- }
39
- }
40
- };
41
- // Replace an exact string in one file, throwing if it isn't there — targeted
42
- // edits should fail loudly when the template drifts.
43
- export const replaceInFileExact = (filePath, from, to) => {
44
- const original = fs.readFileSync(filePath, 'utf8');
45
- if (!original.includes(from)) {
46
- throw new Error(`Expected to find "${from}" in ${filePath} — has the template changed?`);
47
- }
48
- fs.writeFileSync(filePath, original.split(from).join(to));
49
- };
50
- export const readJsonFile = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf8'));
51
- export const writeJsonFile = (filePath, value) => {
52
- fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
53
- };
54
- //# sourceMappingURL=files.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../src/lib/files.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\n// Extensions the rename sweeps are allowed to touch — everything else\n// (images, favicons, fonts) passes through untouched.\nconst TEXT_FILE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.html', '.css', '.md', '.yml', '.yaml']);\n\n// Depth-first list of every file under root (root must exist).\nexport const listFilesRecursive = (root: string): string[] => {\n const files: string[] = [];\n\n const walk = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else {\n files.push(fullPath);\n }\n }\n };\n\n walk(root);\n return files;\n};\n\n// Apply literal (non-regex) string replacements to every text file under\n// root. Replacement tokens are chosen to be collision-free ('@todo/',\n// 'apps/todo/'), so this is a mechanical sweep, not a heuristic one.\nexport const replaceInFiles = (root: string, replacements: Array<[from: string, to: string]>): void => {\n for (const filePath of listFilesRecursive(root)) {\n if (!TEXT_FILE_EXTENSIONS.has(path.extname(filePath))) {\n continue;\n }\n\n const original = fs.readFileSync(filePath, 'utf8');\n let updated = original;\n for (const [from, to] of replacements) {\n updated = updated.split(from).join(to);\n }\n\n if (updated !== original) {\n fs.writeFileSync(filePath, updated);\n }\n }\n};\n\n// Replace an exact string in one file, throwing if it isn't there — targeted\n// edits should fail loudly when the template drifts.\nexport const replaceInFileExact = (filePath: string, from: string, to: string): void => {\n const original = fs.readFileSync(filePath, 'utf8');\n if (!original.includes(from)) {\n throw new Error(`Expected to find \"${from}\" in ${filePath} — has the template changed?`);\n }\n fs.writeFileSync(filePath, original.split(from).join(to));\n};\n\nexport const readJsonFile = <T = any>(filePath: string): T => JSON.parse(fs.readFileSync(filePath, 'utf8'));\n\nexport const writeJsonFile = (filePath: string, value: unknown): void => {\n fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"packageRoot.js","sourceRoot":"","sources":["../../../src/lib/packageRoot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,4EAA4E;AAC5E,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAW,EAAE;IAC5C,IAAI,GAAG,GAAG,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACxF,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAW,EAAE;IACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACxG,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC,CAAC","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\n// This package's root (works from src under ts-node and from lib/commonjs).\nexport const getOwnPackageRoot = (): string => {\n let dir = __dirname;\n while (!fs.existsSync(path.join(dir, 'package.json'))) {\n const parent = path.dirname(dir);\n if (parent === dir) throw new Error('Could not locate the create-qpq-app package root');\n dir = parent;\n }\n return dir;\n};\n\nexport const getOwnVersion = (): string => {\n const packageJson = JSON.parse(fs.readFileSync(path.join(getOwnPackageRoot(), 'package.json'), 'utf8'));\n return packageJson.version;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/lib/prompts.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,OAAe,EAAE,OAAiB,EAAE,aAAsB,EAAmB,EAAE;IAChH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;AAC9D,CAAC,CAAC","sourcesContent":["// Present a single-choice list prompt and return the selected value.\n// @inquirer/prompts is imported lazily so fully-flagged runs never pay for it.\nexport const promptSelect = async (message: string, choices: string[], defaultChoice?: string): Promise<string> => {\n const { select } = await import('@inquirer/prompts');\n return select({ message, choices, default: defaultChoice });\n};\n"]}
@@ -1,22 +0,0 @@
1
- export declare enum AppLanguage {
2
- typescript = "typescript",
3
- javascript = "javascript"
4
- }
5
- export interface CreateQpqAppAnswers {
6
- appName: string;
7
- language: AppLanguage;
8
- domain: string;
9
- initialiseGit: boolean;
10
- installDependencies: boolean;
11
- }
12
- export interface StepContext {
13
- targetDirectory: string;
14
- templateDirectory: string;
15
- ownVersion: string;
16
- answers: CreateQpqAppAnswers;
17
- }
18
- export interface CreateQpqAppStep {
19
- name: string;
20
- shouldRun?: (answers: CreateQpqAppAnswers) => boolean;
21
- run: (context: StepContext) => Promise<void>;
22
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,WAGX;AAHD,WAAY,WAAW;IACrB,wCAAyB,CAAA;IACzB,wCAAyB,CAAA;AAC3B,CAAC,EAHW,WAAW,KAAX,WAAW,QAGtB","sourcesContent":["export enum AppLanguage {\n typescript = 'typescript',\n javascript = 'javascript',\n}\n\n// Everything the pipeline needs to know, collected ONCE (flags first, prompts\n// for whatever is still missing) before any step runs — steps never prompt.\nexport interface CreateQpqAppAnswers {\n appName: string;\n language: AppLanguage;\n domain: string;\n initialiseGit: boolean;\n installDependencies: boolean;\n}\n\nexport interface StepContext {\n // Absolute path of the directory being scaffolded (<cwd>/<appName>).\n targetDirectory: string;\n // Absolute path of the bundled template snapshot (a pruned quidproquojs.com).\n templateDirectory: string;\n // create-qpq-app's own version — the generated app pins its quidproquo-*\n // dependencies to this (the packages are published in lockstep).\n ownVersion: string;\n answers: CreateQpqAppAnswers;\n}\n\n// A self-contained scaffolding step. The pipeline always runs the same list\n// top to bottom; each step owns its applicability via shouldRun.\nexport interface CreateQpqAppStep {\n name: string;\n shouldRun?: (answers: CreateQpqAppAnswers) => boolean;\n run: (context: StepContext) => Promise<void>;\n}\n"]}
File without changes