@sekiban/create-dcb 0.0.0-stage → 0.1.1
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/LICENSE +90 -0
- package/README.md +18 -2
- package/bin/create-dcb.mjs +91 -0
- package/package.json +17 -4
- package/template/AGENTS.md +23 -0
- package/template/LICENSE +90 -0
- package/template/README.md +76 -0
- package/template/REPLACE.md +34 -0
- package/template/cloudflare.config.ts +13 -0
- package/template/migrations/d1/g32/0001_dcb_events.sql +119 -0
- package/template/migrations/d1/g32/0002_g44_global_completeness.sql +88 -0
- package/template/migrations/d1/g32/0003_g58_safe_lane_health.sql +12 -0
- package/template/migrations/d1/g32/0004_g58_live_poll_health.sql +18 -0
- package/template/migrations/d1/g32/0005_g58_safe_lane_history.sql +18 -0
- package/template/migrations/d1/g32/0006_g60_durable_hop_measurements.sql +31 -0
- package/template/migrations/d1/g32/0007_g60_post_admission_decomposition.sql +30 -0
- package/template/migrations/d1/g32/0008_g60_unsafe_writer_boundaries.sql +24 -0
- package/template/migrations/d1/g32/0009_g65_admission_attempts.sql +23 -0
- package/template/migrations/d1/g32/0010_g65_direct_rings.sql +26 -0
- package/template/migrations/d1/g32/0011_g67_safe_lane_passes.sql +25 -0
- package/template/migrations/d1/g32/0012_g67_safe_lane_pass_ownership.sql +20 -0
- package/template/migrations/d1/g32/0013_g67_safe_lane_catch_up_observations.sql +6 -0
- package/template/migrations/d1/g32/0014_g67_safe_lane_fence_expiry.sql +6 -0
- package/template/migrations/d1/g32/0015_g69_admission_attempts.sql +33 -0
- package/template/migrations/d1/g32/0016_g69_consultation003.sql +12 -0
- package/template/migrations/d1/g32/0017_g69_receipt_identity.sql +61 -0
- package/template/migrations/d1/g32/0018_g69_mutation_evidence.sql +5 -0
- package/template/migrations/d1/g32/0019_g69_mutation_receipt_label.sql +6 -0
- package/template/migrations/d1/g32/0020_g69_hotpath_costs.sql +6 -0
- package/template/migrations/mv/0001_materialized_views.sql +87 -0
- package/template/migrations/mv/0002_unsafe_window_materialized_views.sql +58 -0
- package/template/migrations/mv/0003_checkpoint_ahead_hardening.sql +14 -0
- package/template/migrations/mv/0004_unsafe_window_failure_findings.sql +12 -0
- package/template/migrations/mv/0005_g31_wait_receipts.sql +25 -0
- package/template/migrations/mv/0006_g31_wait_target_poison.sql +48 -0
- package/template/migrations/mv/0007_g69_ordering_quarantine.sql +21 -0
- package/template/migrations/mv/0008_g69_rebuild_verification.sql +4 -0
- package/template/migrations/mv/0009_g69_rebuild_proof.sql +11 -0
- package/template/package.json +24 -0
- package/template/public/.assetsignore +2 -0
- package/template/public/app.js +40 -0
- package/template/public/index.html +58 -0
- package/template/public/styles.css +20 -0
- package/template/scripts/deploy.sh +5 -0
- package/template/scripts/migrate.sh +5 -0
- package/template/src/booking-domain.ts +285 -0
- package/template/src/booking-mv.ts +166 -0
- package/template/src/booking-routes.ts +160 -0
- package/template/src/booking-transport.ts +169 -0
- package/template/src/worker.ts +38 -0
- package/template/tsconfig.json +13 -0
- package/template/wrangler.jsonc +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
Acceptance
|
|
4
|
+
|
|
5
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
6
|
+
|
|
7
|
+
Copyright License
|
|
8
|
+
|
|
9
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
10
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
11
|
+
available, and prepare derivative works of the software, in each case subject
|
|
12
|
+
to the limitations and conditions below.
|
|
13
|
+
|
|
14
|
+
Limitations
|
|
15
|
+
|
|
16
|
+
You may not provide the software to third parties as a hosted or managed
|
|
17
|
+
service, where the service provides users with access to any substantial set
|
|
18
|
+
of the features or functionality of the software.
|
|
19
|
+
|
|
20
|
+
You may not move, change, disable, or circumvent the license key functionality
|
|
21
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
22
|
+
software that is protected by the license key.
|
|
23
|
+
|
|
24
|
+
You may not alter, remove, or obscure any licensing, copyright, or other
|
|
25
|
+
notices of the licensor. Any use of the licensor's trademarks is subject to
|
|
26
|
+
applicable law.
|
|
27
|
+
|
|
28
|
+
Patents
|
|
29
|
+
|
|
30
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
31
|
+
non-sublicensable, non-transferable license under any patent claims the
|
|
32
|
+
licensor can license, or becomes able to license, to make, have made, use,
|
|
33
|
+
sell, offer for sale, import and have imported the software. This license does
|
|
34
|
+
not cover any patent claims that you cause to be infringed by modifications or
|
|
35
|
+
additions to the software.
|
|
36
|
+
|
|
37
|
+
If you or your company make any written claim that the software infringes or
|
|
38
|
+
contributes to infringement of any patent, your patent license for the software
|
|
39
|
+
granted under these terms ends immediately. If your company makes such a
|
|
40
|
+
claim, your patent license ends immediately for work on behalf of your company.
|
|
41
|
+
|
|
42
|
+
Notices
|
|
43
|
+
|
|
44
|
+
You must ensure that anyone who gets a copy of any part of the software from
|
|
45
|
+
you also gets a copy of these terms.
|
|
46
|
+
|
|
47
|
+
If you modify the software, you must include in any modified copies of the
|
|
48
|
+
software prominent notices stating that you have modified the software.
|
|
49
|
+
|
|
50
|
+
No Other Rights
|
|
51
|
+
|
|
52
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
53
|
+
this license.
|
|
54
|
+
|
|
55
|
+
Termination
|
|
56
|
+
|
|
57
|
+
If you use the software in violation of these terms, your license is not
|
|
58
|
+
licensed, and your licenses will automatically terminate. If the licensor
|
|
59
|
+
provides you with a notice of your violation, and you cease all violation of
|
|
60
|
+
the license no later than 30 days after your receipt of the notice, your
|
|
61
|
+
licenses will be reinstated retroactively. However, if you violate these terms
|
|
62
|
+
after such reinstatement, any additional violation of these terms will cause
|
|
63
|
+
your licenses to terminate automatically and permanently.
|
|
64
|
+
|
|
65
|
+
No Liability
|
|
66
|
+
|
|
67
|
+
As far as the law allows, the software comes as is, without any warranty or
|
|
68
|
+
condition, and the licensor will not be liable for any damages arising out of
|
|
69
|
+
these terms or the use or nature of the software, under any kind of legal
|
|
70
|
+
claim.
|
|
71
|
+
|
|
72
|
+
Definitions
|
|
73
|
+
|
|
74
|
+
The licensor is the entity offering these terms, including any portion of the
|
|
75
|
+
software.
|
|
76
|
+
|
|
77
|
+
you refers to the individual or entity agreeing to these terms.
|
|
78
|
+
|
|
79
|
+
your company is any legal entity, sole proprietorship, or other entity that
|
|
80
|
+
has control over, is under the control of, or is under common control with
|
|
81
|
+
that organization.
|
|
82
|
+
|
|
83
|
+
control means ownership of substantially all the assets of an entity, or the
|
|
84
|
+
power to direct its management and policies by vote, contract, or otherwise.
|
|
85
|
+
|
|
86
|
+
your licenses are all the licenses granted to you under these terms.
|
|
87
|
+
|
|
88
|
+
use means anything requiring one of your licenses.
|
|
89
|
+
|
|
90
|
+
trademark means trademarks, service marks, or trade names.
|
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sekiban/create-dcb
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create a named Cloudflare starter with a visible booking demo:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx @sekiban/create-dcb my-booking-app
|
|
7
|
+
cd my-booking-app
|
|
8
|
+
npm install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
If `npm view @sekiban/create-dcb` reports E404, the package has not been published yet; use the repository-checkout alternative:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
node packages/create-dcb/bin/create-dcb.mjs my-booking-app
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The CLI slugifies the project name to lowercase hyphenated form, writes into
|
|
18
|
+
`./<slug>`, and refuses an existing non-empty directory. It does not publish
|
|
19
|
+
the package or create Cloudflare resources.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* global process */
|
|
3
|
+
|
|
4
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const bundledTemplate = resolve(packageRoot, "template");
|
|
10
|
+
|
|
11
|
+
function fail(message) {
|
|
12
|
+
throw new Error(`create-dcb: ${message}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function slugify(value) {
|
|
16
|
+
const slug = value
|
|
17
|
+
.normalize("NFKD")
|
|
18
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
21
|
+
.replace(/^-+|-+$/g, "");
|
|
22
|
+
if (slug.length === 0) fail("project name must contain at least one letter or number");
|
|
23
|
+
return slug.length <= 48 ? slug : slug.slice(0, 48).replace(/-+$/g, "");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function sourceTemplate() {
|
|
27
|
+
if (existsSync(join(bundledTemplate, "package.json"))) return bundledTemplate;
|
|
28
|
+
fail("starter template is missing from this installation");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function assertTargetIsWritable(target) {
|
|
32
|
+
if (!existsSync(target)) return;
|
|
33
|
+
const stats = statSync(target);
|
|
34
|
+
if (!stats.isDirectory()) fail(`target ${target} exists and is not a directory`);
|
|
35
|
+
if (readdirSync(target).length > 0) fail(`target ${target} exists and is not empty`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function replacements(slug) {
|
|
39
|
+
return {
|
|
40
|
+
PROJECT_NAME: slug,
|
|
41
|
+
WORKER_NAME: `${slug}-worker`,
|
|
42
|
+
SERVICE_ID: slug,
|
|
43
|
+
PIPELINE_DB: `${slug}-pipeline`,
|
|
44
|
+
MV_DB: `${slug}-mv`,
|
|
45
|
+
QUEUE_NAME: `${slug}-outbox`,
|
|
46
|
+
DLQ_NAME: `${slug}-outbox-dlq`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function substitute(text, values, file) {
|
|
51
|
+
return text.replace(/\{\{([A-Z_]+)\}\}/g, (whole, key) => {
|
|
52
|
+
const value = values[key];
|
|
53
|
+
if (value === undefined) fail(`unknown placeholder ${whole} in ${file}`);
|
|
54
|
+
return value;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function copyTree(source, target, values, root = source) {
|
|
59
|
+
const entries = readdirSync(source, { withFileTypes: true });
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
const sourcePath = join(source, entry.name);
|
|
62
|
+
const targetPath = join(target, entry.name);
|
|
63
|
+
if (entry.isDirectory()) {
|
|
64
|
+
mkdirSync(targetPath, { recursive: true });
|
|
65
|
+
copyTree(sourcePath, targetPath, values, root);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (!entry.isFile()) fail(`unsupported template entry ${relative(root, sourcePath)}`);
|
|
69
|
+
const relativePath = relative(root, sourcePath);
|
|
70
|
+
const text = readFileSync(sourcePath, "utf8");
|
|
71
|
+
writeFileSync(targetPath, substitute(text, values, relativePath));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function main(argv) {
|
|
76
|
+
if (argv.length !== 1) fail("usage: create-dcb <project-name>");
|
|
77
|
+
const slug = slugify(argv[0]);
|
|
78
|
+
const target = resolve(process.cwd(), slug);
|
|
79
|
+
assertTargetIsWritable(target);
|
|
80
|
+
mkdirSync(target, { recursive: true });
|
|
81
|
+
copyTree(sourceTemplate(), target, replacements(slug));
|
|
82
|
+
process.stdout.write(`Created ${target}\n`);
|
|
83
|
+
process.stdout.write("Next: create the two D1 databases, fill database_id values in wrangler.jsonc, then npm install.\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
main(process.argv.slice(2));
|
|
88
|
+
} catch (error) {
|
|
89
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sekiban/create-dcb",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Create a named Sekiban Cloudflare starter project.",
|
|
6
|
+
"license": "Elastic-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/J-Tech-Japan/sekiban-dcb-ts.git",
|
|
10
|
+
"directory": "packages/create-dcb"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/J-Tech-Japan/sekiban-dcb-ts/tree/main/packages/create-dcb#readme",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"engines": { "node": ">=20" },
|
|
15
|
+
"bin": { "create-dcb": "bin/create-dcb.mjs" },
|
|
16
|
+
"files": ["bin", "template", "README.md", "LICENSE"],
|
|
17
|
+
"scripts": {},
|
|
18
|
+
"publishConfig": { "access": "public", "provenance": true }
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Cloudflare CLI guidance
|
|
2
|
+
|
|
3
|
+
Use `npm run migrate` for D1 migrations and `npm run deploy` for the Worker.
|
|
4
|
+
Wrangler remains this starter's build and deploy path.
|
|
5
|
+
|
|
6
|
+
The deliberate `cloudflare.config.ts` guard refuses cf commands run from the
|
|
7
|
+
project root when they load this project's config. It does not cover
|
|
8
|
+
subdirectories, so never run cf from a subdirectory, especially `public/`, and
|
|
9
|
+
never use `cf init <subdir>`.
|
|
10
|
+
|
|
11
|
+
Do not use `cf deploy` in any form, including `cf deploy --prebuilt`, or use
|
|
12
|
+
`cf build`, `cf dev`, `cf init`, or `cf migrate`. Create D1 databases with
|
|
13
|
+
`npx wrangler d1 create ...`, or run cf resource commands from outside the
|
|
14
|
+
project directory. If cf suggests upgrading Wrangler, that hint is safe to
|
|
15
|
+
follow and does not change `npm run deploy`.
|
|
16
|
+
|
|
17
|
+
`npm run migrate -- --cli cf` is an optional remote-D1 path. It needs real D1
|
|
18
|
+
database IDs, and this repository has not run it against a real remote D1.
|
|
19
|
+
The `cf migrate` conversion is intentionally deferred: a future conversion
|
|
20
|
+
would require a reviewed `migrations_dir`, review of every Durable Object
|
|
21
|
+
binding, and replacement of Durable Object migrations with an `exports`
|
|
22
|
+
lifecycle. cf is beta; Wrangler remains supported for 18 months after the beta
|
|
23
|
+
ends.
|
package/template/LICENSE
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
Acceptance
|
|
4
|
+
|
|
5
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
6
|
+
|
|
7
|
+
Copyright License
|
|
8
|
+
|
|
9
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
10
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
11
|
+
available, and prepare derivative works of the software, in each case subject
|
|
12
|
+
to the limitations and conditions below.
|
|
13
|
+
|
|
14
|
+
Limitations
|
|
15
|
+
|
|
16
|
+
You may not provide the software to third parties as a hosted or managed
|
|
17
|
+
service, where the service provides users with access to any substantial set
|
|
18
|
+
of the features or functionality of the software.
|
|
19
|
+
|
|
20
|
+
You may not move, change, disable, or circumvent the license key functionality
|
|
21
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
22
|
+
software that is protected by the license key.
|
|
23
|
+
|
|
24
|
+
You may not alter, remove, or obscure any licensing, copyright, or other
|
|
25
|
+
notices of the licensor. Any use of the licensor's trademarks is subject to
|
|
26
|
+
applicable law.
|
|
27
|
+
|
|
28
|
+
Patents
|
|
29
|
+
|
|
30
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
31
|
+
non-sublicensable, non-transferable license under any patent claims the
|
|
32
|
+
licensor can license, or becomes able to license, to make, have made, use,
|
|
33
|
+
sell, offer for sale, import and have imported the software. This license does
|
|
34
|
+
not cover any patent claims that you cause to be infringed by modifications or
|
|
35
|
+
additions to the software.
|
|
36
|
+
|
|
37
|
+
If you or your company make any written claim that the software infringes or
|
|
38
|
+
contributes to infringement of any patent, your patent license for the software
|
|
39
|
+
granted under these terms ends immediately. If your company makes such a
|
|
40
|
+
claim, your patent license ends immediately for work on behalf of your company.
|
|
41
|
+
|
|
42
|
+
Notices
|
|
43
|
+
|
|
44
|
+
You must ensure that anyone who gets a copy of any part of the software from
|
|
45
|
+
you also gets a copy of these terms.
|
|
46
|
+
|
|
47
|
+
If you modify the software, you must include in any modified copies of the
|
|
48
|
+
software prominent notices stating that you have modified the software.
|
|
49
|
+
|
|
50
|
+
No Other Rights
|
|
51
|
+
|
|
52
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
53
|
+
this license.
|
|
54
|
+
|
|
55
|
+
Termination
|
|
56
|
+
|
|
57
|
+
If you use the software in violation of these terms, your license is not
|
|
58
|
+
licensed, and your licenses will automatically terminate. If the licensor
|
|
59
|
+
provides you with a notice of your violation, and you cease all violation of
|
|
60
|
+
the license no later than 30 days after your receipt of the notice, your
|
|
61
|
+
licenses will be reinstated retroactively. However, if you violate these terms
|
|
62
|
+
after such reinstatement, any additional violation of these terms will cause
|
|
63
|
+
your licenses to terminate automatically and permanently.
|
|
64
|
+
|
|
65
|
+
No Liability
|
|
66
|
+
|
|
67
|
+
As far as the law allows, the software comes as is, without any warranty or
|
|
68
|
+
condition, and the licensor will not be liable for any damages arising out of
|
|
69
|
+
these terms or the use or nature of the software, under any kind of legal
|
|
70
|
+
claim.
|
|
71
|
+
|
|
72
|
+
Definitions
|
|
73
|
+
|
|
74
|
+
The licensor is the entity offering these terms, including any portion of the
|
|
75
|
+
software.
|
|
76
|
+
|
|
77
|
+
you refers to the individual or entity agreeing to these terms.
|
|
78
|
+
|
|
79
|
+
your company is any legal entity, sole proprietorship, or other entity that
|
|
80
|
+
has control over, is under the control of, or is under common control with
|
|
81
|
+
that organization.
|
|
82
|
+
|
|
83
|
+
control means ownership of substantially all the assets of an entity, or the
|
|
84
|
+
power to direct its management and policies by vote, contract, or otherwise.
|
|
85
|
+
|
|
86
|
+
your licenses are all the licenses granted to you under these terms.
|
|
87
|
+
|
|
88
|
+
use means anything requiring one of your licenses.
|
|
89
|
+
|
|
90
|
+
trademark means trademarks, service marks, or trade names.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
This project is a named Cloudflare Worker starter with a deliberately visible
|
|
4
|
+
booking demo. It creates rooms, reserves them, cancels reservations, releases
|
|
5
|
+
rooms, and exposes both tag-state and list/query reads. The demo is a teaching
|
|
6
|
+
surface: see [REPLACE.md](./REPLACE.md) for the exact files to swap when the
|
|
7
|
+
application gets its own domain.
|
|
8
|
+
|
|
9
|
+
## Create and run locally
|
|
10
|
+
|
|
11
|
+
After `@sekiban/create-dcb` is published:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx @sekiban/create-dcb {{PROJECT_NAME}}
|
|
15
|
+
cd {{PROJECT_NAME}}
|
|
16
|
+
npm install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
While developing this repository before publish, use:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
node packages/create-dcb/bin/create-dcb.mjs {{PROJECT_NAME}}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Cloudflare setup
|
|
26
|
+
|
|
27
|
+
Create two D1 databases for this project, then replace the two
|
|
28
|
+
`database_id` placeholders in `wrangler.jsonc` with the IDs Cloudflare gives
|
|
29
|
+
you. The database names, queue name, and dead-letter queue name are already
|
|
30
|
+
derived from this project name. No live resources are created by the starter
|
|
31
|
+
or by its proof check.
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx wrangler d1 create {{PIPELINE_DB}}
|
|
35
|
+
npx wrangler d1 create {{MV_DB}}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
After filling in both IDs and authenticating Wrangler, the project-local SQL
|
|
39
|
+
can be applied and the Worker deployed through the helper:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npm run migrate
|
|
43
|
+
npm run deploy
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The same commands are available as `scripts/migrate.sh` and
|
|
47
|
+
`scripts/deploy.sh`. Both use only this project's `wrangler.jsonc`.
|
|
48
|
+
|
|
49
|
+
The Worker needs a non-secret `SDT_SERVICE_ID` (already present in the
|
|
50
|
+
generated config) and the normal Cloudflare Queue/D1/ Durable Object bindings.
|
|
51
|
+
Set any operator-only secrets with Wrangler secrets rather than committing
|
|
52
|
+
them to this file.
|
|
53
|
+
|
|
54
|
+
## cf CLI (beta)
|
|
55
|
+
|
|
56
|
+
Use `npm run migrate` for D1 migrations and `npm run deploy` for the Worker.
|
|
57
|
+
Wrangler remains this starter's build and deploy path.
|
|
58
|
+
|
|
59
|
+
The deliberate `cloudflare.config.ts` guard refuses cf commands run from the
|
|
60
|
+
project root when they load this project's config. It does not cover
|
|
61
|
+
subdirectories, so never run cf from a subdirectory, especially `public/`, and
|
|
62
|
+
never use `cf init <subdir>`.
|
|
63
|
+
|
|
64
|
+
Do not use `cf deploy` in any form, including `cf deploy --prebuilt`, or use
|
|
65
|
+
`cf build`, `cf dev`, `cf init`, or `cf migrate`. Create D1 databases with
|
|
66
|
+
`npx wrangler d1 create ...`, or run cf resource commands from outside the
|
|
67
|
+
project directory. If cf suggests upgrading Wrangler, that hint is safe to
|
|
68
|
+
follow and does not change `npm run deploy`.
|
|
69
|
+
|
|
70
|
+
`npm run migrate -- --cli cf` is an optional remote-D1 path. It needs real D1
|
|
71
|
+
database IDs, and this repository has not run it against a real remote D1.
|
|
72
|
+
The `cf migrate` conversion is intentionally deferred: a future conversion
|
|
73
|
+
would require a reviewed `migrations_dir`, review of every Durable Object
|
|
74
|
+
binding, and replacement of Durable Object migrations with an `exports`
|
|
75
|
+
lifecycle. cf is beta; Wrangler remains supported for 18 months after the beta
|
|
76
|
+
ends.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Replace the booking demo
|
|
2
|
+
|
|
3
|
+
The files below are the complete removable demo boundary. Delete or replace
|
|
4
|
+
them together when introducing the application's domain; keep the Worker
|
|
5
|
+
shell and infrastructure listed afterwards.
|
|
6
|
+
|
|
7
|
+
## Delete or replace
|
|
8
|
+
|
|
9
|
+
- `src/booking-domain.ts` — room/reservation commands, events, projectors, and queries.
|
|
10
|
+
- `src/booking-transport.ts` — the command and query transport map.
|
|
11
|
+
- `src/booking-mv.ts` — the two demo materializers and Queue delivery views.
|
|
12
|
+
- `src/booking-routes.ts` — demo HTTP command/read routes.
|
|
13
|
+
- `public/index.html` — demo form markup.
|
|
14
|
+
- `public/app.js` — demo command/read client.
|
|
15
|
+
- `public/styles.css` — demo presentation.
|
|
16
|
+
|
|
17
|
+
If the replacement changes the public route shape, update only the application
|
|
18
|
+
route calls in `src/worker.ts`; the runtime composition and infrastructure
|
|
19
|
+
bindings do not need to change.
|
|
20
|
+
|
|
21
|
+
## Keep
|
|
22
|
+
|
|
23
|
+
- `src/worker.ts` — the Worker shell and Durable Object re-exports.
|
|
24
|
+
- `wrangler.jsonc` — Worker, D1, Queue, dead-letter queue, and Durable Object configuration.
|
|
25
|
+
- `migrations/d1/g32/` and `migrations/mv/` — the project-local migration SQL.
|
|
26
|
+
- `scripts/migrate.sh` and `scripts/deploy.sh` — helper-backed lifecycle commands.
|
|
27
|
+
- `package.json` — the published dependency versions and helper scripts.
|
|
28
|
+
- `cloudflare.config.ts` — deliberate cf CLI guard; do not edit or complete it.
|
|
29
|
+
- `public/.assetsignore` — keeps stray cf config files out of Worker assets.
|
|
30
|
+
- `AGENTS.md` — operational guidance for this starter.
|
|
31
|
+
|
|
32
|
+
The replacement domain must continue to provide the runtime domain/config
|
|
33
|
+
values consumed by `src/worker.ts`, or the Worker shell can be adjusted at
|
|
34
|
+
that single composition seam.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a deliberate cf CLI guard, not an unfinished cf migration.
|
|
3
|
+
* Do not edit, complete, or delete this guard. If cf migrate asks you to
|
|
4
|
+
* finish the existing migration, the answer here is to leave this guard in
|
|
5
|
+
* place and keep using the Wrangler path documented by the starter.
|
|
6
|
+
*/
|
|
7
|
+
if (process.env.SEKIBAN_DCB_CF_HELPER !== "d1-migrations") {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"cf commands that load this project's config are refused in the project root. Use npm run migrate and npm run deploy. Create D1 databases with npx wrangler d1 create ... or run cf resource commands from outside the project directory.",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
-- SDT-G32 new-database baseline. This directory is selected only by the
|
|
2
|
+
-- wipe/cutover worker configuration; it deliberately does not migrate or read
|
|
3
|
+
-- serialized_dcb_events from the pre-G32 service.
|
|
4
|
+
|
|
5
|
+
CREATE TABLE dcb_events (
|
|
6
|
+
"ServiceId" TEXT NOT NULL,
|
|
7
|
+
"Id" TEXT NOT NULL,
|
|
8
|
+
"SortableUniqueId" TEXT NOT NULL COLLATE BINARY,
|
|
9
|
+
"EventType" TEXT NOT NULL,
|
|
10
|
+
"Payload" TEXT NOT NULL,
|
|
11
|
+
"Tags" TEXT NOT NULL,
|
|
12
|
+
"Timestamp" TEXT NOT NULL,
|
|
13
|
+
"CausationId" TEXT,
|
|
14
|
+
"CorrelationId" TEXT,
|
|
15
|
+
"ExecutedUser" TEXT,
|
|
16
|
+
CONSTRAINT "PK_dcb_events" PRIMARY KEY ("ServiceId", "Id")
|
|
17
|
+
);
|
|
18
|
+
CREATE INDEX "IX_Events_ServiceId" ON dcb_events ("ServiceId");
|
|
19
|
+
CREATE INDEX "IX_Events_Service_SortableUniqueId" ON dcb_events ("ServiceId", "SortableUniqueId" COLLATE BINARY);
|
|
20
|
+
CREATE INDEX "IX_dcb_events_EventType" ON dcb_events ("EventType");
|
|
21
|
+
CREATE INDEX "IX_dcb_events_Timestamp" ON dcb_events ("Timestamp");
|
|
22
|
+
|
|
23
|
+
-- Operational facts are intentionally outside the C# logical event record.
|
|
24
|
+
CREATE TABLE dcb_event_ops (
|
|
25
|
+
"ServiceId" TEXT NOT NULL,
|
|
26
|
+
"Id" TEXT NOT NULL,
|
|
27
|
+
"AttemptId" TEXT,
|
|
28
|
+
"AllocatorLineageId" TEXT,
|
|
29
|
+
"FirstArrivedAt" INTEGER NOT NULL,
|
|
30
|
+
"LastArrivedAt" INTEGER NOT NULL,
|
|
31
|
+
"MaxDeliveryLagMs" INTEGER NOT NULL,
|
|
32
|
+
PRIMARY KEY ("ServiceId", "Id"),
|
|
33
|
+
FOREIGN KEY ("ServiceId", "Id") REFERENCES dcb_events ("ServiceId", "Id") ON DELETE CASCADE
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE TABLE serialized_dcb_allocator_bindings (
|
|
37
|
+
service_id TEXT PRIMARY KEY,
|
|
38
|
+
allocator_lineage_id TEXT NOT NULL,
|
|
39
|
+
bound_at INTEGER NOT NULL
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
CREATE TABLE serialized_dcb_event_arrivals (
|
|
43
|
+
service_id TEXT NOT NULL,
|
|
44
|
+
event_id TEXT NOT NULL,
|
|
45
|
+
tag TEXT NOT NULL COLLATE BINARY,
|
|
46
|
+
enqueued_at INTEGER NOT NULL,
|
|
47
|
+
arrived_at INTEGER NOT NULL,
|
|
48
|
+
lag_ms INTEGER NOT NULL,
|
|
49
|
+
PRIMARY KEY (service_id, event_id, tag),
|
|
50
|
+
FOREIGN KEY (service_id, event_id) REFERENCES dcb_events ("ServiceId", "Id") ON DELETE CASCADE
|
|
51
|
+
);
|
|
52
|
+
CREATE INDEX serialized_dcb_event_arrivals_service_idx ON serialized_dcb_event_arrivals (service_id, event_id, tag COLLATE BINARY);
|
|
53
|
+
|
|
54
|
+
CREATE TABLE serialized_dcb_lag_estimates (
|
|
55
|
+
service_id TEXT PRIMARY KEY,
|
|
56
|
+
estimate_ms INTEGER NOT NULL,
|
|
57
|
+
observed_at INTEGER NOT NULL
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE serialized_dcb_pending_arrivals (
|
|
61
|
+
service_id TEXT NOT NULL,
|
|
62
|
+
event_id TEXT NOT NULL,
|
|
63
|
+
attempt_id TEXT NOT NULL,
|
|
64
|
+
suid TEXT NOT NULL COLLATE BINARY,
|
|
65
|
+
expected_paths TEXT NOT NULL,
|
|
66
|
+
observed_paths TEXT NOT NULL,
|
|
67
|
+
first_observed_at INTEGER NOT NULL,
|
|
68
|
+
lag_bound_ms INTEGER NOT NULL,
|
|
69
|
+
PRIMARY KEY (service_id, event_id)
|
|
70
|
+
);
|
|
71
|
+
CREATE INDEX serialized_dcb_pending_service_idx ON serialized_dcb_pending_arrivals (service_id, event_id COLLATE BINARY);
|
|
72
|
+
|
|
73
|
+
CREATE TABLE serialized_dcb_inconsistency_findings (
|
|
74
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
75
|
+
service_id TEXT NOT NULL,
|
|
76
|
+
event_id TEXT NOT NULL,
|
|
77
|
+
path TEXT NOT NULL COLLATE BINARY,
|
|
78
|
+
classification TEXT NOT NULL,
|
|
79
|
+
first_observed_at INTEGER NOT NULL,
|
|
80
|
+
lag_bound_ms INTEGER NOT NULL,
|
|
81
|
+
observed_at INTEGER NOT NULL,
|
|
82
|
+
UNIQUE (service_id, event_id, path, classification)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
CREATE TABLE serialized_dcb_delivery_incidents (
|
|
86
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
87
|
+
service_id TEXT NOT NULL,
|
|
88
|
+
identity_key TEXT NOT NULL COLLATE BINARY,
|
|
89
|
+
classification TEXT NOT NULL,
|
|
90
|
+
suid TEXT COLLATE BINARY,
|
|
91
|
+
existing_event_id TEXT,
|
|
92
|
+
incoming_event_id TEXT,
|
|
93
|
+
event_id TEXT,
|
|
94
|
+
bound_lineage_id TEXT,
|
|
95
|
+
incoming_lineage_id TEXT,
|
|
96
|
+
observed_at INTEGER NOT NULL,
|
|
97
|
+
UNIQUE (service_id, identity_key)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
CREATE TABLE serialized_dcb_projection_checkpoints (
|
|
101
|
+
service_id TEXT NOT NULL,
|
|
102
|
+
projection_id TEXT NOT NULL COLLATE BINARY,
|
|
103
|
+
last_suid TEXT NOT NULL COLLATE BINARY,
|
|
104
|
+
state_json TEXT NOT NULL,
|
|
105
|
+
version INTEGER NOT NULL,
|
|
106
|
+
updated_at INTEGER NOT NULL,
|
|
107
|
+
PRIMARY KEY (service_id, projection_id)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
CREATE TABLE serialized_dcb_wait_target_incidents (
|
|
111
|
+
service_id TEXT NOT NULL,
|
|
112
|
+
suid TEXT NOT NULL COLLATE BINARY,
|
|
113
|
+
classification TEXT NOT NULL,
|
|
114
|
+
event_id TEXT,
|
|
115
|
+
observed_at INTEGER NOT NULL,
|
|
116
|
+
PRIMARY KEY (service_id, suid, classification)
|
|
117
|
+
);
|
|
118
|
+
CREATE INDEX serialized_dcb_wait_target_incidents_lookup_idx
|
|
119
|
+
ON serialized_dcb_wait_target_incidents (service_id, suid COLLATE BINARY, classification);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
-- SDT-G44 global-array completeness authority.
|
|
2
|
+
--
|
|
3
|
+
-- This is deliberately an ordinary in-place development migration. The
|
|
4
|
+
-- repository has no production data to preserve: incompatible test data is
|
|
5
|
+
-- reset rather than bridged through a mixed-version protocol.
|
|
6
|
+
|
|
7
|
+
ALTER TABLE dcb_events ADD COLUMN "EventDigest" TEXT;
|
|
8
|
+
|
|
9
|
+
-- A source partition is a Tag Durable Object identity. Its local
|
|
10
|
+
-- obligation_sequence has no global meaning, so the scanner snapshots this
|
|
11
|
+
-- set together with each partition's own high-water sequence.
|
|
12
|
+
CREATE TABLE serialized_dcb_source_partitions (
|
|
13
|
+
service_id TEXT NOT NULL,
|
|
14
|
+
partition_tag TEXT NOT NULL COLLATE BINARY,
|
|
15
|
+
last_obligation_sequence INTEGER NOT NULL,
|
|
16
|
+
registered_at INTEGER NOT NULL,
|
|
17
|
+
PRIMARY KEY (service_id, partition_tag)
|
|
18
|
+
);
|
|
19
|
+
CREATE INDEX serialized_dcb_source_partitions_service_idx
|
|
20
|
+
ON serialized_dcb_source_partitions (service_id, partition_tag COLLATE BINARY);
|
|
21
|
+
|
|
22
|
+
-- A tag-side committed membership is distinct from dcb_events.Tags, which is
|
|
23
|
+
-- only the declared tag set. It is written by the global array receiver in
|
|
24
|
+
-- the same D1 batch as the event and receipt.
|
|
25
|
+
CREATE TABLE serialized_dcb_global_memberships (
|
|
26
|
+
service_id TEXT NOT NULL,
|
|
27
|
+
event_id TEXT NOT NULL,
|
|
28
|
+
partition_tag TEXT NOT NULL COLLATE BINARY,
|
|
29
|
+
event_digest TEXT NOT NULL,
|
|
30
|
+
committed_at INTEGER NOT NULL,
|
|
31
|
+
PRIMARY KEY (service_id, event_id, partition_tag),
|
|
32
|
+
FOREIGN KEY (service_id, event_id) REFERENCES dcb_events ("ServiceId", "Id") ON DELETE RESTRICT
|
|
33
|
+
);
|
|
34
|
+
CREATE INDEX serialized_dcb_global_memberships_lookup_idx
|
|
35
|
+
ON serialized_dcb_global_memberships (service_id, partition_tag COLLATE BINARY, event_id);
|
|
36
|
+
|
|
37
|
+
-- This receipt is the sink fact that a source obligation may read back. It
|
|
38
|
+
-- is intentionally not a Queue acknowledgement and is never inferred from a
|
|
39
|
+
-- projection or delivery runner. Its semantic identity is
|
|
40
|
+
-- (service_id, event_id, event_digest, membership_tag); the separate unique
|
|
41
|
+
-- source key maps that receipt back to the DO-local obligation sequence.
|
|
42
|
+
CREATE TABLE serialized_dcb_global_receipts (
|
|
43
|
+
service_id TEXT NOT NULL,
|
|
44
|
+
partition_tag TEXT NOT NULL COLLATE BINARY,
|
|
45
|
+
obligation_sequence INTEGER NOT NULL,
|
|
46
|
+
event_id TEXT NOT NULL,
|
|
47
|
+
event_digest TEXT NOT NULL,
|
|
48
|
+
membership_tag TEXT NOT NULL COLLATE BINARY,
|
|
49
|
+
received_at INTEGER NOT NULL,
|
|
50
|
+
PRIMARY KEY (service_id, partition_tag, obligation_sequence),
|
|
51
|
+
UNIQUE (service_id, event_id, event_digest, membership_tag),
|
|
52
|
+
FOREIGN KEY (service_id, event_id, membership_tag)
|
|
53
|
+
REFERENCES serialized_dcb_global_memberships (service_id, event_id, partition_tag)
|
|
54
|
+
ON DELETE RESTRICT
|
|
55
|
+
);
|
|
56
|
+
CREATE INDEX serialized_dcb_global_receipts_event_idx
|
|
57
|
+
ON serialized_dcb_global_receipts (service_id, event_id, membership_tag COLLATE BINARY);
|
|
58
|
+
|
|
59
|
+
-- Independent scanner progress is explicit. Its only interim operational
|
|
60
|
+
-- state is BLOCK/UNSETTLED; it deliberately has no owner/ack/closure fields.
|
|
61
|
+
CREATE TABLE serialized_dcb_completeness_scanner_health (
|
|
62
|
+
service_id TEXT PRIMARY KEY,
|
|
63
|
+
scanner_version TEXT NOT NULL,
|
|
64
|
+
status TEXT NOT NULL CHECK (status IN ('HEALTHY', 'UNKNOWN', 'FAILED', 'STALE', 'BLOCK', 'UNSETTLED')),
|
|
65
|
+
cursor_json TEXT,
|
|
66
|
+
last_full_scan_at INTEGER,
|
|
67
|
+
last_error TEXT,
|
|
68
|
+
updated_at INTEGER NOT NULL
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
CREATE TABLE serialized_dcb_completeness_findings (
|
|
72
|
+
service_id TEXT NOT NULL,
|
|
73
|
+
incident_identity TEXT NOT NULL COLLATE BINARY,
|
|
74
|
+
incident_type TEXT NOT NULL,
|
|
75
|
+
-- Event fields are NULL only for a source-partition or scanner failure
|
|
76
|
+
-- discovered before a specific obligation can be read. The same findings
|
|
77
|
+
-- table remains the sole unresolved-state authority.
|
|
78
|
+
partition_tag TEXT COLLATE BINARY,
|
|
79
|
+
obligation_sequence INTEGER,
|
|
80
|
+
event_id TEXT,
|
|
81
|
+
event_digest TEXT,
|
|
82
|
+
state TEXT NOT NULL CHECK (state IN ('OPEN', 'UNRESOLVED')),
|
|
83
|
+
first_observed_at INTEGER NOT NULL,
|
|
84
|
+
last_observed_at INTEGER NOT NULL,
|
|
85
|
+
PRIMARY KEY (service_id, incident_identity)
|
|
86
|
+
);
|
|
87
|
+
CREATE INDEX serialized_dcb_completeness_findings_open_idx
|
|
88
|
+
ON serialized_dcb_completeness_findings (service_id, state, partition_tag COLLATE BINARY, obligation_sequence);
|