@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.
Files changed (52) hide show
  1. package/LICENSE +90 -0
  2. package/README.md +18 -2
  3. package/bin/create-dcb.mjs +91 -0
  4. package/package.json +17 -4
  5. package/template/AGENTS.md +23 -0
  6. package/template/LICENSE +90 -0
  7. package/template/README.md +76 -0
  8. package/template/REPLACE.md +34 -0
  9. package/template/cloudflare.config.ts +13 -0
  10. package/template/migrations/d1/g32/0001_dcb_events.sql +119 -0
  11. package/template/migrations/d1/g32/0002_g44_global_completeness.sql +88 -0
  12. package/template/migrations/d1/g32/0003_g58_safe_lane_health.sql +12 -0
  13. package/template/migrations/d1/g32/0004_g58_live_poll_health.sql +18 -0
  14. package/template/migrations/d1/g32/0005_g58_safe_lane_history.sql +18 -0
  15. package/template/migrations/d1/g32/0006_g60_durable_hop_measurements.sql +31 -0
  16. package/template/migrations/d1/g32/0007_g60_post_admission_decomposition.sql +30 -0
  17. package/template/migrations/d1/g32/0008_g60_unsafe_writer_boundaries.sql +24 -0
  18. package/template/migrations/d1/g32/0009_g65_admission_attempts.sql +23 -0
  19. package/template/migrations/d1/g32/0010_g65_direct_rings.sql +26 -0
  20. package/template/migrations/d1/g32/0011_g67_safe_lane_passes.sql +25 -0
  21. package/template/migrations/d1/g32/0012_g67_safe_lane_pass_ownership.sql +20 -0
  22. package/template/migrations/d1/g32/0013_g67_safe_lane_catch_up_observations.sql +6 -0
  23. package/template/migrations/d1/g32/0014_g67_safe_lane_fence_expiry.sql +6 -0
  24. package/template/migrations/d1/g32/0015_g69_admission_attempts.sql +33 -0
  25. package/template/migrations/d1/g32/0016_g69_consultation003.sql +12 -0
  26. package/template/migrations/d1/g32/0017_g69_receipt_identity.sql +61 -0
  27. package/template/migrations/d1/g32/0018_g69_mutation_evidence.sql +5 -0
  28. package/template/migrations/d1/g32/0019_g69_mutation_receipt_label.sql +6 -0
  29. package/template/migrations/d1/g32/0020_g69_hotpath_costs.sql +6 -0
  30. package/template/migrations/mv/0001_materialized_views.sql +87 -0
  31. package/template/migrations/mv/0002_unsafe_window_materialized_views.sql +58 -0
  32. package/template/migrations/mv/0003_checkpoint_ahead_hardening.sql +14 -0
  33. package/template/migrations/mv/0004_unsafe_window_failure_findings.sql +12 -0
  34. package/template/migrations/mv/0005_g31_wait_receipts.sql +25 -0
  35. package/template/migrations/mv/0006_g31_wait_target_poison.sql +48 -0
  36. package/template/migrations/mv/0007_g69_ordering_quarantine.sql +21 -0
  37. package/template/migrations/mv/0008_g69_rebuild_verification.sql +4 -0
  38. package/template/migrations/mv/0009_g69_rebuild_proof.sql +11 -0
  39. package/template/package.json +24 -0
  40. package/template/public/.assetsignore +2 -0
  41. package/template/public/app.js +40 -0
  42. package/template/public/index.html +58 -0
  43. package/template/public/styles.css +20 -0
  44. package/template/scripts/deploy.sh +5 -0
  45. package/template/scripts/migrate.sh +5 -0
  46. package/template/src/booking-domain.ts +285 -0
  47. package/template/src/booking-mv.ts +166 -0
  48. package/template/src/booking-routes.ts +160 -0
  49. package/template/src/booking-transport.ts +169 -0
  50. package/template/src/worker.ts +38 -0
  51. package/template/tsconfig.json +13 -0
  52. 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
- # Temporary Holding Version
1
+ # @sekiban/create-dcb
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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.
@@ -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);