create-qpq-app 0.1.7 → 0.1.9
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.
- package/README.md +4 -4
- package/lib/commonjs/steps/013_printNextSteps.js +2 -2
- package/lib/commonjs/steps/013_printNextSteps.js.map +1 -1
- package/lib/esm/steps/013_printNextSteps.js +2 -2
- package/lib/esm/steps/013_printNextSteps.js.map +1 -1
- package/package.json +2 -2
- package/template/apps/qpqjs/bootstrap.qpq.ts +6 -0
- package/template/docusaurus/docs/actions/core/ai/ask-ai-prompt-stream.md +21 -1
- package/template/docusaurus/docs/actions/core/ai/ask-ai-prompt.md +9 -3
- package/template/docusaurus/docs/actions/core/array/ask-flat-map-parallel-batch.md +1 -1
- package/template/docusaurus/docs/actions/core/array/ask-map-parallel-batch.md +2 -2
- package/template/docusaurus/docs/actions/core/date/date-time-math.md +2 -2
- package/template/docusaurus/docs/actions/core/event/ask-process-event.md +1 -1
- package/template/docusaurus/docs/actions/core/file/ask-file-delete.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-exists.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-generate-temporary-secure-url.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-generate-temporary-upload-secure-url.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-is-cold-storage.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-list-directory.md +4 -1
- package/template/docusaurus/docs/actions/core/file/ask-file-read-binary-contents.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-read-object-json.md +5 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-read-text-contents.md +4 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-stream-open.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-write-binary-contents.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-write-object-json.md +3 -0
- package/template/docusaurus/docs/actions/core/file/ask-file-write-text-contents.md +3 -0
- package/template/docusaurus/docs/actions/core/graph-database/ask-graph-database-internal-field-names.md +1 -1
- package/template/docusaurus/docs/actions/core/json/ask-decode-json.md +1 -0
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-delete.md +9 -1
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-get-all.md +9 -1
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-get.md +9 -1
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-query-single.md +3 -0
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-query.md +4 -1
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-scan-all.md +2 -0
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-scan.md +12 -0
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-update-partial-properties.md +5 -1
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-update.md +9 -1
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-upsert-with-retry.md +1 -1
- package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-upsert.md +3 -0
- package/template/docusaurus/docs/actions/core/network/ask-network-request.md +1 -1
- package/template/docusaurus/docs/actions/core/state/ask-reduce-state.md +1 -1
- package/template/docusaurus/docs/actions/core/state/ask-state-dispatch.md +1 -1
- package/template/docusaurus/docs/actions/core/stream/ask-stream-map.md +1 -1
- package/template/docusaurus/docs/actions/core/stream/ask-stream-process.md +2 -1
- package/template/docusaurus/docs/actions/core/system/ask-trace-story.md +66 -0
- package/template/docusaurus/docs/actions/core/user-directory/ask-user-directory-authenticate-user.md +3 -3
- package/template/docusaurus/docs/actions/core/user-directory/ask-user-directory-set-access-token.md +1 -0
- package/template/docusaurus/docs/actions/features/event-doc/ask-apply-event-doc-event.md +47 -0
- package/template/docusaurus/docs/actions/features/event-doc/ask-event-doc-event-append.md +1 -0
- package/template/docusaurus/docs/actions/features/event-doc-ai/ask-event-doc-ai-process-send.md +11 -8
- package/template/docusaurus/docs/actions/features/web-socket-queue/_category_.json +1 -0
- package/template/docusaurus/docs/actions/{webserver/service → features/web-socket-queue}/ask-service-request.md +4 -3
- package/template/docusaurus/docs/actions/webserver/generic-data-resource/ask-put-generic-data-resource.md +4 -0
- package/template/docusaurus/docs/actions/webserver/generic-data-resource/ask-scan-generic-data-resource.md +4 -0
- package/template/docusaurus/docs/actions/webserver/websocket/ask-websocket-send-message.md +2 -2
- package/template/docusaurus/docs/architecture-overview.md +2 -0
- package/template/docusaurus/docs/config/config-aws/bootstrap-waf.md +36 -0
- package/template/docusaurus/docs/config/core/action-processors.md +24 -2
- package/template/docusaurus/docs/config/core/ai.md +5 -5
- package/template/docusaurus/docs/config/core/bundle-options.md +102 -0
- package/template/docusaurus/docs/config/core/global.md +1 -1
- package/template/docusaurus/docs/config/core/queue.md +1 -1
- package/template/docusaurus/docs/config/features/admin-settings.md +4 -7
- package/template/docusaurus/docs/config/features/event-doc-routes.md +3 -0
- package/template/docusaurus/docs/config/features/event-doc.md +2 -0
- package/template/docusaurus/docs/config/{webserver → features}/state-dispatch-over-websockets.md +2 -2
- package/template/docusaurus/docs/config/features/tenant-stores.md +80 -0
- package/template/docusaurus/docs/config/features/tenant.md +83 -0
- package/template/docusaurus/docs/config/features/tenanted-event-doc.md +51 -0
- package/template/docusaurus/docs/config/features/tenanted-web-socket-queue.md +52 -0
- package/template/docusaurus/docs/config/{webserver → features}/web-socket-queue.md +11 -8
- package/template/docusaurus/docs/config/webserver/websocket.md +3 -3
- package/template/package.json +4 -13
- package/template/docusaurus/docs/actions/webserver/service/_category_.json +0 -1
package/README.md
CHANGED
|
@@ -5,12 +5,12 @@ Scaffold a new [quidproquo](https://github.com/joe-coady/quidproquo) app:
|
|
|
5
5
|
```bash
|
|
6
6
|
npx create-qpq-app my-app
|
|
7
7
|
cd my-app
|
|
8
|
-
npm run
|
|
8
|
+
npm run dev
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
You get a full QPQ workspace with five services
|
|
12
|
-
`shell` and `todo`
|
|
13
|
-
deploys as a single docker image with `npm run
|
|
11
|
+
You get a full QPQ workspace with five services (`admin`, `auth`, `design`,
|
|
12
|
+
`shell` and `todo`) that builds, runs locally on the QPQ dev server, and
|
|
13
|
+
deploys as a single docker image with `npm run deploy`.
|
|
14
14
|
|
|
15
15
|
## Options
|
|
16
16
|
|
|
@@ -23,8 +23,8 @@ deploys as a single docker image.
|
|
|
23
23
|
Next steps:
|
|
24
24
|
|
|
25
25
|
cd ${answers.appName}
|
|
26
|
-
${install} npm run
|
|
27
|
-
npm run
|
|
26
|
+
${install} npm run dev # api on http://localhost:8080, web on http://localhost:3080
|
|
27
|
+
npm run deploy # build the docker image (then run the printed docker command)
|
|
28
28
|
|
|
29
29
|
Deploy config lives in apps/${answers.appName}/deploy.config.json (domain: ${answers.domain}).
|
|
30
30
|
`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"013_printNextSteps.js","sourceRoot":"","sources":["../../../src/steps/013_printNextSteps.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEa,QAAA,cAAc,GAAqB;IAC9C,IAAI,EAAE,MAAM;IAEZ,GAAG,EAAE,KAAoB,EAAE,4CAAf,EAAE,OAAO,EAAE;QACrB,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,CAAC;QAEtF,OAAO,CAAC,GAAG,CAAC;UACN,OAAO,CAAC,OAAO;;;;;;;OAOlB,OAAO,CAAC,OAAO;EACpB,OAAO;;;8BAGqB,OAAO,CAAC,OAAO,gCAAgC,OAAO,CAAC,MAAM;CAC1F,CAAC,CAAC;IACD,CAAC,CAAA;CACF,CAAC","sourcesContent":["import { CreateQpqAppStep } from '../types';\n\nexport const printNextSteps: CreateQpqAppStep = {\n name: 'Done',\n\n run: async ({ answers }) => {\n const install = answers.installDependencies ? '' : ' npm install\\n npm run build\\n';\n\n console.log(`\nCreated ${answers.appName}!\n\nYour app has five services — admin, auth, design, shell and todo — and\ndeploys as a single docker image.\n\nNext steps:\n\n cd ${answers.appName}\n${install} npm run
|
|
1
|
+
{"version":3,"file":"013_printNextSteps.js","sourceRoot":"","sources":["../../../src/steps/013_printNextSteps.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEa,QAAA,cAAc,GAAqB;IAC9C,IAAI,EAAE,MAAM;IAEZ,GAAG,EAAE,KAAoB,EAAE,4CAAf,EAAE,OAAO,EAAE;QACrB,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,CAAC;QAEtF,OAAO,CAAC,GAAG,CAAC;UACN,OAAO,CAAC,OAAO;;;;;;;OAOlB,OAAO,CAAC,OAAO;EACpB,OAAO;;;8BAGqB,OAAO,CAAC,OAAO,gCAAgC,OAAO,CAAC,MAAM;CAC1F,CAAC,CAAC;IACD,CAAC,CAAA;CACF,CAAC","sourcesContent":["import { CreateQpqAppStep } from '../types';\n\nexport const printNextSteps: CreateQpqAppStep = {\n name: 'Done',\n\n run: async ({ answers }) => {\n const install = answers.installDependencies ? '' : ' npm install\\n npm run build\\n';\n\n console.log(`\nCreated ${answers.appName}!\n\nYour app has five services — admin, auth, design, shell and todo — and\ndeploys as a single docker image.\n\nNext steps:\n\n cd ${answers.appName}\n${install} npm run dev # api on http://localhost:8080, web on http://localhost:3080\n npm run deploy # build the docker image (then run the printed docker command)\n\nDeploy config lives in apps/${answers.appName}/deploy.config.json (domain: ${answers.domain}).\n`);\n },\n};\n"]}
|
|
@@ -11,8 +11,8 @@ deploys as a single docker image.
|
|
|
11
11
|
Next steps:
|
|
12
12
|
|
|
13
13
|
cd ${answers.appName}
|
|
14
|
-
${install} npm run
|
|
15
|
-
npm run
|
|
14
|
+
${install} npm run dev # api on http://localhost:8080, web on http://localhost:3080
|
|
15
|
+
npm run deploy # build the docker image (then run the printed docker command)
|
|
16
16
|
|
|
17
17
|
Deploy config lives in apps/${answers.appName}/deploy.config.json (domain: ${answers.domain}).
|
|
18
18
|
`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"013_printNextSteps.js","sourceRoot":"","sources":["../../../src/steps/013_printNextSteps.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAqB;IAC9C,IAAI,EAAE,MAAM;IAEZ,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACzB,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,CAAC;QAEtF,OAAO,CAAC,GAAG,CAAC;UACN,OAAO,CAAC,OAAO;;;;;;;OAOlB,OAAO,CAAC,OAAO;EACpB,OAAO;;;8BAGqB,OAAO,CAAC,OAAO,gCAAgC,OAAO,CAAC,MAAM;CAC1F,CAAC,CAAC;IACD,CAAC;CACF,CAAC","sourcesContent":["import { CreateQpqAppStep } from '../types';\n\nexport const printNextSteps: CreateQpqAppStep = {\n name: 'Done',\n\n run: async ({ answers }) => {\n const install = answers.installDependencies ? '' : ' npm install\\n npm run build\\n';\n\n console.log(`\nCreated ${answers.appName}!\n\nYour app has five services — admin, auth, design, shell and todo — and\ndeploys as a single docker image.\n\nNext steps:\n\n cd ${answers.appName}\n${install} npm run
|
|
1
|
+
{"version":3,"file":"013_printNextSteps.js","sourceRoot":"","sources":["../../../src/steps/013_printNextSteps.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAqB;IAC9C,IAAI,EAAE,MAAM;IAEZ,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACzB,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,CAAC;QAEtF,OAAO,CAAC,GAAG,CAAC;UACN,OAAO,CAAC,OAAO;;;;;;;OAOlB,OAAO,CAAC,OAAO;EACpB,OAAO;;;8BAGqB,OAAO,CAAC,OAAO,gCAAgC,OAAO,CAAC,MAAM;CAC1F,CAAC,CAAC;IACD,CAAC;CACF,CAAC","sourcesContent":["import { CreateQpqAppStep } from '../types';\n\nexport const printNextSteps: CreateQpqAppStep = {\n name: 'Done',\n\n run: async ({ answers }) => {\n const install = answers.installDependencies ? '' : ' npm install\\n npm run build\\n';\n\n console.log(`\nCreated ${answers.appName}!\n\nYour app has five services — admin, auth, design, shell and todo — and\ndeploys as a single docker image.\n\nNext steps:\n\n cd ${answers.appName}\n${install} npm run dev # api on http://localhost:8080, web on http://localhost:3080\n npm run deploy # build the docker image (then run the printed docker command)\n\nDeploy config lives in apps/${answers.appName}/deploy.config.json (domain: ${answers.domain}).\n`);\n },\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-qpq-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Scaffold a new quidproquo app — npx create-qpq-app my-app",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^22.13.13",
|
|
54
|
-
"quidproquo-tsconfig": "0.1.
|
|
54
|
+
"quidproquo-tsconfig": "0.1.9"
|
|
55
55
|
},
|
|
56
56
|
"bin": {
|
|
57
57
|
"create-qpq-app": "./lib/commonjs/bin/createQpqApp.js"
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
defineBootstrapWaf,
|
|
9
9
|
defineDomainCertificate,
|
|
10
10
|
WafManagedRuleGroup,
|
|
11
|
+
WafRuleOverrideAction,
|
|
11
12
|
} from 'quidproquo-config-aws';
|
|
12
13
|
import { QpqAppDeployContext } from 'quidproquo-deploy-awscdk';
|
|
13
14
|
|
|
@@ -20,6 +21,11 @@ export default ({ domain, region }: QpqAppDeployContext): QPQConfig => [
|
|
|
20
21
|
WafManagedRuleGroup.knownBadInputs,
|
|
21
22
|
WafManagedRuleGroup.sqli,
|
|
22
23
|
],
|
|
24
|
+
managedRuleOverrides: {
|
|
25
|
+
[WafManagedRuleGroup.common]: [
|
|
26
|
+
{ name: 'SizeRestrictions_BODY', action: WafRuleOverrideAction.count }, // Disable the 8kb limit
|
|
27
|
+
],
|
|
28
|
+
},
|
|
23
29
|
}),
|
|
24
30
|
|
|
25
31
|
defineApi('api', domain),
|
|
@@ -83,9 +83,29 @@ Within a step, text / reasoning / tool-input events arrive as matched `*Start
|
|
|
83
83
|
| `Raw` (`raw`) | A provider-specific raw chunk, passed through for debugging. |
|
|
84
84
|
| `Custom` (`custom`) | A provider-specific custom event identified by `kind`. |
|
|
85
85
|
|
|
86
|
+
### `AiStreamFinishReasonEnum`
|
|
87
|
+
|
|
88
|
+
`Finish.finishReason` and `FinishStep.finishReason` are an `AiStreamFinishReasonEnum` value, not a raw string.
|
|
89
|
+
|
|
90
|
+
| Member | Wire value | Meaning |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| `stop` | `stop` | The model completed its answer naturally. |
|
|
93
|
+
| `length` | `length` | The response hit the output token limit. |
|
|
94
|
+
| `contentFilter` | `content-filter` | The provider's content filter stopped the response. |
|
|
95
|
+
| `toolCalls` | `tool-calls` | Generation stopped while the model still had tool calls in flight. On the final `Finish` part this means the turn was halted early by a stop condition (e.g. a step limit) rather than finishing naturally, and can be resumed by re-sending the recorded history. |
|
|
96
|
+
| `error` | `error` | The stream errored. |
|
|
97
|
+
| `other` | `other` | The provider reported a reason outside this catalog. |
|
|
98
|
+
| `unknown` | `unknown` | The provider reported no reason, or one this version does not recognise. |
|
|
99
|
+
|
|
86
100
|
## Errors
|
|
87
101
|
|
|
88
|
-
|
|
102
|
+
| Error | When |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `ErrorTypeEnum.NotImplemented` | The `model` has no mapping to an underlying provider model id. |
|
|
105
|
+
| `ErrorTypeEnum.NotFound` | `options.aiName` names an AI config that does not exist. |
|
|
106
|
+
| `ErrorTypeEnum.GenericError` | Any failure while setting up the stream. |
|
|
107
|
+
|
|
108
|
+
Non-fatal issues that arise mid-stream surface as `Error` (`error`) parts rather than throwing. Wrap the initial call with `askCatch` if the model may be unavailable.
|
|
89
109
|
|
|
90
110
|
## Related
|
|
91
111
|
|
|
@@ -8,7 +8,7 @@ description: Send a prompt to a large language model and get the full text respo
|
|
|
8
8
|
Sends a prompt to a large language model and resolves with the model's complete text response. This is the buffered, non-streaming call — the story pauses until the model has finished generating, then resumes with the whole answer. If you want to consume tokens as they arrive, use [askAiPromptStream](./ask-ai-prompt-stream.md) instead.
|
|
9
9
|
|
|
10
10
|
- **Action type:** `AiActionType.Prompt`
|
|
11
|
-
- **On AWS:** runs through the [Vercel AI SDK](https://ai-sdk.dev) (`generateText`) against Amazon Bedrock. Any tools declared on the matching [defineAi](../../../config/core/ai.md) config are made available to the model, and the processor runs up to
|
|
11
|
+
- **On AWS:** runs through the [Vercel AI SDK](https://ai-sdk.dev) (`generateText`) against Amazon Bedrock. Any tools declared on the matching [defineAi](../../../config/core/ai.md) config are made available to the model, and the processor runs up to 20 tool-calling steps before returning.
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
14
|
import { askAiPrompt, AiModel } from 'quidproquo-core';
|
|
@@ -85,7 +85,7 @@ type AiToolMessage = { role: 'tool'; content: AiToolResultPart[] };
|
|
|
85
85
|
| --- | --- | --- |
|
|
86
86
|
| `text` | `text` | Plain text. |
|
|
87
87
|
| `file` (URL) | `url`, `mediaType`, `filename?` | Attach a file by URL. |
|
|
88
|
-
| `file` (drive) | `drive`, `filepath`, `mediaType`, `filename?` | Attach a file from a [storage drive](../../../config/core/storage-drive.md). The processor resolves the contents at prompt time, so no presigned URL ever lands in logs or session state. |
|
|
88
|
+
| `file` (drive) | `drive`, `filepath`, `scope?`, `mediaType`, `filename?` | Attach a file from a [storage drive](../../../config/core/storage-drive.md). The processor resolves the contents at prompt time, so no presigned URL ever lands in logs or session state. Set `scope` when the file lives under a tenant scope; it is forwarded to the file read. |
|
|
89
89
|
| `tool-call` | `toolCallId`, `toolName`, `input` | An assistant turn's request to call a tool. |
|
|
90
90
|
| `reasoning` | `text`, `providerOptions?` | An assistant turn's thinking block. |
|
|
91
91
|
| `tool-result` | `toolCallId`, `toolName`, `output`, `isError?` | The result you feed back for a tool call (in a `tool` message). |
|
|
@@ -104,7 +104,13 @@ interface AiReasoningConfig {
|
|
|
104
104
|
|
|
105
105
|
## Errors
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
| Error | When |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `ErrorTypeEnum.NotImplemented` | The `model` has no mapping to an underlying provider model id. |
|
|
110
|
+
| `ErrorTypeEnum.NotFound` | `options.aiName` names an AI config that does not exist. |
|
|
111
|
+
| `ErrorTypeEnum.GenericError` | Any failure while generating, with the underlying provider message. |
|
|
112
|
+
|
|
113
|
+
Catch failures with `askCatch`, which returns an `EitherActionResult` — `{ success: true, result }` or `{ success: false, error }`:
|
|
108
114
|
|
|
109
115
|
```typescript
|
|
110
116
|
import { askCatch, askAiPrompt, AiModel } from 'quidproquo-core';
|
|
@@ -47,7 +47,7 @@ function* askFlatMapParallelBatch<T, R>(
|
|
|
47
47
|
| Parameter | Type | Default | Description |
|
|
48
48
|
| --- | --- | --- | --- |
|
|
49
49
|
| `items` | `T[]` | — | The array to iterate. |
|
|
50
|
-
| `numBatch` | `number` | — | Batch size — the maximum number of items that run **concurrently** at once. |
|
|
50
|
+
| `numBatch` | `number` | — | Batch size — the maximum number of items that run **concurrently** at once. Must be at least `1`; anything lower (including `NaN`) throws an `InvalidBatchSizeError` (code `notPositive`). |
|
|
51
51
|
| `askCallback` | `(item: T, index: number, srcArray: T[]) => AskResponse<R[]>` | — | A generator called once per item. Receives the item, its zero-based index, and the source array, and returns an **array** of results. |
|
|
52
52
|
| `delayAfterEachBatchMs` | `number` | `0` | Optional pause (in milliseconds) inserted after every batch, including the last — passed straight through to [askMapParallelBatch](./ask-map-parallel-batch.md). |
|
|
53
53
|
|
|
@@ -45,8 +45,8 @@ function* askMapParallelBatch<T, R>(
|
|
|
45
45
|
| Parameter | Type | Default | Description |
|
|
46
46
|
| --- | --- | --- | --- |
|
|
47
47
|
| `items` | `T[]` | — | The array to iterate. |
|
|
48
|
-
| `numBatch` | `number` | — | Batch size — the maximum number of items that run **concurrently** at once. The array is consumed `numBatch` items at a time; each slice runs in parallel and must finish before the next slice begins. |
|
|
49
|
-
| `askCallback` | `(item: T, index: number, srcArray: T[]) => AskResponse<R>` | — | A generator called once per item. Receives the item, its zero-based index, and the
|
|
48
|
+
| `numBatch` | `number` | — | Batch size — the maximum number of items that run **concurrently** at once. The array is consumed `numBatch` items at a time; each slice runs in parallel and must finish before the next slice begins. Must be at least `1`; anything lower (including `NaN`) throws an `InvalidBatchSizeError` (code `notPositive`) rather than looping forever. |
|
|
49
|
+
| `askCallback` | `(item: T, index: number, srcArray: T[]) => AskResponse<R>` | — | A generator called once per item. Receives the item, its zero-based index in the **original** `items` array, and the original array itself (not the batch slice). |
|
|
50
50
|
| `delayAfterEachBatchMs` | `number` | `0` | Optional pause (in milliseconds) inserted **after every batch**, including the last. When greater than `0`, the helper `yield*`s [askDelay](../platform/ask-delay.md) between batches — useful for spacing out calls to a throttled service. |
|
|
51
51
|
|
|
52
52
|
## Returns
|
|
@@ -20,7 +20,7 @@ export function* askSevenDaysFromNow() {
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
:::note
|
|
23
|
-
Pass a negative amount to move backward in time, e.g. `addDaysToTDateIso(now, -7)` returns the timestamp seven days ago. All helpers accept and return a plain `string`; the returned value is always normalised to UTC (`...Z`) by `toISOString()`.
|
|
23
|
+
Pass a negative amount to move backward in time, e.g. `addDaysToTDateIso(now, -7)` returns the timestamp seven days ago. All helpers accept and return a plain `string`; the returned value is always normalised to UTC (`...Z`) by `toISOString()`. The arithmetic itself uses the UTC `Date` accessors, so the result never depends on the server's local timezone or its DST transitions.
|
|
24
24
|
:::
|
|
25
25
|
|
|
26
26
|
## addMillisecondsToTDateIso
|
|
@@ -66,7 +66,7 @@ function addMonthsToTDateIso(startDate: string, numMonths: number): string;
|
|
|
66
66
|
| `startDate` | `string` | The ISO-8601 timestamp to shift from. |
|
|
67
67
|
| `numMonths` | `number` | Months to add (negative to subtract). |
|
|
68
68
|
|
|
69
|
-
Returns a `string` — the shifted timestamp in UTC ISO-8601 form. Uses JavaScript's `Date.
|
|
69
|
+
Returns a `string` — the shifted timestamp in UTC ISO-8601 form. Uses JavaScript's `Date.setUTCMonth`, so day-of-month overflow rolls over: adding one month to `2026-01-31` yields early March, since February has no 31st.
|
|
70
70
|
|
|
71
71
|
## addYearsToTDateIso
|
|
72
72
|
|
|
@@ -65,7 +65,7 @@ Instead of processing records in parallel, it walks them **one at a time**. When
|
|
|
65
65
|
|
|
66
66
|
## The pipeline actions
|
|
67
67
|
|
|
68
|
-
The individual `askEvent*` actions above are **extension points**, not story-author API — each service type implements them in its action-processor package (e.g. the HTTP API event processors in `quidproquo-webserver`). You compose stories that get *matched and executed* by this pipeline; you don't yield these actions directly. Related transform hooks used by specific service types include `askEventTransformEventParams`, `askEventTransformEventRecord`, and `askEventResolveCaughtError`.
|
|
68
|
+
The individual `askEvent*` actions above are **extension points**, not story-author API — each service type implements them in its action-processor package (e.g. the HTTP API event processors in `quidproquo-webserver`). You compose stories that get *matched and executed* by this pipeline; you don't yield these actions directly. Related transform hooks used by specific service types include `askEventTransformEventParams`, `askEventTransformEventRecord`, `askEventTransformEventRecordResponse`, and `askEventResolveCaughtError`.
|
|
69
69
|
|
|
70
70
|
## Related
|
|
71
71
|
|
|
@@ -24,6 +24,7 @@ export function* askRemoveOldExports() {
|
|
|
24
24
|
function* askFileDelete(
|
|
25
25
|
drive: string,
|
|
26
26
|
filepaths: string[],
|
|
27
|
+
scope?: string,
|
|
27
28
|
): AskResponse<string[]>;
|
|
28
29
|
```
|
|
29
30
|
|
|
@@ -33,6 +34,7 @@ function* askFileDelete(
|
|
|
33
34
|
| --- | --- | --- |
|
|
34
35
|
| `drive` | `string` | Name of the storage drive to delete from — must match a drive declared with [defineStorageDrive](../../../config/core/storage-drive.md) (or one shared via its `owner` option). |
|
|
35
36
|
| `filepaths` | `string[]` | The paths of the files to delete within the drive, forward-slash delimited. Pass one path or many in a single call. |
|
|
37
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor deletes `{scope}/{filepath}` for every path in `filepaths`, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
36
38
|
|
|
37
39
|
## Returns
|
|
38
40
|
|
|
@@ -44,6 +46,7 @@ function* askFileDelete(
|
|
|
44
46
|
| --- | --- |
|
|
45
47
|
| `FileDeleteErrorTypeEnum.AccessDenied` | The caller lacks permission to delete from this drive (e.g. a foreign drive shared without write access). |
|
|
46
48
|
| `FileDeleteErrorTypeEnum.DriveNotFound` | No storage drive with that name exists in the deployed config. |
|
|
49
|
+
| `FileDeleteErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or one of the scoped `filepaths` is absolute or contains `..` segments or null bytes. |
|
|
47
50
|
|
|
48
51
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
49
52
|
|
|
@@ -28,6 +28,7 @@ export function* askEnsureSeedFile() {
|
|
|
28
28
|
function* askFileExists(
|
|
29
29
|
drive: string,
|
|
30
30
|
filepath: string,
|
|
31
|
+
scope?: string,
|
|
31
32
|
): AskResponse<boolean>;
|
|
32
33
|
```
|
|
33
34
|
|
|
@@ -37,6 +38,7 @@ function* askFileExists(
|
|
|
37
38
|
| --- | --- | --- |
|
|
38
39
|
| `drive` | `string` | Name of the storage drive to check — must match a drive declared with [defineStorageDrive](../../../config/core/storage-drive.md) (or one shared via its `owner` option). |
|
|
39
40
|
| `filepath` | `string` | Path of the file within the drive, forward-slash delimited, e.g. `'uploads/avatar.png'`. |
|
|
41
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor checks `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
40
42
|
|
|
41
43
|
## Returns
|
|
42
44
|
|
|
@@ -47,6 +49,7 @@ function* askFileExists(
|
|
|
47
49
|
| Error | Meaning |
|
|
48
50
|
| --- | --- |
|
|
49
51
|
| `FileExistsErrorTypeEnum.AccessDenied` | The caller lacks permission to check existence on this drive. |
|
|
52
|
+
| `FileExistsErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
50
53
|
|
|
51
54
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
52
55
|
|
package/template/docusaurus/docs/actions/core/file/ask-file-generate-temporary-secure-url.md
CHANGED
|
@@ -27,6 +27,7 @@ function* askFileGenerateTemporarySecureUrl(
|
|
|
27
27
|
drive: string,
|
|
28
28
|
filepath: string,
|
|
29
29
|
expirationMs: number,
|
|
30
|
+
scope?: string,
|
|
30
31
|
): AskResponse<string>;
|
|
31
32
|
```
|
|
32
33
|
|
|
@@ -37,6 +38,7 @@ function* askFileGenerateTemporarySecureUrl(
|
|
|
37
38
|
| `drive` | `string` | Name of the storage drive the file lives on — must match a drive declared with [defineStorageDrive](../../../config/core/storage-drive.md) (or one shared via its `owner` option). |
|
|
38
39
|
| `filepath` | `string` | Path of the file within the drive, forward-slash delimited. |
|
|
39
40
|
| `expirationMs` | `number` | How long the URL stays valid, in milliseconds. Must not exceed 7 days (`604800000` ms). |
|
|
41
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the signed URL targets `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
40
42
|
|
|
41
43
|
## Returns
|
|
42
44
|
|
|
@@ -47,6 +49,7 @@ function* askFileGenerateTemporarySecureUrl(
|
|
|
47
49
|
| Error | Meaning |
|
|
48
50
|
| --- | --- |
|
|
49
51
|
| `FileGenerateTemporarySecureUrlErrorTypeEnum.ExpirationTooLong` | The requested `expirationMs` exceeds the 7 day maximum for signed URLs. |
|
|
52
|
+
| `FileGenerateTemporarySecureUrlErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
50
53
|
|
|
51
54
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
52
55
|
|
package/template/docusaurus/docs/actions/core/file/ask-file-generate-temporary-upload-secure-url.md
CHANGED
|
@@ -35,6 +35,7 @@ function* askFileGenerateTemporaryUploadSecureUrl(
|
|
|
35
35
|
advancedOptions?: {
|
|
36
36
|
contentType?: string;
|
|
37
37
|
},
|
|
38
|
+
scope?: string,
|
|
38
39
|
): AskResponse<string>;
|
|
39
40
|
```
|
|
40
41
|
|
|
@@ -46,6 +47,7 @@ function* askFileGenerateTemporaryUploadSecureUrl(
|
|
|
46
47
|
| `filepath` | `string` | Destination path within the drive the client is allowed to write to, forward-slash delimited. |
|
|
47
48
|
| `expirationMs` | `number` | How long the URL stays valid, in milliseconds. Must not exceed 7 days (`604800000` ms). |
|
|
48
49
|
| `advancedOptions` | `object` | Optional upload options — see below. |
|
|
50
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the signed URL targets `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
49
51
|
|
|
50
52
|
### `advancedOptions`
|
|
51
53
|
|
|
@@ -62,6 +64,7 @@ function* askFileGenerateTemporaryUploadSecureUrl(
|
|
|
62
64
|
| Error | Meaning |
|
|
63
65
|
| --- | --- |
|
|
64
66
|
| `FileGenerateTemporaryUploadSecureUrlErrorTypeEnum.ExpirationTooLong` | The requested `expirationMs` exceeds the 7 day maximum for signed URLs. |
|
|
67
|
+
| `FileGenerateTemporaryUploadSecureUrlErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
65
68
|
|
|
66
69
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
67
70
|
|
|
@@ -31,6 +31,7 @@ export function* askReadArchivableReport(filepath: string) {
|
|
|
31
31
|
function* askFileIsColdStorage(
|
|
32
32
|
drive: string,
|
|
33
33
|
filepath: string,
|
|
34
|
+
scope?: string,
|
|
34
35
|
): AskResponse<boolean>;
|
|
35
36
|
```
|
|
36
37
|
|
|
@@ -40,6 +41,7 @@ function* askFileIsColdStorage(
|
|
|
40
41
|
| --- | --- | --- |
|
|
41
42
|
| `drive` | `string` | Name of the storage drive the file lives on — must match a drive declared with [defineStorageDrive](../../../config/core/storage-drive.md) (or one shared via its `owner` option). |
|
|
42
43
|
| `filepath` | `string` | Path of the file within the drive, forward-slash delimited. |
|
|
44
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor checks `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
43
45
|
|
|
44
46
|
## Returns
|
|
45
47
|
|
|
@@ -52,6 +54,7 @@ function* askFileIsColdStorage(
|
|
|
52
54
|
| `FileIsColdStorageErrorTypeEnum.AccessDenied` | The caller lacks permission to read the file's metadata. |
|
|
53
55
|
| `FileIsColdStorageErrorTypeEnum.FileNotFound` | No file exists at the given path. |
|
|
54
56
|
| `FileIsColdStorageErrorTypeEnum.DriveNotFound` | No storage drive with that name exists in the deployed config. |
|
|
57
|
+
| `FileIsColdStorageErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
55
58
|
|
|
56
59
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
57
60
|
|
|
@@ -32,6 +32,7 @@ function* askFileListDirectory(
|
|
|
32
32
|
folderPath: string,
|
|
33
33
|
maxFiles?: number,
|
|
34
34
|
pageToken?: string,
|
|
35
|
+
scope?: string,
|
|
35
36
|
): AskResponse<DirectoryList>;
|
|
36
37
|
```
|
|
37
38
|
|
|
@@ -43,6 +44,7 @@ function* askFileListDirectory(
|
|
|
43
44
|
| `folderPath` | `string` | The folder prefix to list under, forward-slash delimited, e.g. `'exports/'`. |
|
|
44
45
|
| `maxFiles` | `number` | Maximum entries to return in this page. Defaults to `1000`. |
|
|
45
46
|
| `pageToken` | `string` | Token returned by a previous call to fetch the next page. Omit for the first page. |
|
|
47
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor lists under `{scope}/{folderPath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Returned `filepath`s are relative to the scope, matching what you would pass back in. Must be a single path segment: no separators, `..`, or null bytes. |
|
|
46
48
|
|
|
47
49
|
## Returns
|
|
48
50
|
|
|
@@ -72,6 +74,7 @@ When `pageToken` is `undefined`, the last page has been returned.
|
|
|
72
74
|
| `FileListDirectoryErrorTypeEnum.DirectoryNotFound` | No directory exists at the given `folderPath`. |
|
|
73
75
|
| `FileListDirectoryErrorTypeEnum.NotADirectory` | The `folderPath` points at a file rather than a directory. |
|
|
74
76
|
| `FileListDirectoryErrorTypeEnum.DriveNotFound` | No storage drive with that name exists in the deployed config. |
|
|
77
|
+
| `FileListDirectoryErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `folderPath` is absolute or contains `..` segments or null bytes. |
|
|
75
78
|
|
|
76
79
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
77
80
|
|
|
@@ -87,7 +90,7 @@ if (outcome.success) {
|
|
|
87
90
|
|
|
88
91
|
## Notes
|
|
89
92
|
|
|
90
|
-
- To pull **every** page in one call, use `askFileListAllDirectory(drive, folderPath)` — a companion requester exported from quidproquo-core that loops internally and returns a flat `FileInfo[]`. Prefer paging with `askFileListDirectory` for large directories.
|
|
93
|
+
- To pull **every** page in one call, use `askFileListAllDirectory(drive, folderPath, scope?)` — a companion requester exported from quidproquo-core that loops internally and returns a flat `FileInfo[]`. Prefer paging with `askFileListDirectory` for large directories.
|
|
91
94
|
|
|
92
95
|
## Related
|
|
93
96
|
|
|
@@ -26,6 +26,7 @@ export function* askGetLogo() {
|
|
|
26
26
|
function* askFileReadBinaryContents(
|
|
27
27
|
drive: string,
|
|
28
28
|
filepath: string,
|
|
29
|
+
scope?: string,
|
|
29
30
|
): AskResponse<QPQBinaryData>;
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -35,6 +36,7 @@ function* askFileReadBinaryContents(
|
|
|
35
36
|
| --- | --- | --- |
|
|
36
37
|
| `drive` | `string` | Name of the storage drive to read from — must match a drive declared with [defineStorageDrive](../../../config/core/storage-drive.md) (or one shared via its `owner` option). |
|
|
37
38
|
| `filepath` | `string` | Path of the file within the drive, forward-slash delimited. |
|
|
39
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor reads `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
38
40
|
|
|
39
41
|
## Returns
|
|
40
42
|
|
|
@@ -55,6 +57,7 @@ interface QPQBinaryData {
|
|
|
55
57
|
| --- | --- |
|
|
56
58
|
| `FileReadBinaryContentsErrorTypeEnum.InvalidStorageClass` | The file is in a cold storage tier and cannot be read directly. Check first with [askFileIsColdStorage](./ask-file-is-cold-storage.md). |
|
|
57
59
|
| `FileReadBinaryContentsErrorTypeEnum.FileNotFound` | No file exists at the given path. |
|
|
60
|
+
| `FileReadBinaryContentsErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
58
61
|
|
|
59
62
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
60
63
|
|
|
@@ -30,6 +30,7 @@ export function* askGetSettings() {
|
|
|
30
30
|
function* askFileReadObjectJson<T extends object>(
|
|
31
31
|
drive: string,
|
|
32
32
|
filepath: string,
|
|
33
|
+
scope?: string,
|
|
33
34
|
): AskResponse<T>;
|
|
34
35
|
```
|
|
35
36
|
|
|
@@ -39,6 +40,7 @@ function* askFileReadObjectJson<T extends object>(
|
|
|
39
40
|
| --- | --- | --- |
|
|
40
41
|
| `drive` | `string` | Name of the storage drive to read from — must match a drive declared with [defineStorageDrive](../../../config/core/storage-drive.md) (or one shared via its `owner` option). |
|
|
41
42
|
| `filepath` | `string` | Path of the JSON file within the drive, forward-slash delimited. |
|
|
43
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor reads `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
42
44
|
|
|
43
45
|
The type parameter `T` is the shape you expect the parsed JSON to have. It is a compile-time annotation only — the contents are not validated against it at runtime.
|
|
44
46
|
|
|
@@ -51,6 +53,9 @@ The type parameter `T` is the shape you expect the parsed JSON to have. It is a
|
|
|
51
53
|
| Error | Meaning |
|
|
52
54
|
| --- | --- |
|
|
53
55
|
| `FileReadObjectJsonErrorTypeEnum.InvalidStorageClass` | The file is in a cold storage tier and cannot be read directly. Check first with [askFileIsColdStorage](./ask-file-is-cold-storage.md). |
|
|
56
|
+
| `FileReadObjectJsonErrorTypeEnum.FileNotFound` | No file exists at the given `filepath` on the drive. |
|
|
57
|
+
| `FileReadObjectJsonErrorTypeEnum.InvalidJson` | The file exists but its contents are not parseable JSON. |
|
|
58
|
+
| `FileReadObjectJsonErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
54
59
|
|
|
55
60
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
56
61
|
|
|
@@ -24,6 +24,7 @@ export function* askGetWelcomeMessage() {
|
|
|
24
24
|
function* askFileReadTextContents(
|
|
25
25
|
drive: string,
|
|
26
26
|
filepath: string,
|
|
27
|
+
scope?: string,
|
|
27
28
|
): AskResponse<string>;
|
|
28
29
|
```
|
|
29
30
|
|
|
@@ -33,6 +34,7 @@ function* askFileReadTextContents(
|
|
|
33
34
|
| --- | --- | --- |
|
|
34
35
|
| `drive` | `string` | Name of the storage drive to read from — must match a drive declared with [defineStorageDrive](../../../config/core/storage-drive.md) (or one shared via its `owner` option). |
|
|
35
36
|
| `filepath` | `string` | Path of the file within the drive. Always use forward slashes, e.g. `'reports/2026/summary.txt'`. |
|
|
37
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor reads `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
36
38
|
|
|
37
39
|
## Returns
|
|
38
40
|
|
|
@@ -43,6 +45,8 @@ function* askFileReadTextContents(
|
|
|
43
45
|
| Error | Meaning |
|
|
44
46
|
| --- | --- |
|
|
45
47
|
| `FileReadTextContentsErrorTypeEnum.InvalidStorageClass` | The file is in a cold storage tier and cannot be read directly. Check first with `askFileIsColdStorage`. |
|
|
48
|
+
| `FileReadTextContentsErrorTypeEnum.FileNotFound` | No file exists at the given `filepath` on the drive. |
|
|
49
|
+
| `FileReadTextContentsErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
46
50
|
|
|
47
51
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
48
52
|
|
|
@@ -42,6 +42,7 @@ function* askFileStreamOpen<E extends StreamEncoding = 'text'>(
|
|
|
42
42
|
filepath: string,
|
|
43
43
|
encoding?: E,
|
|
44
44
|
chunkSize?: number,
|
|
45
|
+
scope?: string,
|
|
45
46
|
): AskResponse<StreamHandle<E>>;
|
|
46
47
|
```
|
|
47
48
|
|
|
@@ -53,6 +54,7 @@ function* askFileStreamOpen<E extends StreamEncoding = 'text'>(
|
|
|
53
54
|
| `filepath` | `string` | Path of the file within the drive, forward-slash delimited. |
|
|
54
55
|
| `encoding` | `StreamEncoding` | How each chunk is decoded when you read it. `'text'` (default) yields `string`, `'binary'` yields `Uint8Array`, `'json'` parses each chunk. |
|
|
55
56
|
| `chunkSize` | `number` | Bytes per chunk. Defaults to `65536` (64 KiB) in the AWS runtime. |
|
|
57
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor opens `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
56
58
|
|
|
57
59
|
`StreamEncoding` is `'text' | 'binary' | 'json'`.
|
|
58
60
|
|
|
@@ -75,6 +77,7 @@ The handle's `encoding` type parameter flows through to `askStreamRead`, so a `'
|
|
|
75
77
|
| --- | --- |
|
|
76
78
|
| `FileStreamOpenErrorTypeEnum.InvalidStorageClass` | The file is in a cold storage tier and cannot be streamed directly. Check first with [askFileIsColdStorage](./ask-file-is-cold-storage.md). |
|
|
77
79
|
| `FileStreamOpenErrorTypeEnum.FileNotFound` | No file exists at the given path. |
|
|
80
|
+
| `FileStreamOpenErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
78
81
|
|
|
79
82
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
80
83
|
|
|
@@ -30,6 +30,7 @@ function* askFileWriteBinaryContents(
|
|
|
30
30
|
filepath: string,
|
|
31
31
|
data: QPQBinaryData,
|
|
32
32
|
storageDriveAdvancedWriteOptions?: StorageDriveAdvancedWriteOptions,
|
|
33
|
+
scope?: string,
|
|
33
34
|
): AskResponse<void>;
|
|
34
35
|
```
|
|
35
36
|
|
|
@@ -41,6 +42,7 @@ function* askFileWriteBinaryContents(
|
|
|
41
42
|
| `filepath` | `string` | Destination path within the drive, forward-slash delimited. Parent "directories" are implicit — no need to create them. |
|
|
42
43
|
| `data` | `QPQBinaryData` | The binary payload to write — see below. |
|
|
43
44
|
| `storageDriveAdvancedWriteOptions` | `StorageDriveAdvancedWriteOptions` | Optional write options — see below. |
|
|
45
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor writes `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
44
46
|
|
|
45
47
|
### `QPQBinaryData`
|
|
46
48
|
|
|
@@ -69,6 +71,7 @@ interface QPQBinaryData {
|
|
|
69
71
|
| --- | --- |
|
|
70
72
|
| `FileWriteBinaryContentsErrorTypeEnum.AccessDenied` | The caller lacks permission to write to this drive (e.g. a foreign drive shared without write access). |
|
|
71
73
|
| `FileWriteBinaryContentsErrorTypeEnum.DriveNotFound` | No storage drive with that name exists in the deployed config. |
|
|
74
|
+
| `FileWriteBinaryContentsErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
72
75
|
|
|
73
76
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
74
77
|
|
|
@@ -31,6 +31,7 @@ function* askFileWriteObjectJson<T extends object>(
|
|
|
31
31
|
filepath: string,
|
|
32
32
|
data: T,
|
|
33
33
|
storageDriveAdvancedWriteOptions?: StorageDriveAdvancedWriteOptions,
|
|
34
|
+
scope?: string,
|
|
34
35
|
): AskResponse<void>;
|
|
35
36
|
```
|
|
36
37
|
|
|
@@ -42,6 +43,7 @@ function* askFileWriteObjectJson<T extends object>(
|
|
|
42
43
|
| `filepath` | `string` | Destination path within the drive, forward-slash delimited. Parent "directories" are implicit — no need to create them. |
|
|
43
44
|
| `data` | `T` | The object to serialize and write. It is stored as `JSON.stringify(data)`. |
|
|
44
45
|
| `storageDriveAdvancedWriteOptions` | `StorageDriveAdvancedWriteOptions` | Optional write options — see below. |
|
|
46
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor writes `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
45
47
|
|
|
46
48
|
### `StorageDriveAdvancedWriteOptions`
|
|
47
49
|
|
|
@@ -59,6 +61,7 @@ function* askFileWriteObjectJson<T extends object>(
|
|
|
59
61
|
| --- | --- |
|
|
60
62
|
| `FileWriteObjectJsonErrorTypeEnum.AccessDenied` | The caller lacks permission to write to this drive (e.g. a foreign drive shared without write access). |
|
|
61
63
|
| `FileWriteObjectJsonErrorTypeEnum.DriveNotFound` | No storage drive with that name exists in the deployed config. |
|
|
64
|
+
| `FileWriteObjectJsonErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
62
65
|
|
|
63
66
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
64
67
|
|
|
@@ -25,6 +25,7 @@ function* askFileWriteTextContents(
|
|
|
25
25
|
filepath: string,
|
|
26
26
|
data: string,
|
|
27
27
|
storageDriveAdvancedWriteOptions?: StorageDriveAdvancedWriteOptions,
|
|
28
|
+
scope?: string,
|
|
28
29
|
): AskResponse<void>;
|
|
29
30
|
```
|
|
30
31
|
|
|
@@ -36,6 +37,7 @@ function* askFileWriteTextContents(
|
|
|
36
37
|
| `filepath` | `string` | Destination path within the drive, forward-slash delimited. Parent "directories" are implicit — no need to create them. |
|
|
37
38
|
| `data` | `string` | The text content to write. |
|
|
38
39
|
| `storageDriveAdvancedWriteOptions` | `StorageDriveAdvancedWriteOptions` | Optional write options — see below. |
|
|
40
|
+
| `scope` | `string` | Optional storage-scope segment. When set, the processor writes `{scope}/{filepath}` instead, partitioning the drive (used by tenant/scoped features such as the event-doc `scopeResolver`). Must be a single path segment: no separators, `..`, or null bytes. |
|
|
39
41
|
|
|
40
42
|
### `StorageDriveAdvancedWriteOptions`
|
|
41
43
|
|
|
@@ -53,6 +55,7 @@ function* askFileWriteTextContents(
|
|
|
53
55
|
| --- | --- |
|
|
54
56
|
| `FileWriteTextContentsErrorTypeEnum.AccessDenied` | The caller lacks permission to write to this drive (e.g. a foreign drive shared without write access). |
|
|
55
57
|
| `FileWriteTextContentsErrorTypeEnum.DriveNotFound` | No storage drive with that name exists in the deployed config. |
|
|
58
|
+
| `FileWriteTextContentsErrorTypeEnum.InvalidScope` | The `scope` is not a valid single path segment (empty, `.`, too long, or contains separators, `..`, `:`, or null bytes), or the scoped `filepath` is absolute or contains `..` segments or null bytes. |
|
|
56
59
|
|
|
57
60
|
## Notes
|
|
58
61
|
|
|
@@ -44,7 +44,7 @@ None.
|
|
|
44
44
|
|
|
45
45
|
## Returns
|
|
46
46
|
|
|
47
|
-
`GraphDatabaseInternalFieldNames` — a map of the engine's internal field-name tokens:
|
|
47
|
+
`GraphDatabaseInternalFieldNames` (importable from quidproquo-core) — a map of the engine's internal field-name tokens:
|
|
48
48
|
|
|
49
49
|
```typescript
|
|
50
50
|
type GraphDatabaseInternalFieldNames = {
|
|
@@ -56,6 +56,7 @@ The generic `T` is the expected shape of the decoded value; it is applied as a c
|
|
|
56
56
|
| --- | --- |
|
|
57
57
|
| The input is not valid JSON (`JSON.parse` throws). | `ErrorTypeEnum.Invalid` — the error text includes the underlying parser message. |
|
|
58
58
|
| `validateObject` is supplied and returns `false`. | `ErrorTypeEnum.Invalid` — the error text notes the JSON parsed but failed structural validation. |
|
|
59
|
+
| `validateObject` is supplied and throws. | `ErrorTypeEnum.Invalid`: the error text notes the JSON parsed but the validation function threw, and includes the thrown message. |
|
|
59
60
|
|
|
60
61
|
Because these are thrown through the qpq error mechanism, catch them with `askCatch` from quidproquo-core, which returns an `EitherActionResult` — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
61
62
|
|
|
@@ -35,7 +35,13 @@ function* askKeyValueStoreDelete(
|
|
|
35
35
|
| `keyValueStoreName` | `string` | Name of the store — must match a store declared with [defineKeyValueStore](../../../config/core/key-value-store.md) (or one shared via its `owner` option). |
|
|
36
36
|
| `key` | `KvsCoreDataType` | Partition key value of the record to delete (`string \| number`). |
|
|
37
37
|
| `sortKey` | `KvsCoreDataType` | Sort key value, required only if the store declares a sort key (`string \| number`). |
|
|
38
|
-
| `options` | `KeyValueStoreDeleteOptions` |
|
|
38
|
+
| `options` | `KeyValueStoreDeleteOptions` | Optional delete options (see below). |
|
|
39
|
+
|
|
40
|
+
### `KeyValueStoreDeleteOptions`
|
|
41
|
+
|
|
42
|
+
| Property | Type | Default | Description |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| `scope` | `string` | – | Optional storage scope. The processor composes it into the partition key value, so the delete only addresses the record written under that scope (used by tenant/scoped features). Requires the store's partition key to be string-typed. |
|
|
39
45
|
|
|
40
46
|
## Returns
|
|
41
47
|
|
|
@@ -47,6 +53,8 @@ function* askKeyValueStoreDelete(
|
|
|
47
53
|
| --- | --- |
|
|
48
54
|
| `KeyValueStoreDeleteErrorTypeEnum.ServiceUnavailable` | DynamoDB internal error or throttling. |
|
|
49
55
|
| `KeyValueStoreDeleteErrorTypeEnum.ResourceNotFound` | The underlying table does not exist. |
|
|
56
|
+
| `KeyValueStoreDeleteErrorTypeEnum.InvalidScope` | The `scope` option is malformed (empty, `.`, over 128 characters, or containing path separators, `..`, `@`, or null bytes), the store's partition key is not string-typed, or the partition-key value contains the reserved `@@QPQSCOPE@@` delimiter (reserved on string-pk stores, so unscoped calls reject it too). |
|
|
57
|
+
| `KeyValueStoreDeleteErrorTypeEnum.StoreNotFound` | The key value store is not declared in the qpq config (misconfiguration, e.g. a wrong name or a missing `defineKeyValueStore`). |
|
|
50
58
|
|
|
51
59
|
Catch errors with `askCatch` — it returns `{ success: true, result }` or `{ success: false, error }`.
|
|
52
60
|
|