@stackonward/cli 1.4.0 → 1.5.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 (55) hide show
  1. package/README.md +255 -0
  2. package/dist/index.js +14335 -603
  3. package/dist/index.js.map +1 -1
  4. package/dist/resources/deploy/ci/github/caller.yml +41 -0
  5. package/dist/resources/deploy/ci/onedev/import.yml +40 -0
  6. package/dist/resources/deploy/nginx/cache.conf +17 -0
  7. package/dist/resources/deploy/nginx/compression.conf +23 -0
  8. package/dist/resources/deploy/nginx/proxy.conf +15 -0
  9. package/dist/resources/deploy/profiles/go-service/compose.yaml +19 -0
  10. package/dist/resources/deploy/profiles/nuxt-node/Dockerfile +17 -0
  11. package/dist/resources/deploy/profiles/static-files/nginx/site.conf.template +55 -0
  12. package/dist/resources/deploy/profiles/static-nginx/Dockerfile +18 -0
  13. package/dist/resources/deploy/profiles/static-nginx/nginx/default.conf.template +28 -0
  14. package/dist/resources/deploy/targets/compose-host/deploy.config.example.json +114 -0
  15. package/dist/resources/deploy/traffic/nginx/stackonward-nginx-traffic +96 -0
  16. package/dist/resources/registry.json +30 -30
  17. package/dist/resources/templates/base/.claude/docs/architecture.md +4 -0
  18. package/dist/resources/templates/base/AGENTS.md +15 -0
  19. package/dist/resources/templates/base/CLAUDE.md +15 -0
  20. package/dist/resources/templates/go-backend/.dockerignore +25 -3
  21. package/dist/resources/templates/go-backend/deploy.config.json +26 -0
  22. package/dist/resources/templates/go-backend/docs/runtime-provenance.json +9 -6
  23. package/dist/resources/templates/go-backend/docs/source-provenance.json +33 -22
  24. package/dist/resources/templates/go-backend/internal/common/enums/register_locale.go +7 -0
  25. package/dist/resources/templates/go-backend/internal/common/enums/register_locale_test.go +30 -0
  26. package/dist/resources/templates/go-backend/internal/common/locale/locale.go +102 -76
  27. package/dist/resources/templates/go-backend/internal/common/locale/locale_test.go +168 -45
  28. package/dist/resources/templates/go-backend/internal/core/commandreplay/model.go +5 -5
  29. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence.go +20 -7
  30. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence_test.go +51 -22
  31. package/dist/resources/templates/go-backend/internal/core/commandreplay/repository.go +2 -2
  32. package/dist/resources/templates/go-backend/internal/core/commandreplay/service.go +13 -24
  33. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_persistence_test.go +98 -13
  34. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_test.go +61 -36
  35. package/dist/resources/templates/go-backend/internal/core/commandreplay/types.go +6 -6
  36. package/dist/resources/templates/go-backend/internal/core/integrationevent/config.go +24 -0
  37. package/dist/resources/templates/go-backend/internal/core/integrationevent/errors.go +53 -0
  38. package/dist/resources/templates/go-backend/internal/core/integrationevent/model.go +71 -0
  39. package/dist/resources/templates/go-backend/internal/core/integrationevent/persistence.go +193 -0
  40. package/dist/resources/templates/go-backend/internal/core/integrationevent/persistence_postgres_test.go +178 -0
  41. package/dist/resources/templates/go-backend/internal/core/integrationevent/persistence_test.go +168 -0
  42. package/dist/resources/templates/go-backend/internal/core/integrationevent/provider.go +16 -0
  43. package/dist/resources/templates/go-backend/internal/core/integrationevent/registry.go +69 -0
  44. package/dist/resources/templates/go-backend/internal/core/integrationevent/repository.go +17 -0
  45. package/dist/resources/templates/go-backend/internal/core/integrationevent/service.go +283 -0
  46. package/dist/resources/templates/go-backend/internal/core/integrationevent/service_test.go +433 -0
  47. package/dist/resources/templates/go-backend/internal/core/integrationevent/types.go +67 -0
  48. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.down.sql +6 -0
  49. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.up.sql +176 -16
  50. package/dist/resources/templates/go-backend/tests/infrastructure.sql +109 -6
  51. package/dist/resources/templates/nuxt/.claude/docs/stack.md +5 -1
  52. package/dist/resources/templates/nuxt/.dockerignore +19 -0
  53. package/dist/resources/templates/nuxt/deploy.config.json +27 -0
  54. package/dist/resources/templates/nuxt/package.json +8 -8
  55. package/package.json +5 -3
@@ -714,6 +714,174 @@ $$;
714
714
 
715
715
  CREATE TRIGGER trg_sys_operation_logs_finalization_guard BEFORE DELETE OR UPDATE ON public.sys_operation_logs FOR EACH ROW EXECUTE FUNCTION public.sys_operation_logs_finalization_guard();
716
716
 
717
+ CREATE TABLE public.sys_integration_event_inbox (
718
+ id bigserial PRIMARY KEY,
719
+ source_system varchar(191) NOT NULL,
720
+ event_id varchar(191) NOT NULL,
721
+ event_type varchar(191) NOT NULL,
722
+ schema_version varchar(32) NOT NULL,
723
+ product_code varchar(50) NOT NULL,
724
+ aggregate_type varchar(128) NOT NULL,
725
+ aggregate_ref varchar(191) NOT NULL,
726
+ aggregate_version bigint NOT NULL,
727
+ payload_hash char(64) NOT NULL,
728
+ event_payload jsonb NOT NULL,
729
+ received_at timestamptz(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
730
+ created_at timestamptz(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
731
+ CONSTRAINT ck_sys_integration_event_inbox_identity CHECK (BTRIM(source_system) <> '' AND BTRIM(event_id) <> ''),
732
+ CONSTRAINT ck_sys_integration_event_inbox_product CHECK (product_code ~ '^[a-z][a-z0-9_]{1,49}$'),
733
+ CONSTRAINT ck_sys_integration_event_inbox_version CHECK (aggregate_version > 0),
734
+ CONSTRAINT ck_sys_integration_event_inbox_hash CHECK (payload_hash ~ '^[0-9a-f]{64}$'),
735
+ CONSTRAINT ck_sys_integration_event_inbox_payload CHECK (jsonb_typeof(event_payload) = 'object'),
736
+ CONSTRAINT uk_sys_integration_event_inbox_identity UNIQUE (source_system, event_id)
737
+ );
738
+
739
+ COMMENT ON TABLE public.sys_integration_event_inbox IS '已验证入站 Integration Event 的全局事件身份与规范负载';
740
+ COMMENT ON COLUMN public.sys_integration_event_inbox.id IS '主键 ID';
741
+ COMMENT ON COLUMN public.sys_integration_event_inbox.source_system IS '事件来源系统稳定标识';
742
+ COMMENT ON COLUMN public.sys_integration_event_inbox.event_id IS '来源系统内的事件唯一标识';
743
+ COMMENT ON COLUMN public.sys_integration_event_inbox.event_type IS '事件类型';
744
+ COMMENT ON COLUMN public.sys_integration_event_inbox.schema_version IS '事件负载 schema 版本';
745
+ COMMENT ON COLUMN public.sys_integration_event_inbox.product_code IS '受信事件产品编码';
746
+ COMMENT ON COLUMN public.sys_integration_event_inbox.aggregate_type IS '上游聚合类型';
747
+ COMMENT ON COLUMN public.sys_integration_event_inbox.aggregate_ref IS '上游聚合公开引用';
748
+ COMMENT ON COLUMN public.sys_integration_event_inbox.aggregate_version IS '上游聚合版本';
749
+ COMMENT ON COLUMN public.sys_integration_event_inbox.payload_hash IS '规范事件信封 SHA-256';
750
+ COMMENT ON COLUMN public.sys_integration_event_inbox.event_payload IS 'RFC 8785 规范化事件信封';
751
+ COMMENT ON COLUMN public.sys_integration_event_inbox.received_at IS '入站验证完成时间';
752
+ COMMENT ON COLUMN public.sys_integration_event_inbox.created_at IS '创建时间';
753
+
754
+ CREATE TABLE public.sys_integration_event_receipts (
755
+ id bigserial PRIMARY KEY,
756
+ source_system varchar(191) NOT NULL,
757
+ subscriber_code varchar(64) NOT NULL,
758
+ event_id varchar(191) NOT NULL,
759
+ status varchar(20) NOT NULL DEFAULT 'processing',
760
+ receipt_status varchar(20),
761
+ error_code varchar(120),
762
+ processing_started_at timestamptz(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
763
+ processed_at timestamptz(3),
764
+ created_at timestamptz(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
765
+ updated_at timestamptz(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
766
+ CONSTRAINT uk_sys_integration_event_receipts_subscriber_event UNIQUE (source_system, subscriber_code, event_id),
767
+ CONSTRAINT ck_sys_integration_event_receipts_status CHECK (status IN ('processing', 'processed', 'failed')),
768
+ CONSTRAINT ck_sys_integration_event_receipts_result CHECK (
769
+ (status = 'processed' AND processed_at IS NOT NULL AND receipt_status IN ('processed', 'duplicate') AND error_code IS NULL)
770
+ OR (status = 'failed' AND processed_at IS NOT NULL AND receipt_status IS NULL AND error_code IS NOT NULL AND BTRIM(error_code) <> '')
771
+ OR (status = 'processing' AND processed_at IS NULL AND receipt_status IS NULL AND error_code IS NULL)
772
+ )
773
+ );
774
+
775
+ COMMENT ON TABLE public.sys_integration_event_receipts IS '每个订阅者对入站事件的独立持久处理回执';
776
+ COMMENT ON COLUMN public.sys_integration_event_receipts.id IS '主键 ID';
777
+ COMMENT ON COLUMN public.sys_integration_event_receipts.source_system IS '事件来源系统稳定标识';
778
+ COMMENT ON COLUMN public.sys_integration_event_receipts.subscriber_code IS '目标订阅者稳定编码';
779
+ COMMENT ON COLUMN public.sys_integration_event_receipts.event_id IS '来源系统内的事件唯一标识';
780
+ COMMENT ON COLUMN public.sys_integration_event_receipts.status IS '订阅者处理状态';
781
+ COMMENT ON COLUMN public.sys_integration_event_receipts.receipt_status IS '处理成功或重复的回执状态';
782
+ COMMENT ON COLUMN public.sys_integration_event_receipts.error_code IS '稳定处理错误码';
783
+ COMMENT ON COLUMN public.sys_integration_event_receipts.processing_started_at IS '订阅者开始处理时间';
784
+ COMMENT ON COLUMN public.sys_integration_event_receipts.processed_at IS '处理终结时间';
785
+ COMMENT ON COLUMN public.sys_integration_event_receipts.created_at IS '创建时间';
786
+ COMMENT ON COLUMN public.sys_integration_event_receipts.updated_at IS '更新时间';
787
+
788
+ CREATE INDEX idx_sys_integration_event_receipts_active
789
+ ON public.sys_integration_event_receipts (processing_started_at, id)
790
+ WHERE status = 'processing';
791
+
792
+ CREATE TABLE public.sys_integration_aggregate_checkpoints (
793
+ id bigserial PRIMARY KEY,
794
+ source_system varchar(191) NOT NULL,
795
+ subscriber_code varchar(64) NOT NULL,
796
+ aggregate_type varchar(128) NOT NULL,
797
+ aggregate_ref varchar(191) NOT NULL,
798
+ last_aggregate_version bigint NOT NULL,
799
+ last_event_id varchar(191) NOT NULL,
800
+ last_payload_hash char(64) NOT NULL,
801
+ revision_no bigint NOT NULL DEFAULT 1,
802
+ created_at timestamptz(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
803
+ updated_at timestamptz(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
804
+ CONSTRAINT ck_sys_integration_aggregate_checkpoints_version CHECK (last_aggregate_version > 0 AND revision_no > 0),
805
+ CONSTRAINT ck_sys_integration_aggregate_checkpoints_hash CHECK (last_payload_hash ~ '^[0-9a-f]{64}$'),
806
+ CONSTRAINT uk_sys_integration_aggregate_checkpoints_scope UNIQUE (source_system, subscriber_code, aggregate_type, aggregate_ref)
807
+ );
808
+
809
+ COMMENT ON TABLE public.sys_integration_aggregate_checkpoints IS '入站订阅者对上游聚合的连续版本检查点';
810
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.id IS '主键 ID';
811
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.source_system IS '事件来源系统稳定标识';
812
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.subscriber_code IS '目标订阅者稳定编码';
813
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.aggregate_type IS '上游聚合类型';
814
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.aggregate_ref IS '上游聚合公开引用';
815
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.last_aggregate_version IS '已连续处理的最后聚合版本';
816
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.last_event_id IS '最后处理的事件 ID';
817
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.last_payload_hash IS '最后处理的规范事件信封 SHA-256';
818
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.revision_no IS '本地乐观锁修订号';
819
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.created_at IS '创建时间';
820
+ COMMENT ON COLUMN public.sys_integration_aggregate_checkpoints.updated_at IS '更新时间';
821
+
822
+ CREATE FUNCTION public.reject_sys_integration_event_inbox_change() RETURNS trigger
823
+ LANGUAGE plpgsql
824
+ AS $$
825
+ BEGIN
826
+ RAISE EXCEPTION 'sys_integration_event_inbox is append-only';
827
+ END;
828
+ $$;
829
+
830
+ CREATE TRIGGER trg_sys_integration_event_inbox_append
831
+ BEFORE UPDATE OR DELETE ON public.sys_integration_event_inbox
832
+ FOR EACH ROW EXECUTE FUNCTION public.reject_sys_integration_event_inbox_change();
833
+
834
+ CREATE FUNCTION public.guard_sys_integration_event_receipt_transition() RETURNS trigger
835
+ LANGUAGE plpgsql
836
+ AS $$
837
+ BEGIN
838
+ IF TG_OP = 'DELETE' THEN
839
+ RAISE EXCEPTION 'sys_integration_event_receipts cannot be deleted';
840
+ END IF;
841
+ IF OLD.source_system IS DISTINCT FROM NEW.source_system
842
+ OR OLD.subscriber_code IS DISTINCT FROM NEW.subscriber_code
843
+ OR OLD.event_id IS DISTINCT FROM NEW.event_id
844
+ OR OLD.processing_started_at IS DISTINCT FROM NEW.processing_started_at
845
+ OR OLD.created_at IS DISTINCT FROM NEW.created_at THEN
846
+ RAISE EXCEPTION 'sys_integration_event_receipts identity is immutable';
847
+ END IF;
848
+ IF OLD.status = 'processing' AND NEW.status IN ('processed', 'failed') THEN
849
+ RETURN NEW;
850
+ END IF;
851
+ RAISE EXCEPTION 'sys_integration_event_receipts only allow processing to terminal transition';
852
+ END;
853
+ $$;
854
+
855
+ CREATE TRIGGER trg_sys_integration_event_receipts_transition
856
+ BEFORE UPDATE OR DELETE ON public.sys_integration_event_receipts
857
+ FOR EACH ROW EXECUTE FUNCTION public.guard_sys_integration_event_receipt_transition();
858
+
859
+ CREATE FUNCTION public.guard_sys_integration_aggregate_checkpoint_transition() RETURNS trigger
860
+ LANGUAGE plpgsql
861
+ AS $$
862
+ BEGIN
863
+ IF TG_OP = 'DELETE' THEN
864
+ RAISE EXCEPTION 'sys_integration_aggregate_checkpoints cannot be deleted';
865
+ END IF;
866
+ IF OLD.source_system IS DISTINCT FROM NEW.source_system
867
+ OR OLD.subscriber_code IS DISTINCT FROM NEW.subscriber_code
868
+ OR OLD.aggregate_type IS DISTINCT FROM NEW.aggregate_type
869
+ OR OLD.aggregate_ref IS DISTINCT FROM NEW.aggregate_ref
870
+ OR OLD.created_at IS DISTINCT FROM NEW.created_at THEN
871
+ RAISE EXCEPTION 'sys_integration_aggregate_checkpoints identity is immutable';
872
+ END IF;
873
+ IF NEW.last_aggregate_version <> OLD.last_aggregate_version + 1
874
+ OR NEW.revision_no <> OLD.revision_no + 1 THEN
875
+ RAISE EXCEPTION 'sys_integration_aggregate_checkpoints must advance exactly one version';
876
+ END IF;
877
+ RETURN NEW;
878
+ END;
879
+ $$;
880
+
881
+ CREATE TRIGGER trg_sys_integration_aggregate_checkpoints_transition
882
+ BEFORE UPDATE OR DELETE ON public.sys_integration_aggregate_checkpoints
883
+ FOR EACH ROW EXECUTE FUNCTION public.guard_sys_integration_aggregate_checkpoint_transition();
884
+
717
885
  CREATE TABLE sys_command_replay_records (
718
886
  id bigserial PRIMARY KEY,
719
887
  created_at timestamp(3) with time zone DEFAULT now() NOT NULL,
@@ -721,8 +889,8 @@ CREATE TABLE sys_command_replay_records (
721
889
  command_scope character varying(128) NOT NULL,
722
890
  idempotency_key character varying(191) NOT NULL,
723
891
  request_hash character varying(64) NOT NULL,
724
- principal_type character varying(32),
725
- principal_ref character varying(64),
892
+ principal_type character varying(32) NOT NULL,
893
+ principal_ref character varying(64) NOT NULL,
726
894
  status character varying(20) DEFAULT 'started'::character varying NOT NULL,
727
895
  response_payload jsonb DEFAULT '{}'::jsonb NOT NULL,
728
896
  error_code character varying(120),
@@ -741,8 +909,8 @@ CREATE TABLE sys_command_replay_records (
741
909
  );
742
910
 
743
911
  ALTER TABLE sys_command_replay_records
744
- ADD CONSTRAINT "uk_sys_command_replay_records_namespace_scope_key"
745
- UNIQUE ("namespace", "command_scope", "idempotency_key"),
912
+ ADD CONSTRAINT "uk_sys_command_replay_records_principal_scope_key"
913
+ UNIQUE ("namespace", "principal_type", "principal_ref", "command_scope", "idempotency_key"),
746
914
  ADD CONSTRAINT "ck_sys_command_replay_records_namespace"
747
915
  CHECK ("namespace" ~ '^[a-z][a-z0-9_-]*([.][a-z][a-z0-9_-]*){2,7}$'),
748
916
  ADD CONSTRAINT "ck_sys_command_replay_records_scope"
@@ -753,16 +921,8 @@ ADD CONSTRAINT "uk_sys_command_replay_records_namespace_scope_key"
753
921
  CHECK ("status" IN ('started', 'succeeded', 'failed')),
754
922
  ADD CONSTRAINT "ck_sys_command_replay_records_principal"
755
923
  CHECK (
756
- (
757
- "principal_type" IS NULL
758
- AND "principal_ref" IS NULL
759
- )
760
- OR (
761
- "principal_type" IS NOT NULL
762
- AND BTRIM("principal_type") <> ''
763
- AND "principal_ref" IS NOT NULL
764
- AND BTRIM("principal_ref") <> ''
765
- )
924
+ BTRIM("principal_type") <> ''
925
+ AND BTRIM("principal_ref") <> ''
766
926
  ),
767
927
  ADD CONSTRAINT "ck_sys_command_replay_records_error_state"
768
928
  CHECK (
@@ -851,9 +1011,9 @@ COMMENT ON COLUMN "sys_command_replay_records"."idempotency_key" IS '调用方
851
1011
 
852
1012
  COMMENT ON COLUMN "sys_command_replay_records"."request_hash" IS '规范请求载荷 SHA-256 摘要';
853
1013
 
854
- COMMENT ON COLUMN "sys_command_replay_records"."principal_type" IS '调用主体类型';
1014
+ COMMENT ON COLUMN "sys_command_replay_records"."principal_type" IS '参与幂等作用域的可信调用主体类型';
855
1015
 
856
- COMMENT ON COLUMN "sys_command_replay_records"."principal_ref" IS '调用主体公开引用';
1016
+ COMMENT ON COLUMN "sys_command_replay_records"."principal_ref" IS '参与幂等作用域的可信调用主体公开引用';
857
1017
 
858
1018
  COMMENT ON COLUMN "sys_command_replay_records"."status" IS '命令执行状态';
859
1019
 
@@ -8,22 +8,125 @@ BEGIN
8
8
  WHERE table_schema = 'public' AND table_name IN (
9
9
  'sys_access_logs', 'sys_login_logs', 'sys_audit_logs', 'sys_operation_logs',
10
10
  'sys_audit_event_inbox', 'sys_outbox_events', 'sys_outbox_event_deliveries',
11
- 'sys_command_replay_records'
11
+ 'sys_command_replay_records', 'sys_integration_event_inbox',
12
+ 'sys_integration_event_receipts', 'sys_integration_aggregate_checkpoints'
12
13
  );
13
- IF table_count <> 8 THEN
14
+ IF table_count <> 11 THEN
14
15
  RAISE EXCEPTION 'infrastructure tables are incomplete';
15
16
  END IF;
16
17
 
18
+ INSERT INTO sys_integration_event_inbox (
19
+ source_system, event_id, event_type, schema_version, product_code,
20
+ aggregate_type, aggregate_ref, aggregate_version, payload_hash, event_payload
21
+ ) VALUES (
22
+ 'source-a', 'event-1', 'resource.created', '1.0.0', 'product_a',
23
+ 'resource', 'resource-1', 1, repeat('a', 64), '{"id":"event-1"}'
24
+ );
25
+ INSERT INTO sys_integration_event_inbox (
26
+ source_system, event_id, event_type, schema_version, product_code,
27
+ aggregate_type, aggregate_ref, aggregate_version, payload_hash, event_payload
28
+ ) VALUES (
29
+ 'source-b', 'event-1', 'resource.created', '1.0.0', 'product_a',
30
+ 'resource', 'resource-1', 1, repeat('a', 64), '{"id":"event-1"}'
31
+ );
32
+ BEGIN
33
+ INSERT INTO sys_integration_event_inbox (
34
+ source_system, event_id, event_type, schema_version, product_code,
35
+ aggregate_type, aggregate_ref, aggregate_version, payload_hash, event_payload
36
+ ) VALUES (
37
+ 'source-a', 'event-1', 'resource.created', '1.0.0', 'product_a',
38
+ 'resource', 'resource-1', 1, repeat('b', 64), '{"id":"event-1"}'
39
+ );
40
+ RAISE EXCEPTION 'duplicate global integration event identity was accepted';
41
+ EXCEPTION WHEN unique_violation THEN NULL;
42
+ END;
43
+ BEGIN
44
+ UPDATE sys_integration_event_inbox SET event_type = 'resource.changed'
45
+ WHERE source_system = 'source-a' AND event_id = 'event-1';
46
+ RAISE EXCEPTION 'integration event inbox mutation was accepted';
47
+ EXCEPTION WHEN raise_exception THEN
48
+ IF SQLERRM <> 'sys_integration_event_inbox is append-only' THEN RAISE; END IF;
49
+ END;
50
+
51
+ INSERT INTO sys_integration_event_receipts (source_system, subscriber_code, event_id)
52
+ VALUES ('source-a', 'subscriber-a', 'event-1');
53
+ INSERT INTO sys_integration_event_receipts (source_system, subscriber_code, event_id)
54
+ VALUES ('source-a', 'subscriber-b', 'event-1');
55
+ BEGIN
56
+ INSERT INTO sys_integration_event_receipts (source_system, subscriber_code, event_id)
57
+ VALUES ('source-a', 'subscriber-a', 'event-1');
58
+ RAISE EXCEPTION 'duplicate subscriber integration event receipt was accepted';
59
+ EXCEPTION WHEN unique_violation THEN NULL;
60
+ END;
61
+ UPDATE sys_integration_event_receipts
62
+ SET status = 'processed', receipt_status = 'processed', processed_at = now(), updated_at = now()
63
+ WHERE source_system = 'source-a' AND subscriber_code = 'subscriber-a' AND event_id = 'event-1';
64
+ BEGIN
65
+ UPDATE sys_integration_event_receipts SET status = 'processing', receipt_status = NULL, processed_at = NULL
66
+ WHERE source_system = 'source-a' AND subscriber_code = 'subscriber-a' AND event_id = 'event-1';
67
+ RAISE EXCEPTION 'terminal integration event receipt regressed';
68
+ EXCEPTION WHEN raise_exception THEN
69
+ IF SQLERRM <> 'sys_integration_event_receipts only allow processing to terminal transition' THEN RAISE; END IF;
70
+ END;
71
+
72
+ INSERT INTO sys_integration_aggregate_checkpoints (
73
+ source_system, subscriber_code, aggregate_type, aggregate_ref,
74
+ last_aggregate_version, last_event_id, last_payload_hash
75
+ ) VALUES ('source-a', 'subscriber-a', 'resource', 'resource-1', 1, 'event-1', repeat('a', 64));
76
+ INSERT INTO sys_integration_aggregate_checkpoints (
77
+ source_system, subscriber_code, aggregate_type, aggregate_ref,
78
+ last_aggregate_version, last_event_id, last_payload_hash
79
+ ) VALUES ('source-b', 'subscriber-a', 'resource', 'resource-1', 1, 'event-1', repeat('a', 64));
80
+ BEGIN
81
+ INSERT INTO sys_integration_aggregate_checkpoints (
82
+ source_system, subscriber_code, aggregate_type, aggregate_ref,
83
+ last_aggregate_version, last_event_id, last_payload_hash
84
+ ) VALUES ('source-a', 'subscriber-a', 'resource', 'resource-1', 1, 'event-1', repeat('a', 64));
85
+ RAISE EXCEPTION 'duplicate aggregate checkpoint scope was accepted';
86
+ EXCEPTION WHEN unique_violation THEN NULL;
87
+ END;
88
+ UPDATE sys_integration_aggregate_checkpoints
89
+ SET last_aggregate_version = 2, last_event_id = 'event-2', last_payload_hash = repeat('b', 64),
90
+ revision_no = 2, updated_at = now()
91
+ WHERE source_system = 'source-a' AND subscriber_code = 'subscriber-a'
92
+ AND aggregate_type = 'resource' AND aggregate_ref = 'resource-1';
93
+ BEGIN
94
+ UPDATE sys_integration_aggregate_checkpoints
95
+ SET last_aggregate_version = 4, revision_no = 3, updated_at = now()
96
+ WHERE source_system = 'source-a' AND subscriber_code = 'subscriber-a'
97
+ AND aggregate_type = 'resource' AND aggregate_ref = 'resource-1';
98
+ RAISE EXCEPTION 'aggregate checkpoint gap was accepted';
99
+ EXCEPTION WHEN raise_exception THEN
100
+ IF SQLERRM <> 'sys_integration_aggregate_checkpoints must advance exactly one version' THEN RAISE; END IF;
101
+ END;
102
+
17
103
  INSERT INTO sys_command_replay_records (
18
- namespace, command_scope, idempotency_key, request_hash, expires_at
19
- ) VALUES ('company.product.action', 'actor', 'request-1', repeat('a', 64), now() + interval '1 hour');
104
+ namespace, principal_type, principal_ref, command_scope, idempotency_key, request_hash, expires_at
105
+ ) VALUES ('company.product.action', 'identity', 'user_123', 'actor', 'request-1', repeat('a', 64), now() + interval '1 hour');
20
106
  BEGIN
21
107
  INSERT INTO sys_command_replay_records (
22
- namespace, command_scope, idempotency_key, request_hash, expires_at
23
- ) VALUES ('company.product.action', 'actor', 'request-1', repeat('a', 64), now() + interval '1 hour');
108
+ namespace, principal_type, principal_ref, command_scope, idempotency_key, request_hash, expires_at
109
+ ) VALUES ('company.product.action', 'identity', 'user_123', 'actor', 'request-1', repeat('a', 64), now() + interval '1 hour');
24
110
  RAISE EXCEPTION 'duplicate command was accepted';
25
111
  EXCEPTION WHEN unique_violation THEN NULL;
26
112
  END;
113
+ INSERT INTO sys_command_replay_records (
114
+ namespace, principal_type, principal_ref, command_scope, idempotency_key, request_hash, expires_at
115
+ ) VALUES ('company.product.action', 'identity', 'user_456', 'actor', 'request-1', repeat('b', 64), now() + interval '1 hour');
116
+ BEGIN
117
+ INSERT INTO sys_command_replay_records (
118
+ namespace, command_scope, idempotency_key, request_hash, expires_at
119
+ ) VALUES ('company.product.action', 'actor', 'request-2', repeat('c', 64), now() + interval '1 hour');
120
+ RAISE EXCEPTION 'command without principal was accepted';
121
+ EXCEPTION WHEN not_null_violation THEN NULL;
122
+ END;
123
+ BEGIN
124
+ INSERT INTO sys_command_replay_records (
125
+ namespace, principal_type, principal_ref, command_scope, idempotency_key, request_hash, expires_at
126
+ ) VALUES ('company.product.action', ' ', 'user_789', 'actor', 'request-3', repeat('d', 64), now() + interval '1 hour');
127
+ RAISE EXCEPTION 'command with blank principal was accepted';
128
+ EXCEPTION WHEN check_violation THEN NULL;
129
+ END;
27
130
  BEGIN
28
131
  UPDATE sys_command_replay_records SET status = 'failed'
29
132
  WHERE namespace = 'company.product.action';
@@ -98,7 +98,11 @@ Single page presentation? → page-private component
98
98
  Used by multiple pages? → existing shared app component or package
99
99
  ```
100
100
 
101
- File names follow installed conventions: Vue components use descriptive PascalCase, composables `useFeature.ts`, Nuxt pages/routes use filesystem route syntax, and server endpoints include method suffixes such as `.get.ts`. Avoid generic `helper`, `manager`, `new` or caller-branded names.
101
+ Directories, modules and packages are namespaces; child files and symbols keep only the minimum responsibility. Use `analytics/events.ts::EventInput`, `useTracking`, `GTMOptions` and `GA4Event`, never `types/marketing-analytics.ts` or a prefix swap such as `MarketingHeader` -> `PublicHeader`. External semantic identifiers such as `purchase`, `login` and `gateway_*` remain unchanged.
102
+
103
+ Directories, packages and ordinary TypeScript modules use kebab-case; Vue composables use `useX.ts`; Vue component files and types use PascalCase; variables/functions use camelCase; only fixed invariants use UPPER_SNAKE_CASE. Nuxt routes use filesystem syntax and server endpoints keep method suffixes such as `.get.ts`. Vague standalone or catch-all names such as `helper`, `utils`, `common` and `manager` are forbidden; choose a precise owner and responsibility. `temp`, `new` and `final` naming residue is forbidden.
104
+
105
+ Nuxt component directories remain namespaces. `components/site/Header.vue` is `Header` through a local explicit import and `SiteHeader` across owners or through global auto-import. Preserve default path-aware auto-naming; global `pathPrefix: false` is forbidden. Search existing owners, siblings and usages before naming or renaming, update all references, and delete superseded names together.
102
106
 
103
107
  ## IV. Forbidden Patterns
104
108
 
@@ -0,0 +1,19 @@
1
+ .git
2
+ .github
3
+ .claude
4
+ .husky
5
+ .nuxt
6
+ node_modules
7
+ coverage
8
+ test
9
+ playwright-report
10
+
11
+ .env
12
+ .env.*
13
+ .npmrc
14
+
15
+ *.log
16
+ .DS_Store
17
+
18
+ deploy.config.json
19
+ README.md
@@ -0,0 +1,27 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "name": "{{projectName}}",
4
+ "profile": "nuxt-node",
5
+ "source": {
6
+ "verify": {
7
+ "executable": "pnpm",
8
+ "args": ["check"]
9
+ },
10
+ "build": {
11
+ "executable": "pnpm",
12
+ "args": ["build"]
13
+ },
14
+ "output": ".output"
15
+ },
16
+ "image": {
17
+ "dockerfile": "Dockerfile",
18
+ "context": ".",
19
+ "repository": "{{projectName}}",
20
+ "platform": "linux/amd64"
21
+ },
22
+ "environments": {
23
+ "production": {
24
+ "target": "registry-only"
25
+ }
26
+ }
27
+ }
@@ -28,10 +28,10 @@
28
28
  "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm build"
29
29
  },
30
30
  "dependencies": {
31
- "@stackonward/api-client": "^0.0.2",
32
- "@stackonward/google-tag-manager-nuxt": "^0.3.0",
33
- "@stackonward/nuxt-layer-base": "^0.0.3",
34
- "@stackonward/nuxt-site-shell": "^0.3.0"
31
+ "@stackonward/api-client": "^0.0.3",
32
+ "@stackonward/google-tag-manager-nuxt": "^0.3.2",
33
+ "@stackonward/nuxt-layer-base": "^0.0.4",
34
+ "@stackonward/nuxt-site-shell": "^0.3.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@commitlint/cli": "^21.2.2",
@@ -45,10 +45,10 @@
45
45
  "@nuxtjs/seo": "^5.3.2",
46
46
  "@pinia/colada-nuxt": "^1.0.1",
47
47
  "@pinia/nuxt": "^0.11.3",
48
- "@stackonward/commitlint-config": "^0.1.0",
49
- "@stackonward/eslint-config": "^1.0.0",
50
- "@stackonward/prettier-config": "^0.0.2",
51
- "@stackonward/stylelint-config": "^0.1.0",
48
+ "@stackonward/commitlint-config": "^0.1.1",
49
+ "@stackonward/eslint-config": "^1.0.1",
50
+ "@stackonward/prettier-config": "^0.0.3",
51
+ "@stackonward/stylelint-config": "^0.1.1",
52
52
  "@types/node": "^22.0.0",
53
53
  "@vueuse/nuxt": "^14.3.0",
54
54
  "eslint": "^10.10.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackonward/cli",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "StackOnward CLI for creating projects and installing reusable components and snippets",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,8 +36,10 @@
36
36
  "ora": "^8.0.0",
37
37
  "prompts": "^2.4.2",
38
38
  "zod": "4",
39
- "@stackonward/api-client": "0.0.2",
40
- "@stackonward/google-tag-manager": "0.2.0"
39
+ "@stackonward/api-client": "0.0.3",
40
+ "@stackonward/google-tag-manager": "0.2.2",
41
+ "@stackonward/onedev-provisioning": "0.1.0",
42
+ "@stackonward/provisioning": "0.1.0"
41
43
  },
42
44
  "devDependencies": {
43
45
  "@types/fs-extra": "^11.0.4",