create-qpq-app 0.1.7 → 0.1.8
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 +2 -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 +81 -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/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-get-all.md
CHANGED
|
@@ -37,7 +37,13 @@ function* askKeyValueStoreGetAll<Value>(
|
|
|
37
37
|
| Parameter | Type | Description |
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| `keyValueStoreName` | `string` | Name of the store to read from — must match a store declared with [defineKeyValueStore](../../../config/core/key-value-store.md) (or one shared via its `owner` option). |
|
|
40
|
-
| `options` | `KeyValueStoreGetAllOptions` |
|
|
40
|
+
| `options` | `KeyValueStoreGetAllOptions` | Optional read options (see below). |
|
|
41
|
+
|
|
42
|
+
### `KeyValueStoreGetAllOptions`
|
|
43
|
+
|
|
44
|
+
| Property | Type | Default | Description |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| `scope` | `string` | – | Optional storage scope. The processor enforces it as a begins-with prefix filter on the partition key, so only records written under the same scope are returned (used by tenant/scoped features). Requires the store's partition key to be string-typed. When no scope is given, scope-composed records are excluded, so one tenant's data never appears in an unscoped listing. |
|
|
41
47
|
|
|
42
48
|
## Returns
|
|
43
49
|
|
|
@@ -49,6 +55,8 @@ function* askKeyValueStoreGetAll<Value>(
|
|
|
49
55
|
| --- | --- |
|
|
50
56
|
| `KeyValueStoreGetAllErrorTypeEnum.ServiceUnavailable` | DynamoDB internal error or throttling. |
|
|
51
57
|
| `KeyValueStoreGetAllErrorTypeEnum.ResourceNotFound` | The underlying table does not exist. |
|
|
58
|
+
| `KeyValueStoreGetAllErrorTypeEnum.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 a scoped call's partition-key value contains the reserved `@@QPQSCOPE@@` delimiter. |
|
|
59
|
+
| `KeyValueStoreGetAllErrorTypeEnum.StoreNotFound` | The key value store is not declared in the qpq config (misconfiguration, e.g. a wrong name or a missing `defineKeyValueStore`). |
|
|
52
60
|
|
|
53
61
|
Catch errors with `askCatch` — it returns `{ success: true, result }` or `{ success: false, error }`.
|
|
54
62
|
|
|
@@ -39,7 +39,13 @@ function* askKeyValueStoreGet<Value>(
|
|
|
39
39
|
| --- | --- | --- |
|
|
40
40
|
| `keyValueStoreName` | `string` | Name of the store to read from — must match a store declared with [defineKeyValueStore](../../../config/core/key-value-store.md) (or one shared via its `owner` option). |
|
|
41
41
|
| `key` | `string` | The partition key value of the record to fetch. |
|
|
42
|
-
| `options` | `KeyValueStoreGetOptions` |
|
|
42
|
+
| `options` | `KeyValueStoreGetOptions` | Optional read options (see below). |
|
|
43
|
+
|
|
44
|
+
### `KeyValueStoreGetOptions`
|
|
45
|
+
|
|
46
|
+
| Property | Type | Default | Description |
|
|
47
|
+
| --- | --- | --- | --- |
|
|
48
|
+
| `scope` | `string` | – | Optional storage scope. The processor composes it into the partition key value, so scoped and unscoped records live under separate keys in the same store (used by tenant/scoped features). Requires the store's partition key to be string-typed. |
|
|
43
49
|
|
|
44
50
|
## Returns
|
|
45
51
|
|
|
@@ -51,6 +57,8 @@ function* askKeyValueStoreGet<Value>(
|
|
|
51
57
|
| --- | --- |
|
|
52
58
|
| `KeyValueStoreGetErrorTypeEnum.ServiceUnavailable` | DynamoDB internal error or throttling. |
|
|
53
59
|
| `KeyValueStoreGetErrorTypeEnum.ResourceNotFound` | The underlying table does not exist. |
|
|
60
|
+
| `KeyValueStoreGetErrorTypeEnum.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). |
|
|
61
|
+
| `KeyValueStoreGetErrorTypeEnum.StoreNotFound` | The key value store is not declared in the qpq config (misconfiguration, e.g. a wrong name or a missing `defineKeyValueStore`). |
|
|
54
62
|
|
|
55
63
|
Errors thrown by actions can be caught with `askCatch` from quidproquo-core. It returns an object — `{ success: true, result }` on success, or `{ success: false, error }` on failure:
|
|
56
64
|
|
package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-query-single.md
CHANGED
|
@@ -32,6 +32,7 @@ function* askKeyValueStoreQuerySingle<T>(
|
|
|
32
32
|
filter?: KvsQueryOperation,
|
|
33
33
|
sortAscending?: boolean,
|
|
34
34
|
limit?: number,
|
|
35
|
+
scope?: string,
|
|
35
36
|
): AskResponse<T | null>;
|
|
36
37
|
```
|
|
37
38
|
|
|
@@ -44,6 +45,7 @@ function* askKeyValueStoreQuerySingle<T>(
|
|
|
44
45
|
| `filter` | `KvsQueryOperation` | – | Optional filter on non-key attributes, applied after the key match. |
|
|
45
46
|
| `sortAscending` | `boolean` | – | Order by the sort key; `false` returns the highest/newest first. |
|
|
46
47
|
| `limit` | `number` | `1` | How many records to fetch per underlying page while searching. The story still returns only the first matching record. |
|
|
48
|
+
| `scope` | `string` | – | Optional storage scope, passed through as the underlying query's `scope` option. See [`KeyValueStoreQueryOptions`](./ask-key-value-store-query.md#keyvaluestorequeryoptions). An invalid scope surfaces as `KeyValueStoreQueryErrorTypeEnum.InvalidScope`. |
|
|
47
49
|
|
|
48
50
|
## Returns
|
|
49
51
|
|
|
@@ -52,6 +54,7 @@ function* askKeyValueStoreQuerySingle<T>(
|
|
|
52
54
|
## Notes
|
|
53
55
|
|
|
54
56
|
- Because `filter` is applied after the key match, when a filter is supplied the helper pages through results until it has gathered up to `limit` records before returning the first — so a matching record isn't missed just because it fell outside the first page.
|
|
57
|
+
- The record returned is the first one **collected across all fetched pages**, so a match found on an early page still wins even when a later page comes back empty.
|
|
55
58
|
- Errors surface the same as the underlying query (`KeyValueStoreQueryErrorTypeEnum`); catch them with `askCatch`.
|
|
56
59
|
|
|
57
60
|
## Related
|
|
@@ -55,7 +55,8 @@ function* askKeyValueStoreQuery<KvsItem>(
|
|
|
55
55
|
| `sortAscending` | `boolean` | `true` | Order results by the sort key. `false` returns the newest/highest first. |
|
|
56
56
|
| `limit` | `number` | – | Maximum number of records to return in this page. |
|
|
57
57
|
| `nextPageKey` | `string` | – | Opaque cursor from a previous page's `nextPageKey`; pass it to fetch the following page. |
|
|
58
|
-
| `ttlInSeconds` | `number` | – |
|
|
58
|
+
| `ttlInSeconds` | `number` | – | Accepted but not implemented: no processor currently applies a TTL to query results, so setting it has no effect. |
|
|
59
|
+
| `scope` | `string` | – | Optional storage scope. The processor composes it into the partition-key conditions, so the query only matches records written under the same scope (used by tenant/scoped features). Requires a string-typed partition key, and the key condition must constrain the partition key. |
|
|
59
60
|
|
|
60
61
|
## Query conditions (`KvsQueryOperation`)
|
|
61
62
|
|
|
@@ -116,6 +117,8 @@ When `nextPageKey` is set, pass it back as `options.nextPageKey` to fetch the ne
|
|
|
116
117
|
| --- | --- |
|
|
117
118
|
| `KeyValueStoreQueryErrorTypeEnum.ServiceUnavailable` | DynamoDB internal error or throttling. |
|
|
118
119
|
| `KeyValueStoreQueryErrorTypeEnum.ResourceNotFound` | The underlying table does not exist. |
|
|
120
|
+
| `KeyValueStoreQueryErrorTypeEnum.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, a scoped query's key condition does not constrain the partition key (or constrains it with an operator that cannot be scoped), or a partition-key condition value contains the reserved `@@QPQSCOPE@@` delimiter (reserved on string-pk stores, so unscoped queries reject it too). |
|
|
121
|
+
| `KeyValueStoreQueryErrorTypeEnum.StoreNotFound` | The key value store is not declared in the qpq config (misconfiguration, e.g. a wrong name or a missing `defineKeyValueStore`). |
|
|
119
122
|
|
|
120
123
|
Catch errors with `askCatch` — it returns `{ success: true, result }` or `{ success: false, error }`.
|
|
121
124
|
|
package/template/docusaurus/docs/actions/core/key-value-store/ask-key-value-store-scan-all.md
CHANGED
|
@@ -29,6 +29,7 @@ export function* askAllSuspendedUsers() {
|
|
|
29
29
|
function* askKeyValueStoreScanAll<T>(
|
|
30
30
|
storeName: string,
|
|
31
31
|
filterCondition?: KvsQueryOperation,
|
|
32
|
+
options?: KeyValueStoreScanOptions,
|
|
32
33
|
): AskResponse<T[]>;
|
|
33
34
|
```
|
|
34
35
|
|
|
@@ -38,6 +39,7 @@ function* askKeyValueStoreScanAll<T>(
|
|
|
38
39
|
| --- | --- | --- |
|
|
39
40
|
| `storeName` | `string` | Name of the store to scan — must match a store declared with [defineKeyValueStore](../../../config/core/key-value-store.md). |
|
|
40
41
|
| `filterCondition` | `KvsQueryOperation` | Optional filter applied to every scanned record — see [Query conditions](./ask-key-value-store-query.md#query-conditions-kvsqueryoperation). Omit it to return every record. |
|
|
42
|
+
| `options` | `KeyValueStoreScanOptions` | Optional scan options, forwarded to the underlying scan on **every** page (see [`KeyValueStoreScanOptions`](./ask-key-value-store-scan.md#keyvaluestorescanoptions)). In particular `scope` restricts every page to records written under that storage scope. |
|
|
41
43
|
|
|
42
44
|
## Returns
|
|
43
45
|
|
|
@@ -30,6 +30,7 @@ function* askKeyValueStoreScan<KvsItem>(
|
|
|
30
30
|
keyValueStoreName: string,
|
|
31
31
|
filterCondition?: KvsQueryOperation,
|
|
32
32
|
nextPageKey?: string,
|
|
33
|
+
options?: KeyValueStoreScanOptions,
|
|
33
34
|
): AskResponse<QpqPagedData<KvsItem>>;
|
|
34
35
|
```
|
|
35
36
|
|
|
@@ -40,6 +41,15 @@ function* askKeyValueStoreScan<KvsItem>(
|
|
|
40
41
|
| `keyValueStoreName` | `string` | Name of the store to scan — must match a store declared with [defineKeyValueStore](../../../config/core/key-value-store.md) (or one shared via its `owner` option). |
|
|
41
42
|
| `filterCondition` | `KvsQueryOperation` | Optional filter applied to every scanned record. Built with the `kvs*` condition helpers — see [Query conditions](./ask-key-value-store-query.md#query-conditions-kvsqueryoperation). Omit it to return everything. |
|
|
42
43
|
| `nextPageKey` | `string` | Opaque cursor from a previous page's `nextPageKey`; pass it to fetch the following page. |
|
|
44
|
+
| `options` | `KeyValueStoreScanOptions` | Optional scan options (see below). |
|
|
45
|
+
|
|
46
|
+
### `KeyValueStoreScanOptions`
|
|
47
|
+
|
|
48
|
+
| Property | Type | Default | Description |
|
|
49
|
+
| --- | --- | --- | --- |
|
|
50
|
+
| `limit` | `number` | – | Accepted but not implemented: no processor currently caps the page size of a scan, so setting it has no effect. |
|
|
51
|
+
| `ttlInSeconds` | `number` | – | Accepted but not implemented: no processor currently applies a TTL to scans, so setting it has no effect. |
|
|
52
|
+
| `scope` | `string` | – | Optional storage scope. The processor enforces it as a begins-with prefix filter on the partition key, so only records written under the same scope are returned (used by tenant/scoped features). It is still a full-table scan on the storage side; only the results are isolated. Requires the store's partition key to be string-typed. When no scope is given, scope-composed records are excluded, so one tenant's data never appears in an unscoped listing. |
|
|
43
53
|
|
|
44
54
|
## Returns
|
|
45
55
|
|
|
@@ -60,6 +70,8 @@ When `nextPageKey` is set, pass it back as the third argument to fetch the next
|
|
|
60
70
|
| --- | --- |
|
|
61
71
|
| `KeyValueStoreScanErrorTypeEnum.ServiceUnavailable` | DynamoDB internal error or throttling. |
|
|
62
72
|
| `KeyValueStoreScanErrorTypeEnum.ResourceNotFound` | The underlying table does not exist. |
|
|
73
|
+
| `KeyValueStoreScanErrorTypeEnum.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 a scoped call's partition-key value contains the reserved `@@QPQSCOPE@@` delimiter. |
|
|
74
|
+
| `KeyValueStoreScanErrorTypeEnum.StoreNotFound` | The key value store is not declared in the qpq config (misconfiguration, e.g. a wrong name or a missing `defineKeyValueStore`). |
|
|
63
75
|
|
|
64
76
|
Catch errors with `askCatch` — it returns `{ success: true, result }` or `{ success: false, error }`.
|
|
65
77
|
|
|
@@ -38,6 +38,8 @@ function* askKeyValueStoreUpdatePartialProperties<TModel, PartitionKey extends k
|
|
|
38
38
|
keyValueStoreName: string,
|
|
39
39
|
partitionKeyName: PartitionKey,
|
|
40
40
|
partialProperties: Partial<TModel> & { [k in PartitionKey]: TModel[PartitionKey] },
|
|
41
|
+
sortKeyName?: undefined,
|
|
42
|
+
options?: KeyValueStoreUpdateOptions,
|
|
41
43
|
): AskResponse<TModel>;
|
|
42
44
|
|
|
43
45
|
// Partition + sort key
|
|
@@ -46,6 +48,7 @@ function* askKeyValueStoreUpdatePartialProperties<TModel, PartitionKey extends k
|
|
|
46
48
|
partitionKeyName: PartitionKey,
|
|
47
49
|
partialProperties: Partial<TModel> & { [k in PartitionKey]: TModel[PartitionKey] } & { [k in SortKey]: TModel[SortKey] },
|
|
48
50
|
sortKeyName: SortKey,
|
|
51
|
+
options?: KeyValueStoreUpdateOptions,
|
|
49
52
|
): AskResponse<TModel>;
|
|
50
53
|
```
|
|
51
54
|
|
|
@@ -57,6 +60,7 @@ function* askKeyValueStoreUpdatePartialProperties<TModel, PartitionKey extends k
|
|
|
57
60
|
| `partitionKeyName` | `keyof TModel` | The property name that holds the partition key. That property must be present in `partialProperties`; it is used to address the record, not written. |
|
|
58
61
|
| `partialProperties` | `Partial<TModel>` (with the key properties required) | The record patch. Each non-key property becomes a `Set`; a property explicitly set to `undefined` becomes a `Remove`. |
|
|
59
62
|
| `sortKeyName` | `keyof TModel` | (Overload) The property name holding the sort key, when the store has one. Must be present in `partialProperties`. |
|
|
63
|
+
| `options` | `KeyValueStoreUpdateOptions` | Optional update options, passed straight through to the underlying update (see [`KeyValueStoreUpdateOptions`](./ask-key-value-store-update.md#keyvaluestoreupdateoptions)). In particular `scope` addresses the record written under that storage scope. |
|
|
60
64
|
|
|
61
65
|
## Returns
|
|
62
66
|
|
|
@@ -65,7 +69,7 @@ function* askKeyValueStoreUpdatePartialProperties<TModel, PartitionKey extends k
|
|
|
65
69
|
## Notes
|
|
66
70
|
|
|
67
71
|
- The partition-key (and sort-key) properties are skipped when building the operations — they identify the record rather than mutate it.
|
|
68
|
-
- A property whose value is `undefined` is turned into a `Remove`; a property with a valid value is turned into a `Set`.
|
|
72
|
+
- A property whose value is `undefined` is turned into a `Remove`; a property with a valid value is turned into a `Set`. A value that isn't a supported store data type (validated via `isValidKvsAdvancedDataType`, e.g. a nested object) throws an `InvalidKvsPartialPropertyError` (code `unsupportedValueType`) before anything is written, rather than being silently skipped.
|
|
69
73
|
- Errors surface the same as the underlying update (`KeyValueStoreUpdateErrorTypeEnum`); catch them with `askCatch`.
|
|
70
74
|
|
|
71
75
|
## Related
|
|
@@ -52,7 +52,13 @@ function* askKeyValueStoreUpdate<Value>(
|
|
|
52
52
|
| `updates` | `KvsUpdate` | The list of update operations to apply. See [Update operations](#update-operations-kvsupdate). |
|
|
53
53
|
| `key` | `KvsCoreDataType` | Partition key value of the record to update (`string \| number`). |
|
|
54
54
|
| `sortKey` | `KvsCoreDataType` | Sort key value, required only if the store declares a sort key (`string \| number`). |
|
|
55
|
-
| `options` | `KeyValueStoreUpdateOptions` |
|
|
55
|
+
| `options` | `KeyValueStoreUpdateOptions` | Optional update options (see below). |
|
|
56
|
+
|
|
57
|
+
### `KeyValueStoreUpdateOptions`
|
|
58
|
+
|
|
59
|
+
| Property | Type | Default | Description |
|
|
60
|
+
| --- | --- | --- | --- |
|
|
61
|
+
| `scope` | `string` | – | Optional storage scope. The processor composes it into the partition key value, so the update only addresses the record written under that scope (used by tenant/scoped features). Requires the store's partition key to be string-typed. |
|
|
56
62
|
|
|
57
63
|
## Update operations (`KvsUpdate`)
|
|
58
64
|
|
|
@@ -95,6 +101,8 @@ Each `path` is a `KvsAttributePath` — either a top-level attribute name (`'bal
|
|
|
95
101
|
| --- | --- |
|
|
96
102
|
| `KeyValueStoreUpdateErrorTypeEnum.ServiceUnavailable` | DynamoDB internal error or throttling. |
|
|
97
103
|
| `KeyValueStoreUpdateErrorTypeEnum.ResourceNotFound` | The underlying table does not exist. |
|
|
104
|
+
| `KeyValueStoreUpdateErrorTypeEnum.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). |
|
|
105
|
+
| `KeyValueStoreUpdateErrorTypeEnum.StoreNotFound` | The key value store is not declared in the qpq config (misconfiguration, e.g. a wrong name or a missing `defineKeyValueStore`). |
|
|
98
106
|
|
|
99
107
|
Catch errors with `askCatch` — it returns `{ success: true, result }` or `{ success: false, error }`.
|
|
100
108
|
|
|
@@ -42,7 +42,7 @@ function* askKeyValueStoreUpsertWithRetry<KvsItem>(
|
|
|
42
42
|
|
|
43
43
|
### `KeyValueStoreUpsertWithRetryOptions`
|
|
44
44
|
|
|
45
|
-
Extends `KeyValueStoreUpsertOptions` (`ttlInSeconds`, `ifNotExists`) with:
|
|
45
|
+
Extends `KeyValueStoreUpsertOptions` (`ttlInSeconds`, `ifNotExists`, `scope`) with:
|
|
46
46
|
|
|
47
47
|
| Property | Type | Default | Description |
|
|
48
48
|
| --- | --- | --- | --- |
|
|
@@ -47,6 +47,7 @@ function* askKeyValueStoreUpsert<KvsItem>(
|
|
|
47
47
|
| --- | --- | --- | --- |
|
|
48
48
|
| `ttlInSeconds` | `number` | – | Time-to-live in seconds; sets the record's expiry (used with the store's `ttlAttribute`). |
|
|
49
49
|
| `ifNotExists` | `boolean` | `false` | Conditional insert: only write when no item with the same key exists. A losing concurrent writer receives `KeyValueStoreUpsertErrorTypeEnum.Conflict` instead of silently overwriting — the primitive for optimistic-concurrency schemes (e.g. append-only event logs where the sort key is a claimed index). |
|
|
50
|
+
| `scope` | `string` | – | Optional storage scope. The processor composes it into the item's partition key value, so the record is written under that scope and only scoped reads see it (used by tenant/scoped features). Requires the store's partition key to be string-typed. |
|
|
50
51
|
|
|
51
52
|
## Returns
|
|
52
53
|
|
|
@@ -59,6 +60,8 @@ function* askKeyValueStoreUpsert<KvsItem>(
|
|
|
59
60
|
| `KeyValueStoreUpsertErrorTypeEnum.ServiceUnavailable` | DynamoDB internal error or throttling. |
|
|
60
61
|
| `KeyValueStoreUpsertErrorTypeEnum.ResourceNotFound` | The underlying table does not exist. |
|
|
61
62
|
| `KeyValueStoreUpsertErrorTypeEnum.Conflict` | A conditional (`ifNotExists`) write lost to an existing item. Namespaced — not the generic `ErrorTypeEnum.Conflict` — so retry logic can target the write race specifically without also catching domain-level conflicts. |
|
|
63
|
+
| `KeyValueStoreUpsertErrorTypeEnum.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). |
|
|
64
|
+
| `KeyValueStoreUpsertErrorTypeEnum.StoreNotFound` | The key value store is not declared in the qpq config (misconfiguration, e.g. a wrong name or a missing `defineKeyValueStore`). |
|
|
62
65
|
|
|
63
66
|
Catch errors with `askCatch` — it returns `{ success: true, result }` or `{ success: false, error }`.
|
|
64
67
|
|
|
@@ -88,7 +88,7 @@ if (outcome.success) {
|
|
|
88
88
|
|
|
89
89
|
## Notes
|
|
90
90
|
|
|
91
|
-
- Requests abort after
|
|
91
|
+
- Requests abort after 30 seconds, surfacing as `NetworkRequestErrorTypeEnum.Timeout`. (30s deliberately outlasts API Gateway's 29s integration timeout, so a slow upstream shows up as a real gateway error rather than a client-side abort.)
|
|
92
92
|
|
|
93
93
|
## Related
|
|
94
94
|
|
|
@@ -157,4 +157,4 @@ Effects the reducer doesn't handle leave the state unchanged and are skipped. Us
|
|
|
157
157
|
|
|
158
158
|
- [askStateDispatch](./ask-state-dispatch.md) — dispatch the effects this story reduces.
|
|
159
159
|
- [askStateRead](./ask-state-read.md) — read the accumulated state mid-story.
|
|
160
|
-
- [defineStateDispatchOverWebsockets](../../../config/
|
|
160
|
+
- [defineStateDispatchOverWebsockets](../../../config/features/state-dispatch-over-websockets.md) — stream reduced dispatches to a WebSocket client.
|
|
@@ -84,4 +84,4 @@ function* askStateDispatchEffect<E extends Effect<any, any>>(
|
|
|
84
84
|
|
|
85
85
|
- [askStateRead](./ask-state-read.md) — read the state that dispatches accumulate into.
|
|
86
86
|
- [askReduceState](./ask-reduce-state.md) — run a story purely for its dispatched effects and return the reduced state.
|
|
87
|
-
- [defineStateDispatchOverWebsockets](../../../config/
|
|
87
|
+
- [defineStateDispatchOverWebsockets](../../../config/features/state-dispatch-over-websockets.md) — redirect these dispatches to a connected WebSocket client.
|
|
@@ -61,7 +61,7 @@ function* askStreamMap<E extends StreamEncoding, T, R = StreamDataType<E, T>>(
|
|
|
61
61
|
## Notes
|
|
62
62
|
|
|
63
63
|
- The `index` counts only chunks handed to your callback — chunks that are `skipped` or carry no `data` are dropped and do not advance it.
|
|
64
|
-
- The handle is closed for you when the loop ends.
|
|
64
|
+
- The handle is closed for you when the loop ends. This includes failures: when a read or your callback fails, the stream is closed first and the original error is then rethrown, so a surrounding [askCatch](../system/ask-catch.md) can handle it without leaking the stream.
|
|
65
65
|
- Because it buffers the entire stream in memory, prefer [askStreamProcess](./ask-stream-process.md) when you only need to react to each chunk and don't need the full list.
|
|
66
66
|
|
|
67
67
|
## Related
|
|
@@ -46,7 +46,8 @@ function* askStreamProcess<E extends StreamEncoding, T>(
|
|
|
46
46
|
## Notes
|
|
47
47
|
|
|
48
48
|
- The `index` counts only chunks handed to your callback — chunks that are `skipped` or carry no `data` are dropped and do not advance it.
|
|
49
|
-
- The handle is closed for you when the loop ends.
|
|
49
|
+
- The handle is closed for you when the loop ends. This includes failures: when a read or your callback fails, the stream is closed first and the original error is then rethrown, so a surrounding [askCatch](../system/ask-catch.md) can handle it without leaking the stream.
|
|
50
|
+
- If you need finer control — reading only part of a stream, or polling with `noWait` — drive [askStreamRead](./ask-stream-read.md) / [askStreamClose](./ask-stream-close.md) directly instead.
|
|
50
51
|
- To build up a result rather than react to each chunk, use [askStreamMap](./ask-stream-map.md).
|
|
51
52
|
|
|
52
53
|
## Related
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: askTraceStory
|
|
3
|
+
description: Replay a recorded story against its real code and capture a line-by-line execution trace.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# askTraceStory
|
|
7
|
+
|
|
8
|
+
Replays a previously recorded story execution (a `StoryResult` from the logs) against the story's real code and records a **line-by-line execution trace**: every statement the story executed, with the values of its local variables at that moment.
|
|
9
|
+
|
|
10
|
+
- **Action type:** `SystemActionType.TraceStory`
|
|
11
|
+
- **Runtime support:** only node runtimes (AWS Lambda and the dev server) implement a processor. The tracer drives the V8 inspector, so browser runtimes cannot process this action.
|
|
12
|
+
|
|
13
|
+
The recorded `StoryResult` already contains every action result the original run saw, so the replay is deterministic: the story re-executes with the exact same inputs and answers, and the tracer captures what happened between the actions. The story's code is loaded through the runtime's dynamic module loader (which federates on Lambda), so the trace runs the same code that ran originally.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { askTraceStory } from 'quidproquo-core';
|
|
17
|
+
|
|
18
|
+
export function* askDebugExecution(storyResult: StoryResult<any>) {
|
|
19
|
+
const trace = yield* askTraceStory(storyResult);
|
|
20
|
+
|
|
21
|
+
// trace.steps: one entry per executed statement, with locals captured at that point.
|
|
22
|
+
return trace;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Signature
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
function* askTraceStory(
|
|
30
|
+
storyResult: StoryResult<any>,
|
|
31
|
+
scriptPatterns?: string[],
|
|
32
|
+
onlyOwnCode?: boolean,
|
|
33
|
+
): AskResponse<QpqExecutionTrace>;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Parameters
|
|
37
|
+
|
|
38
|
+
| Parameter | Type | Description |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| `storyResult` | `StoryResult<any>` | The recorded execution to replay. Must carry `qpqFunctionRuntimeInfo` so the processor can locate the story's code. |
|
|
41
|
+
| `scriptPatterns` | `string[]` | Optional regex sources matched against script urls, tracing extra scripts in addition to the story function's own script (for bundles split across chunks). |
|
|
42
|
+
| `onlyOwnCode` | `boolean` | Optional. When true, breakpoints are only set on statements whose source-mapped origin is the service's own code; positions mapping into node_modules (framework and dependencies) are skipped, so the step budget is spent entirely on user statements. |
|
|
43
|
+
|
|
44
|
+
## Returns
|
|
45
|
+
|
|
46
|
+
`QpqExecutionTrace`: the trace of the replay.
|
|
47
|
+
|
|
48
|
+
- `sources`: the original source files the trace maps into (with content when source maps shipped `sourcesContent`).
|
|
49
|
+
- `steps`: one entry per executed statement (source position, function name, captured locals, and the return value at function-return points).
|
|
50
|
+
- `truncated`: true when the step budget was hit; the replay still ran to completion but later steps were not recorded.
|
|
51
|
+
- `stats`: pause/breakpoint counts and timing, including the urls of every instrumented script.
|
|
52
|
+
|
|
53
|
+
## Errors
|
|
54
|
+
|
|
55
|
+
| Error type | When |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `ErrorTypeEnum.BadRequest` | The `storyResult` has no `qpqFunctionRuntimeInfo`, so the processor cannot locate its code. |
|
|
58
|
+
| `ErrorTypeEnum.NotFound` | The story's module could not be dynamically loaded. |
|
|
59
|
+
| `ErrorTypeEnum.GenericError` | The tracer itself failed while replaying the story. |
|
|
60
|
+
|
|
61
|
+
Wrap the call in [askCatch](./ask-catch.md) to handle these as values.
|
|
62
|
+
|
|
63
|
+
## Related
|
|
64
|
+
|
|
65
|
+
- [askGetRuntimeCorrelation](./ask-get-runtime-correlation.md): the correlation id that identifies the recorded execution to trace.
|
|
66
|
+
- [askExecuteStory](./ask-execute-story.md): run a story fresh from its runtime reference (rather than replaying a recorded run).
|
package/template/docusaurus/docs/actions/core/user-directory/ask-user-directory-authenticate-user.md
CHANGED
|
@@ -65,7 +65,7 @@ function* askUserDirectoryAuthenticateUser(
|
|
|
65
65
|
| `userDirectoryName` | `string` | Name of the directory to authenticate against — must match a directory declared with [defineUserDirectory](../../../config/core/user-directory.md) (or one shared via its `owner` option). |
|
|
66
66
|
| `isCustom` | `boolean` | `false` for a standard email + password sign-in; `true` to start Cognito's custom auth flow (requires a [customAuthRuntime](../../../config/core/user-directory.md#custom-auth-runtime)). When `true`, `password` is not used. |
|
|
67
67
|
| `email` | `string` | The user's email / username. |
|
|
68
|
-
| `password` | `string` | The user's password. Required for a standard (`isCustom: false`) sign-in
|
|
68
|
+
| `password` | `string` | The user's password. Required for a standard (`isCustom: false`) sign-in: a missing or empty password throws `InvalidPassword`. Omitted for custom auth. |
|
|
69
69
|
|
|
70
70
|
## Returns
|
|
71
71
|
|
|
@@ -98,8 +98,8 @@ interface AuthenticationInfo {
|
|
|
98
98
|
|
|
99
99
|
| Error | Meaning |
|
|
100
100
|
| --- | --- |
|
|
101
|
-
| `UserDirectoryAuthenticateUserErrorTypeEnum.UserNotFound` |
|
|
102
|
-
| `UserDirectoryAuthenticateUserErrorTypeEnum.InvalidPassword` |
|
|
101
|
+
| `UserDirectoryAuthenticateUserErrorTypeEnum.UserNotFound` | The email or password is incorrect. Unknown users and wrong passwords are deliberately reported the same way so callers cannot probe which accounts exist. |
|
|
102
|
+
| `UserDirectoryAuthenticateUserErrorTypeEnum.InvalidPassword` | A standard (`isCustom: false`) sign-in was attempted without a password. Thrown before the request reaches the identity provider. |
|
|
103
103
|
|
|
104
104
|
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:
|
|
105
105
|
|
package/template/docusaurus/docs/actions/core/user-directory/ask-user-directory-set-access-token.md
CHANGED
|
@@ -43,6 +43,7 @@ function* askUserDirectorySetAccessToken(
|
|
|
43
43
|
## Notes
|
|
44
44
|
|
|
45
45
|
- The token is stored on the session only; sessions are not transferred across service boundaries (queues, event buses, service functions), so the identity does not leak beyond the current in-process runtime.
|
|
46
|
+
- A token that cannot be verified (missing, malformed, wrong pool, bad signature, or expired) fails the action with a generic `Unauthorized` error and leaves the session untouched. Catch it with `askCatch` from quidproquo-core when a failed load should not abort the story.
|
|
46
47
|
|
|
47
48
|
## Related
|
|
48
49
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: askApplyEventDocEvent
|
|
3
|
+
description: Declaratively apply an event-doc event, leaving HOW it is applied (optimistic UI append, direct server append, ...) to a registered processor.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# askApplyEventDocEvent
|
|
7
|
+
|
|
8
|
+
A **purely declarative** way to apply an event-doc event: the story yields an `eventType` and its `data`, and a registered processor decides how that turns into an actual write — for example an optimistic local append followed by a POST in a browser editor, or a direct [askEventDocEventAppend](./ask-event-doc-event-append.md) on the backend. Because it has no side effect of its own, verbs written in terms of it (like a domain's `askXSetY` action creators) run unchanged wherever a processor for it is registered — backend, tests, or transforms.
|
|
9
|
+
|
|
10
|
+
- **Action type:** `EventDocActionType.ApplyEvent`
|
|
11
|
+
- **quidproquo-features ships no default processor** for this action. Each consumer registers the implementation that fits its runtime via `defineActionProcessors`. Calling it with no processor registered fails the same way any unhandled action type does.
|
|
12
|
+
- The version stamped on the resulting event is not passed by the caller — the registered processor (the "editor") stamps its own configured schema version and provenance when it applies the event.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { askApplyEventDocEvent } from 'quidproquo-features';
|
|
16
|
+
|
|
17
|
+
export function* askTenantSetBrand(data: TenantSetBrandData) {
|
|
18
|
+
yield* askApplyEventDocEvent(TenantEffect.setBrand, data);
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Signature
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
function* askApplyEventDocEvent(eventType: string, data: unknown): AskResponse<void>;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Parameters
|
|
29
|
+
|
|
30
|
+
| Parameter | Type | Description |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `eventType` | `string` | The effect/event type discriminant, matched by the reducer that folds the document and by the registered processor. |
|
|
33
|
+
| `data` | `unknown` | The typed domain data for the event, opaque to this action itself. |
|
|
34
|
+
|
|
35
|
+
## Returns
|
|
36
|
+
|
|
37
|
+
`AskResponse<void>` — applying an event never returns a value; a processor that fails to apply it surfaces the failure as its own runtime's error state (e.g. UI error state in a browser processor) rather than throwing back through this call.
|
|
38
|
+
|
|
39
|
+
## Notes
|
|
40
|
+
|
|
41
|
+
- This is the contract only — it carries no default behavior. A service that yields it must also register a processor for `EventDocActionType.ApplyEvent` (via `defineActionProcessors`) appropriate to where the story runs.
|
|
42
|
+
- Prefer this over calling [askEventDocEventAppend](./ask-event-doc-event-append.md) directly when you want the same verb (e.g. a domain's action creator) to work identically in a browser editor with optimistic updates and on the backend with a direct append.
|
|
43
|
+
|
|
44
|
+
## Related
|
|
45
|
+
|
|
46
|
+
- [askEventDocEventAppend](./ask-event-doc-event-append.md) — the store-backed backend append a processor for this action typically delegates to.
|
|
47
|
+
- [askEventDocAppendServerEvent](./ask-event-doc-append-server-event.md) — builds a server-authored event envelope; a natural implementation for a backend `ApplyEvent` processor.
|
|
@@ -91,6 +91,7 @@ What the client POSTs to append an event. `modelId` and the server-stamped prove
|
|
|
91
91
|
## Related
|
|
92
92
|
|
|
93
93
|
- [askEventDocAppendServerEvent](./ask-event-doc-append-server-event.md) — the server-authored wrapper that builds the input envelope for you.
|
|
94
|
+
- [askApplyEventDocEvent](./ask-apply-event-doc-event.md) — a declarative, processor-dispatched alternative when the same verb must also run in a browser editor.
|
|
94
95
|
- [askEventDocEventWrite](./ask-event-doc-event-write.md) — the low-level conditional write this composes.
|
|
95
96
|
- [askEventDocEventList / EventListAll / EventLast](./ask-event-doc-event-list.md) — reading the log this appends to.
|
|
96
97
|
- [askEventDocProvideStore](./ask-event-doc-provide-store.md) — provides the store context this requires.
|
package/template/docusaurus/docs/actions/features/event-doc-ai/ask-event-doc-ai-process-send.md
CHANGED
|
@@ -5,7 +5,7 @@ description: The backend chat turn — persist the user message, stream the mode
|
|
|
5
5
|
|
|
6
6
|
# askEventDocAiProcessSend
|
|
7
7
|
|
|
8
|
-
Runs one conversational turn on the backend. It appends the user's message to the chat history, streams the assistant's reply through [askAiPromptStream](../../core/ai/ask-ai-prompt-stream.md) — dispatching each stream part to the browser live —
|
|
8
|
+
Runs one conversational turn on the backend. It appends the user's message to the chat history, then streams the assistant's reply through [askAiPromptStream](../../core/ai/ask-ai-prompt-stream.md) — dispatching each stream part to the browser live — folding each completed reply into durable segments and saving it. If a round stops with `finishReason: toolCalls` (the underlying step limit cut it off while the model still wanted to act), it automatically resends the updated history so the model continues the same turn, up to a bounded number of rounds. Once a round finishes naturally, it bumps the chat to the top of the list and returns. This is the handler behind the `ChatSend` websocket method (`onChatSend`).
|
|
9
9
|
|
|
10
10
|
- Built from [askConfigGetGlobal](../../core/config/ask-config-get-global.md), [askAiPromptStream](../../core/ai/ask-ai-prompt-stream.md), [askStreamMap](../../core/stream/ask-stream-map.md), the history/list helpers, and the `askUIEventDocAi*` stream dispatches.
|
|
11
11
|
|
|
@@ -47,13 +47,16 @@ function* askEventDocAiProcessSend(
|
|
|
47
47
|
1. Reads the AI name, model, and reasoning budget from the feature's globals, and resolves the system prompt — freshest source first: the configured `systemPromptGenerator` inline function (run per-turn with `{ docId }` so it can carry live document state), else the static `systemPrompt`, else a built-in default. The prompt is never persisted.
|
|
48
48
|
2. [Validates the attachments](./ask-event-doc-ai-attachments-validate.md) against the document's storage drive and trusted `docId`.
|
|
49
49
|
3. Loads the chat history, appends the new user message (attachments first, then text), and **saves** immediately — so a refresh mid-reply still shows the question.
|
|
50
|
-
4.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
4. Runs one or more rounds (bounded, currently up to 20):
|
|
51
|
+
1. Streams the reply with [askAiPromptStream](../../core/ai/ask-ai-prompt-stream.md). File segments in the history become drive-referenced file parts (no URLs), which the action processor resolves to contents at prompt time. Tools do **not** receive `docId` from the model — executors inherit the session context and read the trusted id there. From the second round on, a transport-only nudge message (never saved to history) is appended so the conversation doesn't end on an assistant turn.
|
|
52
|
+
2. Maps over the stream with [askStreamMap](../../core/stream/ask-stream-map.md), dispatching each part to the UI as it arrives (the live typing view) and collecting the parts.
|
|
53
|
+
3. Folds the collected parts into durable segments (`text`/`reasoning` deltas merged, tool calls paired with results). A stream that produced no content (e.g. it errored before any text) saves no assistant message.
|
|
54
|
+
4. If there are segments, **saves** the finalized assistant message and dispatches it to the UI as the finalized message.
|
|
55
|
+
5. Clears the UI's now-superseded live-stream buffer.
|
|
56
|
+
6. If the round's [`finishReason`](../../core/ai/ask-ai-prompt-stream.md#aistreamfinishreasonenum) was `toolCalls` (halted early) and it produced segments, loops back to stream another round from the updated history; otherwise stops.
|
|
57
|
+
5. Touches the chat (bumps `updatedAt`) and returns `{ complete: true }`.
|
|
58
|
+
|
|
59
|
+
Steps 4.ii–4.v use the `askUIEventDocAiAppendStreamChunk`, `askUIEventDocAiAppendChatMessage`, and `askUIEventDocAiClearStream` UI actions — the client renders the reply from those dispatches while this request is still in flight, then reconciles to the finalized message. Because rounds can repeat, the UI may see more than one finalized assistant message appended for what is presented as a single turn.
|
|
57
60
|
|
|
58
61
|
## The chat model
|
|
59
62
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "label": "WebSocket queue", "link": { "type": "generated-index", "description": "Send RPC-style requests to another qpq service over the WebSocket queue." } }
|
|
@@ -11,7 +11,7 @@ Sends an **RPC-style request to another qpq service**, identified by a service n
|
|
|
11
11
|
- The request is dispatched as a `qpq/serviceRequest/<serviceName>/<method>` message and its correlated response is awaited. In a browser (quidproquo-web-react) client this travels over the app's WebSocket queue connection; the response is returned when the correlated reply arrives.
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { askServiceRequest } from 'quidproquo-
|
|
14
|
+
import { askServiceRequest } from 'quidproquo-features';
|
|
15
15
|
|
|
16
16
|
interface GetQuoteRequest { symbol: string; }
|
|
17
17
|
interface GetQuoteResponse { symbol: string; price: number; }
|
|
@@ -30,7 +30,7 @@ export function* askGetQuote(symbol: string) {
|
|
|
30
30
|
Prefer `createServiceRequester` for a typed, reusable wrapper:
|
|
31
31
|
|
|
32
32
|
```typescript
|
|
33
|
-
import { createServiceRequester } from 'quidproquo-
|
|
33
|
+
import { createServiceRequester } from 'quidproquo-features';
|
|
34
34
|
|
|
35
35
|
// Bind the service + method once, with request/response types
|
|
36
36
|
const askGetQuote = createServiceRequester<GetQuoteRequest, GetQuoteResponse>('market', 'getQuote');
|
|
@@ -65,5 +65,6 @@ function* askServiceRequest<TPayload, TResponse>(
|
|
|
65
65
|
|
|
66
66
|
## Related
|
|
67
67
|
|
|
68
|
-
- [askServiceFunctionExecute](
|
|
68
|
+
- [askServiceFunctionExecute](../../webserver/service-function/ask-service-function-execute.md) — direct Lambda-invoke of a named service function.
|
|
69
|
+
- [defineWebSocketQueue](../../../config/features/web-socket-queue.md) — the WebSocket queue this request travels over in a browser client.
|
|
69
70
|
- [askCatch](../../core/system/ask-catch.md) — catch errors returned by the service handler.
|
|
@@ -9,6 +9,10 @@ Writes a single item into a **generic data resource** — a named table addresse
|
|
|
9
9
|
|
|
10
10
|
- **Action type:** `GenericDataResourceActionTypeEnum.Put`
|
|
11
11
|
|
|
12
|
+
:::warning Not implemented by any runtime
|
|
13
|
+
No action processor currently implements this action on any platform (AWS, dev server, or browser). Yielding it from a story will fail at runtime with an unknown-action error. The action type exists so tooling (like the admin log viewer) can label historical log entries. Use a [key-value store](../../../config/core/key-value-store.md) for application data.
|
|
14
|
+
:::
|
|
15
|
+
|
|
12
16
|
```typescript
|
|
13
17
|
import { askPutGenericDataResource } from 'quidproquo-webserver';
|
|
14
18
|
|
|
@@ -9,6 +9,10 @@ Reads items out of a **generic data resource** — a named table addressed direc
|
|
|
9
9
|
|
|
10
10
|
- **Action type:** `GenericDataResourceActionTypeEnum.Scan`
|
|
11
11
|
|
|
12
|
+
:::warning Not implemented by any runtime
|
|
13
|
+
No action processor currently implements this action on any platform (AWS, dev server, or browser). Yielding it from a story will fail at runtime with an unknown-action error. The action type exists so tooling (like the admin log viewer) can label historical log entries. Use a [key-value store](../../../config/core/key-value-store.md) for application data.
|
|
14
|
+
:::
|
|
15
|
+
|
|
12
16
|
```typescript
|
|
13
17
|
import { askScanGenericDataResource } from 'quidproquo-webserver';
|
|
14
18
|
|
|
@@ -35,7 +35,7 @@ function* askWebsocketSendMessage<T>(
|
|
|
35
35
|
|
|
36
36
|
| Parameter | Type | Description |
|
|
37
37
|
| --- | --- | --- |
|
|
38
|
-
| `websocketApiName` | `string` | The `apiName` of the target [WebSocket API](../../../config/webserver/websocket.md) (the `apiName` option of [defineWebsocket](../../../config/webserver/websocket.md), or the `apiName` argument to [defineWebSocketQueue](../../../config/
|
|
38
|
+
| `websocketApiName` | `string` | The `apiName` of the target [WebSocket API](../../../config/webserver/websocket.md) (the `apiName` option of [defineWebsocket](../../../config/webserver/websocket.md), or the `apiName` argument to [defineWebSocketQueue](../../../config/features/web-socket-queue.md)). Identifies which deployed API the connection belongs to. |
|
|
39
39
|
| `connectionId` | `string` | Identifier of the client connection to send to. You receive this on the `WebsocketEvent` in your connect/message handlers; persist it (e.g. keyed by user) if you need to send outside the request that created it. |
|
|
40
40
|
| `payload` | `T` | The message to send. Serialized to JSON before transmission, so any JSON-serializable value works. |
|
|
41
41
|
|
|
@@ -67,5 +67,5 @@ export function* askTrySend(connectionId: string, payload: unknown) {
|
|
|
67
67
|
## Related
|
|
68
68
|
|
|
69
69
|
- [defineWebsocket](../../../config/webserver/websocket.md) — declares the WebSocket API and its connect/disconnect/message handlers.
|
|
70
|
-
- [defineWebSocketQueue](../../../config/
|
|
70
|
+
- [defineWebSocketQueue](../../../config/features/web-socket-queue.md) — managed messaging layer that tracks connections for you.
|
|
71
71
|
- [askCatch](../../../actions/core/system/ask-catch.md) — catch the throttled/disconnected errors above.
|
|
@@ -332,6 +332,8 @@ interface StorySession {
|
|
|
332
332
|
accessToken?: string; // Auth token (not transferred)
|
|
333
333
|
decodedAccessToken?: DecodedAccessToken; // User info
|
|
334
334
|
context: QpqContext<any>; // Contextual data
|
|
335
|
+
localContext?: QpqContext<any>; // Service-local context (never crosses a service boundary)
|
|
336
|
+
functionGlobals?: Record<string, unknown>; // Globals from the executing function's runtime definition
|
|
335
337
|
}
|
|
336
338
|
```
|
|
337
339
|
|