quidproquo 0.1.16 → 0.1.17
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 +191 -8
- package/package.json +10 -7
package/README.md
CHANGED
|
@@ -1,15 +1,198 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/qpqjs/quidproquo/main/assets/qpq-lockup-dark.svg">
|
|
5
|
+
<img src="https://raw.githubusercontent.com/qpqjs/quidproquo/main/assets/qpq-lockup.svg" alt="quidproquo" width="440">
|
|
6
|
+
</picture>
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
<p><strong>Build web applications out of pure functions.</strong></p>
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
[](https://www.npmjs.com/package/quidproquo)
|
|
11
|
+
[](https://github.com/qpqjs/quidproquo/blob/main/LICENSE)
|
|
12
|
+
[](https://nodejs.org)
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
[Getting started](#getting-started) · [How it works](#how-it-works) · [Packages](#packages) · [Docs](https://docs.quidproquojs.com)
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
</div>
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
---
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
Business logic in quidproquo is written as generator functions called **stories**. A story never calls the clock, the database, or the network directly. It yields a typed action describing what it wants, and the runtime hands that action to whichever implementation fits the platform it is running on.
|
|
21
|
+
|
|
22
|
+
The same story runs on AWS Lambda, in a Node process, in a browser, or against fixtures in a test, with no changes to the code.
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
function* askGreet(name: string): AskResponse<string> {
|
|
26
|
+
const now = yield* askDateNow();
|
|
27
|
+
return `hello ${name}, it is ${now}`;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`askDateNow()` does not read the clock. It yields `{ type: '@quidproquo-core/Date/Now' }` and suspends, and the runtime resolves it and resumes the generator with the answer.
|
|
32
|
+
|
|
33
|
+
## Getting started
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx create-qpq-app myapp
|
|
37
|
+
cd myapp
|
|
38
|
+
npm run dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
That scaffolds a complete working app (five services, a local dev server, and a one-image docker deploy), installs it, builds it, and makes the first commit. `npm run dev` runs the backend and every frontend dev server in one process, so one ctrl+c stops everything.
|
|
42
|
+
|
|
43
|
+
The api comes up on `http://localhost:8080` and the web on `http://localhost:3080`. Check it is alive:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
curl http://localhost:8080/api/shell/v1/health
|
|
47
|
+
# {"status":"healthy","service":"shell","checkedAt":"..."}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
You need **Node.js 24 or newer**. Docker is only needed when you deploy, not for local dev.
|
|
51
|
+
|
|
52
|
+
<details>
|
|
53
|
+
<summary>Scaffolder options</summary>
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx create-qpq-app <app-name> [options]
|
|
57
|
+
|
|
58
|
+
--language <typescript|javascript> skip the language prompt
|
|
59
|
+
--domain <domain> app domain (default: <app-name>.example.com)
|
|
60
|
+
--no-git skip git init
|
|
61
|
+
--no-install skip npm install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Pass `--language javascript` and you get the same app with type annotations stripped and JSX preserved, running on the same toolchain.
|
|
65
|
+
|
|
66
|
+
</details>
|
|
67
|
+
|
|
68
|
+
## How it works
|
|
69
|
+
|
|
70
|
+
Here is the health route the scaffolded app ships with:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { askDateNow, AskResponse, HTTPEvent, HTTPEventResponse, qpqWebServerUtils } from 'quidproquo';
|
|
74
|
+
import { dynamicRoute } from 'quidproquo-features';
|
|
75
|
+
|
|
76
|
+
export const health = dynamicRoute(
|
|
77
|
+
['GET', '/health'],
|
|
78
|
+
function* healthCheck(event: HTTPEvent): AskResponse<HTTPEventResponse> {
|
|
79
|
+
const checkedAt = yield* askDateNow();
|
|
80
|
+
|
|
81
|
+
return qpqWebServerUtils.toJsonEventResponse({
|
|
82
|
+
status: 'healthy',
|
|
83
|
+
service: 'shell',
|
|
84
|
+
checkedAt,
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Locally the clock is answered by the dev server, in production by whatever platform you deployed to, and in a test by a fixed timestamp you supply. That is why every function you call from a story is named `ask*`: it is a request, it has an answer, and it must be called with `yield*`.
|
|
91
|
+
|
|
92
|
+
| Term | What it is |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| **Action** | A plain, serializable object with a type and an optional payload |
|
|
95
|
+
| **Requester** | The `ask*` generator your story calls to yield an action |
|
|
96
|
+
| **Story** | A generator function composing requesters into business logic |
|
|
97
|
+
| **Processor** | The platform-specific function that actually performs an action |
|
|
98
|
+
| **Runtime** | The loop that runs a story, routes each yielded action to a processor, and logs the whole thing |
|
|
99
|
+
|
|
100
|
+
Three things fall out of that arrangement:
|
|
101
|
+
|
|
102
|
+
- **Tests need no infrastructure.** Mock actions by type and assert on the result.
|
|
103
|
+
```typescript
|
|
104
|
+
const result = runStory(askGreet('world'), { [DateActionType.Now]: '2026-01-01T00:00:00.000Z' });
|
|
105
|
+
```
|
|
106
|
+
- **Moving platforms is a config change.** The stories do not know or care which processor set answered them.
|
|
107
|
+
- **Every run leaves a complete log.** Because all side effects pass through the runtime, you get execution logs, replay, and statement-level traces without instrumenting anything. That is what the [admin console](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-web-admin) reads.
|
|
108
|
+
|
|
109
|
+
The [ESLint plugin](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-eslint-config) enforces both halves of the `ask` contract: always `yield*` an ask call, never name a plain function `ask*`.
|
|
110
|
+
|
|
111
|
+
## Packages
|
|
112
|
+
|
|
113
|
+
**Start here**
|
|
114
|
+
|
|
115
|
+
| Package | Description |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| [create-qpq-app](https://github.com/qpqjs/quidproquo/tree/main/create-qpq-app) | Scaffolder. `npx create-qpq-app my-app` |
|
|
118
|
+
| [quidproquo](https://github.com/qpqjs/quidproquo/tree/main/quidproquo) | The main entry point. Re-exports core and webserver |
|
|
119
|
+
| [quidproquo-cli](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-cli) | The `qpq` CLI: dev, build, deploy and teardown |
|
|
120
|
+
|
|
121
|
+
**Framework**
|
|
122
|
+
|
|
123
|
+
| Package | Description |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| [quidproquo-core](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-core) | Actions, stories, the runtime, and the config system |
|
|
126
|
+
| [quidproquo-webserver](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-webserver) | Routes, apis, websockets, dns, email, and the web-side config |
|
|
127
|
+
| [quidproquo-features](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-features) | Ready-made features: admin, auth users, tenants, event documents |
|
|
128
|
+
|
|
129
|
+
**Runtimes**
|
|
130
|
+
|
|
131
|
+
| Package | Description |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| [quidproquo-actionprocessor-js](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-actionprocessor-js) | Processors that need no platform at all |
|
|
134
|
+
| [quidproquo-actionprocessor-node](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-actionprocessor-node) | Node processors |
|
|
135
|
+
| [quidproquo-actionprocessor-awslambda](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-actionprocessor-awslambda) | AWS processors and the Lambda entry handlers |
|
|
136
|
+
| [quidproquo-actionprocessor-web](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-actionprocessor-web) | Browser processors |
|
|
137
|
+
| [quidproquo-dev-server](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-dev-server) | Runs your whole app locally on real code paths |
|
|
138
|
+
|
|
139
|
+
**Frontend**
|
|
140
|
+
|
|
141
|
+
| Package | Description |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| [quidproquo-web](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-web) | Browser utilities, no UI framework required |
|
|
144
|
+
| [quidproquo-web-react](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-web-react) | React bindings: state store, hooks, auth, websockets |
|
|
145
|
+
| [quidproquo-web-admin](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-web-admin) | The admin console: logs, traces, config, maintenance |
|
|
146
|
+
|
|
147
|
+
**Build and deploy**
|
|
148
|
+
|
|
149
|
+
| Package | Description |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| [quidproquo-config-aws](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-config-aws) | AWS-specific config settings |
|
|
152
|
+
| [quidproquo-deploy-awscdk](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-deploy-awscdk) | Turns a qpq config into CDK stacks |
|
|
153
|
+
| [quidproquo-deploy-rspack](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-deploy-rspack) | Rspack bundling for services and federated remotes |
|
|
154
|
+
| [quidproquo-deploy-webpack](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-deploy-webpack) | The same, on webpack |
|
|
155
|
+
|
|
156
|
+
**Tooling**
|
|
157
|
+
|
|
158
|
+
| Package | Description |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| [quidproquo-tsconfig](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-tsconfig) | Shared TypeScript config |
|
|
161
|
+
| [quidproquo-eslint-config](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-eslint-config) | Shared ESLint config and the qpq lint rules |
|
|
162
|
+
| [quidproquo-testing](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-testing) | Generator assertions and vitest matchers |
|
|
163
|
+
|
|
164
|
+
**Integrations**
|
|
165
|
+
|
|
166
|
+
| Package | Description |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| [quidproquo-neo4j](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-neo4j) | Neo4j behind the graph database actions |
|
|
169
|
+
| [quidproquo-xstate](https://github.com/qpqjs/quidproquo/tree/main/quidproquo-xstate) | XState machines as qpq actions |
|
|
170
|
+
|
|
171
|
+
## Working on the framework
|
|
172
|
+
|
|
173
|
+
The repo is an npm workspace. From the root:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npm install
|
|
177
|
+
npm run build # build every package
|
|
178
|
+
npm run build:lite # only what changed since the last build
|
|
179
|
+
npm run test # vitest across the workspace
|
|
180
|
+
npm run lint # eslint across the workspace
|
|
181
|
+
npm run validate # lint, build, test, and typecheck the test suite
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Target a single package with `-w`, for example `npm run watch -w quidproquo-core`.
|
|
185
|
+
|
|
186
|
+
Tests run on vitest and alias sibling packages to their `src`, so you do not need to build before running them.
|
|
187
|
+
|
|
188
|
+
## Status
|
|
189
|
+
|
|
190
|
+
Pre-1.0. The whole family releases in lockstep, so any set of `quidproquo-*` versions that share a number was built and tested together. Expect APIs to move between releases, and pin your versions.
|
|
191
|
+
|
|
192
|
+
## Documentation
|
|
193
|
+
|
|
194
|
+
Full docs, including the action reference, live at **[docs.quidproquojs.com](https://docs.quidproquojs.com)**. The site is built from [quidproquojs.com/docusaurus](https://github.com/qpqjs/quidproquo/tree/main/quidproquojs.com/docusaurus) in this repo, and the app around it is a real qpq workspace that doubles as the `create-qpq-app` template.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT. See [LICENSE](https://github.com/qpqjs/quidproquo/blob/main/LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quidproquo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"lib/**/*"
|
|
11
11
|
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
12
15
|
"scripts": {
|
|
13
16
|
"test": "vitest run",
|
|
14
17
|
"test:watch": "vitest",
|
|
@@ -23,21 +26,21 @@
|
|
|
23
26
|
},
|
|
24
27
|
"repository": {
|
|
25
28
|
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/
|
|
29
|
+
"url": "git+https://github.com/qpqjs/quidproquo.git"
|
|
27
30
|
},
|
|
28
31
|
"keywords": [],
|
|
29
32
|
"author": "",
|
|
30
33
|
"license": "MIT",
|
|
31
34
|
"bugs": {
|
|
32
|
-
"url": "https://github.com/
|
|
35
|
+
"url": "https://github.com/qpqjs/quidproquo/issues"
|
|
33
36
|
},
|
|
34
|
-
"homepage": "https://github.com/
|
|
37
|
+
"homepage": "https://github.com/qpqjs/quidproquo#readme",
|
|
35
38
|
"devDependencies": {
|
|
36
39
|
"typescript": "^5.8.2",
|
|
37
|
-
"quidproquo-tsconfig": "0.1.
|
|
40
|
+
"quidproquo-tsconfig": "0.1.17"
|
|
38
41
|
},
|
|
39
42
|
"dependencies": {
|
|
40
|
-
"quidproquo-core": "0.1.
|
|
41
|
-
"quidproquo-webserver": "0.1.
|
|
43
|
+
"quidproquo-core": "0.1.17",
|
|
44
|
+
"quidproquo-webserver": "0.1.17"
|
|
42
45
|
}
|
|
43
46
|
}
|