Explorar o código

SVI Массовое новое внедрение, линтеры

SVI hai 2 meses
pai
achega
949e909c78
Modificáronse 100 ficheiros con 1291 adicións e 223 borrados
  1. 1 0
      .gitignore
  2. 34 0
      v4/.golangci.yml
  3. 13 0
      v4/.semgrep.yml
  4. 6 1
      v4/Taskfile.yml
  5. 2 2
      v4/lev0/helpers/helpers.go
  6. 27 0
      v4/lev0/helpers/helpers.md
  7. 0 0
      v4/lev0/helpers/helpers_test.go
  8. 6 0
      v4/lev0/proton/neutron.go
  9. 74 0
      v4/lev0/quark/contract/contract.go
  10. 68 0
      v4/lev0/quark/quark_base/quark_base.go
  11. 57 0
      v4/lev0/quark/quark_base/quark_base_test.go
  12. 42 0
      v4/lev0/quark/quark_bool/quark_bool.go
  13. 71 0
      v4/lev0/quark/quark_bool/quark_bool_test.go
  14. 70 0
      v4/lev0/quark/quark_byte/quark_byte.go
  15. 105 0
      v4/lev0/quark/quark_byte/quark_byte_test.go
  16. 58 0
      v4/lev0/quark/quark_float/quark_float.go
  17. 44 0
      v4/lev0/quark/quark_float/quark_float_test.go
  18. 53 0
      v4/lev0/quark/quark_int/quark_int.go
  19. 41 0
      v4/lev0/quark/quark_int/quark_int_test.go
  20. 42 0
      v4/lev0/quark/quark_str/quark_str.go
  21. 34 0
      v4/lev0/quark/quark_str/quark_str_test.go
  22. 44 0
      v4/lev0/quark/quark_val/quark_val.go
  23. 48 0
      v4/lev0/quark/quark_val/quark_val_test.go
  24. 1 1
      v4/lev1/ctx_value/ctx_value.go
  25. 1 1
      v4/lev1/kbus_msg/msg_pub/msg_pub.go
  26. 1 1
      v4/lev1/kbus_msg/msg_serve/msg_serve.go
  27. 1 1
      v4/lev1/kbus_msg/msg_sub/msg_sub.go
  28. 1 1
      v4/lev1/kbus_msg/msg_unsub/msg_unsub.go
  29. 1 1
      v4/lev1/lev1.go
  30. 79 0
      v4/lev1/lev1_test.go
  31. 1 3
      v4/lev1/local_ctx/local_ctx.go
  32. 1 1
      v4/lev1/log_buf/log_buf.go
  33. 1 1
      v4/lev1/log_buf/log_buf_test.go
  34. 1 1
      v4/lev1/log_msg/log_msg.go
  35. 1 1
      v4/lev1/lst_sort/lst_sort.go
  36. 1 1
      v4/lev1/mock_env/mock_env.go
  37. 4 4
      v4/lev1/mock_env/mock_env_test.go
  38. 1 1
      v4/lev1/option/option.go
  39. 1 1
      v4/lev1/result/result.go
  40. 1 1
      v4/lev2/kbus_http/client_bus_http/client_bus_http.go
  41. 3 3
      v4/lev2/kmodule/kmodule.go
  42. 1 1
      v4/lev2/kmodule/mod_stat/mod_stat.go
  43. 2 0
      v4/lev2/kmodule/mod_stat/mod_stat_day/mod_stat_day.go
  44. 1 1
      v4/lev2/kmonolit/kmonolit.go
  45. 1 1
      v4/lev2/kmonolit/kmonolit_test.go
  46. 1 1
      v4/lev2/kserv_http/kserv_http.go
  47. 1 1
      v4/lev2/kserv_http/kserv_http_test.go
  48. 9 0
      v4/lev2/kserv_http_std/kserv_http_std_test.go
  49. 1 1
      v4/lev2/kstore_kv/kstore_kv.go
  50. 7 0
      v4/lev2/kstore_kv_std/kstore_kv_std_test.go
  51. 1 1
      v4/lev2/kstore_kv_std/kv_bucket_base/kv_bucket_base.go
  52. 7 0
      v4/lev2/kstore_kv_std/kv_bucket_base/kv_bucket_base_test.go
  53. 7 0
      v4/lev2/kstore_kv_std/kv_bucket_ram/kv_bucket_ram_test.go
  54. 1 1
      v4/lev2/kstore_kv_std/rec_kv/rec_kv.go
  55. 1 1
      v4/lev2/kstore_kv_std/rec_meta/rec_meta.go
  56. 9 9
      v4/lev2/kstore_kv_std/rec_meta/rec_meta_test.go
  57. 6 6
      v4/lev2/kstore_kv_std/rec_val/rec_val.go
  58. 13 13
      v4/lev2/kstore_kv_std/rec_val/rec_val_test.go
  59. 12 12
      v4/lev2/kwg/kwg.go
  60. 8 8
      v4/lev2/kwg/kwg_test.go
  61. 10 0
      v4/lev2/lev2_test.go
  62. 4 4
      v4/lev2/log_topic/log_topic.go
  63. 3 3
      v4/lev2/log_topic/log_topic_test.go
  64. 1 1
      v4/lev2/mock_hand_serve/mock_hand_serve.go
  65. 4 4
      v4/lev2/mock_hand_serve/mock_hand_serve_test.go
  66. 9 12
      v4/lev2/mock_hand_sub_http/mock_hand_sub_http.go
  67. 6 6
      v4/lev2/mock_hand_sub_http/mock_hand_sub_http_test.go
  68. 1 1
      v4/lev2/mock_hand_sub_local/mock_hand_sub.go
  69. 6 6
      v4/lev2/wui/hx_swap/hx_swap.go
  70. 6 6
      v4/lev2/wui/hx_swap_oob/hx_swap_oob.go
  71. 9 6
      v4/lev2/wui/hx_target/hx_target.go
  72. 4 0
      v4/lev2/wui/hx_target/hx_target_test.go
  73. 9 6
      v4/lev2/wui/hx_trigger/hx_trigger.go
  74. 5 0
      v4/lev2/wui/hx_trigger/hx_trigger_test.go
  75. 6 6
      v4/lev2/wui/hx_url/hx_url.go
  76. 5 5
      v4/lev2/wui/hx_url_method/hx_url_method.go
  77. 5 5
      v4/lev2/wui/hx_url_patch/hx_url_patch.go
  78. 10 10
      v4/lev2/wui/hx_vals/hx_vals.go
  79. 7 7
      v4/lev2/wui/wbutton/wbutton.go
  80. 2 2
      v4/lev2/wui/wbutton/wbutton_test.go
  81. 2 2
      v4/lev2/wui/wctx/wctx.go
  82. 10 10
      v4/lev2/wui/whx/whx.go
  83. 2 2
      v4/lev2/wui/whx/whx_test.go
  84. 5 5
      v4/lev2/wui/wlabel/wlabel.go
  85. 1 1
      v4/lev2/wui/wlabel/wlabel_test.go
  86. 6 6
      v4/lev2/wui/wtext/wtext.go
  87. 3 3
      v4/lev2/wui/wtext/wtext_test.go
  88. 1 1
      v4/lev2/wui/wtypes/ihx_swap.go
  89. 1 1
      v4/lev2/wui/wtypes/ihx_swap_oob.go
  90. 1 1
      v4/lev2/wui/wtypes/ihx_target.go
  91. 1 1
      v4/lev2/wui/wtypes/ihx_trigger.go
  92. 1 1
      v4/lev2/wui/wtypes/ihx_url.go
  93. 1 1
      v4/lev2/wui/wtypes/iwui_ctx.go
  94. 1 1
      v4/lev2/wui/wtypes/iwui_hx.go
  95. 1 1
      v4/lev2/wui/wtypes/iwui_label.go
  96. 1 1
      v4/lev2/wui/wtypes/iwui_text.go
  97. 1 1
      v4/lev2/wui/wtypes/iwui_widget.go
  98. 3 3
      v4/lev2/wui/wui.go
  99. 2 2
      v4/lev2/wui/wui_test.go
  100. 5 5
      v4/lev2/wui/wwidget/wui_widget.go

+ 1 - 0
.gitignore

@@ -19,3 +19,4 @@ go.work
 bin_dev
 .token
 .vscode
+.aider*

+ 34 - 0
v4/.golangci.yml

@@ -0,0 +1,34 @@
+version: "2"
+linters:
+  enable:
+   - asasalint
+   - bodyclose
+   - contextcheck
+   - cyclop
+   - dupl
+   - forbidigo
+   - funlen
+   - gocheckcompilerdirectives
+   - gocognit
+   - goconst
+   - gocritic
+   - gocyclo
+   - godot
+   - godox
+   - lll
+   - maintidx
+   # - mnd
+   - nestif
+   - nilerr
+   - nonamedreturns
+   - prealloc
+   - rowserrcheck
+   - staticcheck
+   - unconvert
+   - unparam
+   - usestdlibvars
+   - wrapcheck
+
+issues:
+  max-issues-per-linter: 0
+  max-same-issues: 0

+ 13 - 0
v4/.semgrep.yml

@@ -0,0 +1,13 @@
+rules:
+  - id: ban-raw-primitive-types
+    languages: [go]
+    message: "Алярм! Использование встроенных типов запрещено. Используйте контрактные обертки из lev0/contract."
+    severity: ERROR
+    patterns:
+      - pattern: func $F(..., $ARG $TYPE, ...) { ... }
+      - metavariable-regex:
+          metavariable: $TYPE
+          regex: ^(bool|byte|uint8|int|float64|string)$ # Список запрещённых примитивов
+    paths:
+      include: ["cmd/**", "lev0/**", "lev1/**", "lev2/**", "lev3/**", "memory/**"] # Применяем только к бизнес-логике
+      exclude: ["vendor/**", "doc/**"]    # Исключаем сами обертки

+ 6 - 1
v4/Taskfile.yml

@@ -18,6 +18,7 @@ tasks:
     cmds:
       - clear
       - go fmt ./...
+      - semgrep scan --config .semgrep.yml
       - go build -race -o ./bin_dev/demo ./cmd/demo/main.go
   mod:
     desc: "Обновление зависимостей"
@@ -32,6 +33,7 @@ tasks:
     cmds:
       - clear
       - go fmt ./...
+      - semgrep scan --config .semgrep.yml
       - go test -race -shuffle=on -timeout=30s -coverprofile=./cover.out ./...
       - go tool cover -func=./cover.out
   lint:
@@ -44,12 +46,15 @@ tasks:
       # - staticcheck ./...
       # - go install github.com/MakeNowJust/enumcase/cmd/enumcase@latest
       # - enumcase ./...
-      - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+      # - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+      - go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2
       - golangci-lint run ./...
       # - go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
       # - gocyclo -over 11 .
       # - go install github.com/securego/gosec/cmd/gosec@latest
       # - gosec ./...
+      - pipx install semgrep
+      - semgrep scan --config .semgrep.yml
   fmt:
     desc: "Форматирование кода"
     cmds:

+ 2 - 2
v4/lev1/helpers/helpers.go → v4/lev0/helpers/helpers.go

@@ -53,13 +53,13 @@ func TimeNowStr() ATime {
 	return ATime(strTime)
 }
 
-// TimeNow -- возвращает Unix сейчас-время (мсек, не зависит от положения)
+// TimeNow -- возвращает локальное Unix сейчас-время (мсек, не зависит от положения)
 func TimeNow() int64 {
 	timeNow := time.Now().Local().UnixMilli()
 	return timeNow
 }
 
-// SleepMs -- спит миллисекунду
+// SleepMs -- спит миллисекунду (иногда бывает полезно)
 func SleepMs() {
 	time.Sleep(time.Millisecond * 1)
 }

+ 27 - 0
v4/lev0/helpers/helpers.md

@@ -0,0 +1,27 @@
+# helpers -- вспомогательные функции
+
+Набор вспомогательных функций для облегчения жизни.
+
+## type FnAssert, FnHassert
+
+Вспомогательные сигнатуры. Сделаны специально для облегчения экспорта
+
+## func Assert
+
+Проверка на правильность утверждения с падением в панику на локальном стенде (soft assert).
+
+## func Hassert
+
+Hassert -- проверка на правильность утверждения с безусловным падением в панику (hard assert)
+
+## func TimeNowStr
+
+TimeNow -- возвращает локальное Unix сейчас-время (мсек, не зависит от положения)
+
+## func SleepMS
+
+SleepMs -- спит миллисекунду (иногда бывает полезно)
+
+## env STAGE
+
+Специальная переменная окружения. `local`, `prod`, `пустое значение` -- влияет на то, как будут реагировать `Assert`, `Hassert`

+ 0 - 0
v4/lev1/helpers/helpers_test.go → v4/lev0/helpers/helpers_test.go


+ 6 - 0
v4/lev0/proton/neutron.go

@@ -0,0 +1,6 @@
+// package proton -- протон на основе кварка
+package proton
+
+// Proton -- протон на основе кварка
+type Proton struct {
+}

+ 74 - 0
v4/lev0/quark/contract/contract.go

@@ -0,0 +1,74 @@
+// package contract -- контракт каждого протона и нейтрона
+package contract
+
+import (
+	"os"
+
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
+)
+
+// isContractDisabled -- признак включённости контрактов
+// По умолчанию определяется переменной окружения IS_CONTRACT (любое непустое значение включает контракты).
+var isContractDisabled = os.Getenv("IS_CONTRACT") == ""
+
+// stage -- значение переменной окружения STAGE
+// Если STAGE != "prod", контракты всегда включены.
+var stage = os.Getenv("STAGE")
+
+// Contract -- контракт каждого протона и нейтрона
+// T -- тип контекста, передаваемого в пред-, ин- и постусловия
+type Contract struct {
+	// fnPre -- предусловие: должно быть истинно перед выполнением операции
+	fnPre func()
+	// fnPost -- постусловие: должно быть истинно после выполнения операции
+	fnPost func()
+}
+
+// ContractOpt -- опция для настройки контракта
+type ContractOpt func(*Contract)
+
+// ContractOptPre -- задать предусловие
+func ContractOptPre(fn func()) ContractOpt {
+	return func(sf *Contract) {
+		sf.fnPre = fn
+	}
+}
+
+// ContractOptPost -- задать постусловие
+func ContractOptPost(fn func()) ContractOpt {
+	return func(sf *Contract) {
+		sf.fnPost = fn
+	}
+}
+
+// NewContract -- создать новый контракт с опциями
+func NewContract(opts ...ContractOpt) *Contract {
+	sf := &Contract{}
+	for _, opt := range opts {
+		opt(sf)
+	}
+	return sf
+}
+
+// Wrap -- оборачивает произвольную функцию выполнением контракта
+// Схема вызова: Pre(ctx) -> fn(ctx) -> Post(ctx).
+// Если соответствующее условие не задано, оно пропускается.
+func (sf *Contract) Wrap(fn func()) func() {
+	Hassert(fn != nil, "Contract[T].Wrap(): fn==nil")
+	// В непроизводственных окружениях (STAGE != "prod") всегда оборачиваем
+	if stage == "prod" && isContractDisabled {
+		// В prod оборачиваем только если явно включены контракты
+		return fn
+	}
+	return func() {
+		if sf.fnPre != nil {
+			sf.fnPre()
+		}
+
+		fn()
+
+		if sf.fnPost != nil {
+			sf.fnPost()
+		}
+	}
+}

+ 68 - 0
v4/lev0/quark/quark_base/quark_base.go

@@ -0,0 +1,68 @@
+// package quark_base -- базовый кварк
+package quark_base
+
+import (
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_val"
+)
+
+// QBase -- базовый кварк
+type QBase[M quark_val.CMod, T any] struct {
+	mass int                    // Масса кварка
+	val  *quark_val.Field[M, T] // хранимое значение
+}
+
+// QBaseOpt -- опции для базового кварка
+type QBaseOpt[M quark_val.CMod, T any] func(qBase *QBase[M, T])
+
+// QBaseOptMass -- опция для массы кварка
+func QBaseOptMass[M quark_val.CMod, T any](mass int) QBaseOpt[M, T] {
+	return func(sf *QBase[M, T]) {
+		sf.mass = mass
+	}
+}
+
+// QBaseOptVal -- опция для значения кварка
+func QBaseOptVal[M quark_val.Fix, T any](val T) QBaseOpt[quark_val.Fix, T] {
+	return func(qBase *QBase[quark_val.Fix, T]) {
+		qBase.val = quark_val.NewField(val)
+	}
+}
+
+// QBaseOptValMut -- опция для изменяемого значения кварка
+func QBaseOptValMut[M quark_val.Mut, T any](val T) QBaseOpt[quark_val.Mut, T] {
+	return func(sf *QBase[quark_val.Mut, T]) {
+		sf.val = quark_val.NewFieldMut(val)
+	}
+}
+
+// NewQBase -- создать новый фиксированный базовый кварк
+func NewQBase[M quark_val.Fix, T any](opts ...QBaseOpt[M, T]) *QBase[M, T] {
+	sf := &QBase[M, T]{}
+	for _, opt := range opts {
+		opt(sf)
+	}
+	Hassert(sf.mass > 0, "NewQBase(): нулевая масса запрещена!")
+	Hassert(sf.val != nil, "NewQBase(): значение не создано")
+	return sf
+}
+
+// NewQBaseMut -- создать новый мутабельный базовый кварк
+func NewQBaseMut[M quark_val.Mut, T any](opts ...QBaseOpt[M, T]) *QBase[M, T] {
+	sf := &QBase[M, T]{}
+	for _, opt := range opts {
+		opt(sf)
+	}
+	Hassert(sf.mass > 0, "NewQBase(): нулевая масса запрещена!")
+	return sf
+}
+
+// Mass -- получить массу кварка
+func (sf *QBase[M, T]) Mass() int {
+	return sf.mass
+}
+
+// Val -- возвращает хранимое значение
+func (sf *QBase[M, T]) Val() *quark_val.Field[M, T] {
+	return sf.val
+}

+ 57 - 0
v4/lev0/quark/quark_base/quark_base_test.go

@@ -0,0 +1,57 @@
+package quark_base
+
+import (
+	"testing"
+
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_val"
+)
+
+type tester struct {
+	t *testing.T
+}
+
+func TestQuarkBase(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.new()
+	sf.newMut()
+	sf.newMass()
+}
+
+// Создаёт новый базовый кварк
+func (sf *tester) new() {
+	sf.t.Log("new")
+	optMass := QBaseOptMass[quark_val.Fix, int](1)
+	optVal := QBaseOptVal(5)
+	qb := NewQBase(optMass, optVal)
+	if qb.Mass() <= 0 {
+		sf.t.Fatalf("new(): некорректная масса, получили %d, ожидали > 0", qb.Mass())
+	}
+}
+
+// Создаёт мутировавший базовый кварк
+func (sf *tester) newMut() {
+	sf.t.Log("newMut")
+	optMass := QBaseOptMass[quark_val.Mut, string](10)
+	optVal := QBaseOptValMut("test")
+	qb := NewQBaseMut(optMass, optVal)
+	if qb.Mass() != 10 {
+		sf.t.Fatalf("newMut(): некорректная масса, получили %d, ожидали %d", qb.Mass(), 10)
+	}
+	quark_val.Set(qb.Val(), "test_new")
+}
+
+// Создаёт базовый кварк с заданной массой
+func (sf *tester) newMass() {
+	sf.t.Log("newMass")
+	const wantMass = 42
+	optMass := QBaseOptMass[quark_val.Fix, int](wantMass)
+	optVal := QBaseOptVal(22)
+	qb := NewQBase(optMass, optVal)
+	if qb.Mass() != wantMass {
+		sf.t.Fatalf("newMass(): некорректная масса, получили %d, ожидали %d", qb.Mass(), wantMass)
+	}
+	// Это не пройдёт -- кварк фиксированный
+	// quark_val.Set(qb.Val(), 20)
+}

+ 42 - 0
v4/lev0/quark/quark_bool/quark_bool.go

@@ -0,0 +1,42 @@
+// package quark_bool -- кварк булево
+package quark_bool
+
+import (
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_base"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_val"
+)
+
+// QBool -- кварк булево (типизированный модификатором базового кварка)
+type QBool[M quark_val.CMod] struct {
+	*quark_base.QBase[M, bool] // базовый кварк
+}
+
+// NewQBool -- создать фиксированный кварк булево
+func NewQBool(val bool) *QBool[quark_val.Fix] {
+	optMass := quark_base.QBaseOptMass[quark_val.Fix, bool](1)
+	optVal := quark_base.QBaseOptVal(val)
+	sf := &QBool[quark_val.Fix]{
+		QBase: quark_base.NewQBase(optMass, optVal),
+	}
+	return sf
+}
+
+// NewQBoolMut -- создать мутируемый кварк булево
+func NewQBoolMut(val bool) *QBool[quark_val.Mut] {
+	optMass := quark_base.QBaseOptMass[quark_val.Mut, bool](1)
+	optVal := quark_base.QBaseOptValMut(val)
+	sf := &QBool[quark_val.Mut]{
+		QBase: quark_base.NewQBaseMut(optMass, optVal),
+	}
+	return sf
+}
+
+// Get -- Возвращает хранимое значение
+func (sf *QBool[M]) Get() bool {
+	return sf.Val().Get()
+}
+
+// Set -- устанавливает хранимое значение
+func Set(sf *QBool[quark_val.Mut], val bool) {
+	quark_val.Set(sf.Val(), val)
+}

+ 71 - 0
v4/lev0/quark/quark_bool/quark_bool_test.go

@@ -0,0 +1,71 @@
+package quark_bool
+
+import (
+	"testing"
+)
+
+type tester struct {
+	t *testing.T
+}
+
+func TestQBool(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.newTrueMut()
+	sf.newFalseNotMut()
+	sf.setValue()
+}
+
+// Создаёт булевый кварк со значением true и мутацией
+func (sf *tester) newTrueMut() {
+	sf.t.Log("создание булевого кварка: true, мутировавший")
+	qb := NewQBoolMut(true)
+	if qb == nil {
+		sf.t.Fatalf("newTrueMut(): кварк не создан (nil)")
+	}
+	if !qb.Get() {
+		sf.t.Fatalf("newTrueMut(): некорректное значение, получили %v, ожидали %v", qb.Get(), true)
+	}
+	if qb.Mass() != 1 {
+		sf.t.Fatalf("newTrueMut(): некорректная масса, получили %d, ожидали %d", qb.Mass(), 1)
+	}
+}
+
+// Создаёт булевый кварк со значением false без мутации
+func (sf *tester) newFalseNotMut() {
+	sf.t.Log("создание булевого кварка: false, без мутации")
+	qb := NewQBool(false)
+	if qb == nil {
+		sf.t.Fatalf("newFalseNotMut(): кварк не создан (nil)")
+	}
+	if qb.Get() {
+		sf.t.Fatalf("newFalseNotMut(): некорректное значение, получили %v, ожидали %v", qb.Get(), false)
+	}
+	if qb.Mass() != 1 {
+		sf.t.Fatalf("newFalseNotMut(): некорректная масса, получили %d, ожидали %d", qb.Mass(), 1)
+	}
+}
+
+// Проверяет установку значения через Set
+func (sf *tester) setValue() {
+	sf.t.Log("setValue")
+	// Для изменения значения кварк должен быть мутируемым (isMut = true)
+	qb := NewQBoolMut(false)
+	if qb == nil {
+		sf.t.Fatalf("setValue(): кварк не создан (nil)")
+	}
+	if qb.Get() {
+		sf.t.Fatalf("setValue(): начальное значение некорректно, ожидается false")
+	}
+
+	Set(qb, true)
+	if !qb.Get() {
+		sf.t.Fatalf("setValue(): значение после Set(true) некорректно, ожидается true")
+	}
+
+	Set(qb, false)
+	if qb.Get() {
+		sf.t.Fatalf("setValue(): значение после Set(false) некорректно, ожидается false")
+	}
+}

+ 70 - 0
v4/lev0/quark/quark_byte/quark_byte.go

@@ -0,0 +1,70 @@
+// package quark_byte -- кварк байт
+package quark_byte
+
+import (
+	"fmt"
+	"strconv"
+
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_base"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_val"
+)
+
+// QByte -- кварк байт
+type QByte[M quark_val.CMod] struct {
+	*quark_base.QBase[M, byte] // базовый кварк
+}
+
+// NewQByte -- создать фиксированный кварк байт
+func NewQByte(val byte) *QByte[quark_val.Fix] {
+	optMass := quark_base.QBaseOptMass[quark_val.Fix, byte](1)
+	optVal := quark_base.QBaseOptVal(val)
+	sf := &QByte[quark_val.Fix]{
+		QBase: quark_base.NewQBase(optMass, optVal),
+	}
+	return sf
+}
+
+// NewQByteMut -- создать мутируемый кварк байт
+func NewQByteMut(val byte) *QByte[quark_val.Mut] {
+	optMass := quark_base.QBaseOptMass[quark_val.Mut, byte](1)
+	optVal := quark_base.QBaseOptValMut(val)
+	sf := &QByte[quark_val.Mut]{
+		QBase: quark_base.NewQBaseMut(optMass, optVal),
+	}
+	return sf
+}
+
+// Get -- получить значение
+func (sf *QByte[T]) Get() byte {
+	return sf.Val().Get()
+}
+
+// Set -- установить значение
+func Set(sf *QByte[quark_val.Mut], val byte) {
+	quark_val.Set(sf.Val(), val)
+}
+
+// ToInt -- представить значение как int
+func (sf *QByte[T]) ToInt() int {
+	return int(sf.Val().Get())
+}
+
+// ToStr -- представить значение как строку из одного байта
+func (sf *QByte[T]) ToStr() string {
+	return fmt.Sprint(sf.Val().Get())
+}
+
+// FromInt -- установить значение из int
+func FromInt(sf *QByte[quark_val.Mut], v int) {
+	Hassert(v >= 0 && v <= 255, "QByte.FromInt(): значение вне диапазона байта")
+	quark_val.Set(sf.Val(), byte(v))
+}
+
+// FromStr -- установить значение из строки (как из целого числа)
+func FromStr(sf *QByte[quark_val.Mut], s string) {
+	// Конвертируем строку в int и используем FromInt для всех проверок
+	v, err := strconv.Atoi(s)
+	Hassert(err == nil, "QByte.FromStr(): не удалось преобразовать строку в int, err=\n\t%v", err)
+	FromInt(sf, v)
+}

+ 105 - 0
v4/lev0/quark/quark_byte/quark_byte_test.go

@@ -0,0 +1,105 @@
+package quark_byte
+
+import (
+	"testing"
+)
+
+type tester struct {
+	t *testing.T
+}
+
+func TestQByte(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.newMut()
+	sf.newFix()
+	sf.convertInt()
+	sf.convertStr()
+}
+
+// Создаёт мутируемый кварк байта и проверяет базовые свойства
+func (sf *tester) newMut() {
+	sf.t.Log("newMut")
+	const wantVal byte = 42
+
+	qb := NewQByteMut(wantVal)
+	if qb == nil {
+		sf.t.Fatalf("newMut(): кварк не создан (nil)")
+	}
+	if qb.Mass() != 1 {
+		sf.t.Fatalf("newMut(): некорректная масса, получили %d, ожидали %d", qb.Mass(), 1)
+	}
+	if qb.Get() != wantVal {
+		sf.t.Fatalf("newMut(): некорректное значение, получили %d, ожидали %d", qb.Get(), wantVal)
+	}
+
+	// Проверяем, что Set меняет значение для мутируемого кварка
+	const newVal byte = 7
+	Set(qb, newVal)
+	if qb.Get() != newVal {
+		sf.t.Fatalf("newMut(): значение после Set() некорректно, получили %d, ожидали %d", qb.Get(), newVal)
+	}
+}
+
+// Создаёт  кварк байта и проверяет базовые свойства (без изменения значения)
+func (sf *tester) newFix() {
+	sf.t.Log("newFix")
+	const wantVal byte = 10
+
+	qb := NewQByte(wantVal)
+	if qb == nil {
+		sf.t.Fatalf("newFix(): кварк не создан (nil)")
+	}
+	if qb.Mass() != 1 {
+		sf.t.Fatalf("newFix(): некорректная масса, получили %d, ожидали %d", qb.Mass(), 1)
+	}
+	if qb.Get() != wantVal {
+		sf.t.Fatalf("newFix(): некорректное значение, получили %d, ожидали %d", qb.Get(), wantVal)
+	}
+}
+
+// Проверяет преобразование в/из int
+func (sf *tester) convertInt() {
+	sf.t.Log("convertInt")
+
+	const srcVal byte = 100
+	qb := NewQByteMut(srcVal)
+	if qb == nil {
+		sf.t.Fatalf("convertInt(): кварк не создан (nil)")
+	}
+
+	if qb.ToInt() != int(srcVal) {
+		sf.t.Fatalf("convertInt(): ToInt() вернул %d, ожидали %d", qb.ToInt(), int(srcVal))
+	}
+
+	const newIntVal = 200
+	FromInt(qb, newIntVal)
+	if qb.Get() != byte(newIntVal) {
+		sf.t.Fatalf("convertInt(): FromInt() установил %d, ожидали %d", qb.Get(), byte(newIntVal))
+	}
+}
+
+// Проверяет преобразование в/из строки
+func (sf *tester) convertStr() {
+	sf.t.Log("проверка преобразования в/из строки")
+
+	const srcVal byte = 5
+	qb := NewQByteMut(srcVal)
+	if qb == nil {
+		sf.t.Fatalf("convertStr(): кварк не создан (nil)")
+	}
+
+	// ToStr должен возвращать строковое представление байта как числа
+	strVal := qb.ToStr()
+	if strVal != "5" {
+		sf.t.Fatalf("convertStr(): ToStr() вернул %q, ожидали 5", strVal)
+	}
+
+	// FromStr использует StrToInt, поэтому передаём числовую строку
+	const newIntVal = 13
+	FromStr(qb, "13")
+	if qb.Get() != byte(newIntVal) {
+		sf.t.Fatalf("convertStr(): FromStr() установил %d, ожидали %d", qb.Get(), byte(newIntVal))
+	}
+}

+ 58 - 0
v4/lev0/quark/quark_float/quark_float.go

@@ -0,0 +1,58 @@
+// package quark_float -- кварк вещественное
+package quark_float
+
+import (
+	"fmt"
+	"strconv"
+
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_base"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_val"
+)
+
+// QFloat -- кварк вещественное
+type QFloat[M quark_val.CMod] struct {
+	*quark_base.QBase[M, float64]
+}
+
+// NewQFloat -- создание фиксированного кварка вещественное
+func NewQFloat(val float64) *QFloat[quark_val.Fix] {
+	optMass := quark_base.QBaseOptMass[quark_val.Fix, float64](8)
+	optVal := quark_base.QBaseOptVal(val)
+	sf := &QFloat[quark_val.Fix]{
+		QBase: quark_base.NewQBase(optMass, optVal),
+	}
+	return sf
+}
+
+// NewQFloatMut -- создание мутабельного кварка вещественное
+func NewQFloatMut(val float64) *QFloat[quark_val.Mut] {
+	optMass := quark_base.QBaseOptMass[quark_val.Mut, float64](8)
+	optVal := quark_base.QBaseOptValMut(val)
+	sf := &QFloat[quark_val.Mut]{
+		QBase: quark_base.NewQBaseMut(optMass, optVal),
+	}
+	return sf
+}
+
+// Get -- возвращает хранимое значение
+func (sf *QFloat[M]) Get() float64 {
+	return sf.Val().Get()
+}
+
+// Set -- устанавливает хранимое значение
+func Set(sf *QFloat[quark_val.Mut], val float64) {
+	quark_val.Set(sf.Val(), val)
+}
+
+// FromStr -- устанавливает из строки
+func FromStr(sf *QFloat[quark_val.Mut], str string) {
+	fVal, err := strconv.ParseFloat(str, 64)
+	Hassert(err == nil, "QFloat.FromStr(): str=%q, err=\n\t%v", str, err)
+	quark_val.Set(sf.Val(), fVal)
+}
+
+// ToStr -- конвертирует в строку
+func (sf *QFloat[M]) ToStr() string {
+	return fmt.Sprint(sf.Get())
+}

+ 44 - 0
v4/lev0/quark/quark_float/quark_float_test.go

@@ -0,0 +1,44 @@
+package quark_float
+
+import "testing"
+
+type tester struct {
+	t *testing.T
+}
+
+func TestQuarkFloat(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.new()
+	sf.newMut()
+}
+
+// Новое мутабельное значение
+func (sf *tester) newMut() {
+	sf.t.Log("newMut")
+	qf := NewQFloatMut(123.456)
+	if qf.Get() != 123.456 {
+		sf.t.Fatalf("NewQFloatMut(123.456): qf.Get()=%v", qf.Get())
+	}
+	Set(qf, 12.34)
+	if qf.Get() != 12.34 {
+		sf.t.Fatalf("Set(qf, 12.34): qf.Get()=%v", qf.Get())
+	}
+	FromStr(qf, "12.1")
+	if qf.Get() != 12.1 {
+		sf.t.Fatalf("FromStr(qf, \"12.1\"): qf.Get()=%v", qf.Get())
+	}
+	if str := qf.ToStr(); str != "12.1" {
+		sf.t.Fatalf("qf.ToStr(): qf.Get()=%v", str)
+	}
+}
+
+// Новое фиксированное значение
+func (sf *tester) new() {
+	sf.t.Log("new")
+	qf := NewQFloat(123.456)
+	if qf.Get() != 123.456 {
+		sf.t.Fatalf("NewQFloat(123.456): qf.Get()=%v", qf.Get())
+	}
+}

+ 53 - 0
v4/lev0/quark/quark_int/quark_int.go

@@ -0,0 +1,53 @@
+// package quark_int -- кварк целое
+package quark_int
+
+import (
+	"strconv"
+
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_base"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_val"
+)
+
+// QInt -- кварк целое
+type QInt[M quark_val.CMod] struct {
+	*quark_base.QBase[M, int]
+}
+
+// NewQInt -- создать новый фиксированный кварк целое
+func NewQInt(v int) *QInt[quark_val.Fix] {
+	optMass := quark_base.QBaseOptMass[quark_val.Fix, int](8)
+	optVal := quark_base.QBaseOptVal(v)
+	sf := &QInt[quark_val.Fix]{
+		QBase: quark_base.NewQBase(optMass, optVal),
+	}
+	return sf
+}
+
+// NewQIntMut -- создать новый изменяемый кварк целое
+func NewQIntMut(v int) *QInt[quark_val.Mut] {
+	optMass := quark_base.QBaseOptMass[quark_val.Mut, int](8)
+	optVal := quark_base.QBaseOptValMut(v)
+	sf := &QInt[quark_val.Mut]{
+		QBase: quark_base.NewQBaseMut(optMass, optVal),
+	}
+	return sf
+}
+
+// Get -- возвращает хранимое значение
+func (sf *QInt[M]) Get() int {
+	return sf.Val().Get()
+}
+
+// Set -- установить значение
+func Set(sf *QInt[quark_val.Mut], v int) {
+	quark_val.Set(sf.Val(), v)
+}
+
+// FromStr -- установить значение из строки
+func FromStr(sf *QInt[quark_val.Mut], s string) {
+	iVal, err := strconv.Atoi(s)
+	Hassert(err == nil, "QInt.FromStr(): не удалось преобразовать строку(%v) в int, err=\n\t%v",
+		s, err)
+	quark_val.Set(sf.Val(), iVal)
+}

+ 41 - 0
v4/lev0/quark/quark_int/quark_int_test.go

@@ -0,0 +1,41 @@
+package quark_int
+
+import "testing"
+
+type tester struct {
+	t *testing.T
+}
+
+func TestQuarkInt(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.new()
+	sf.newMut()
+}
+
+// Новое мутабельное целое
+func (sf *tester) newMut() {
+	sf.t.Log("newMut")
+	qi := NewQIntMut(100)
+	if qi.Get() != 100 {
+		sf.t.Fatalf("newMut(): qi.Get()=%d", qi.Get())
+	}
+	Set(qi, 200)
+	if qi.Get() != 200 {
+		sf.t.Fatalf("newMut(): qi.Get()=%d", qi.Get())
+	}
+	FromStr(qi, "300")
+	if qi.Get() != 300 {
+		sf.t.Fatalf("newMut(): qi.Get()=%d", qi.Get())
+	}
+}
+
+// Новое фиксированное целое
+func (sf *tester) new() {
+	sf.t.Log("new")
+	qi := NewQInt(100)
+	if qi.Get() != 100 {
+		sf.t.Fatalf("new(): qi.Get()=%d", qi.Get())
+	}
+}

+ 42 - 0
v4/lev0/quark/quark_str/quark_str.go

@@ -0,0 +1,42 @@
+// package quark_str -- кварк строки
+package quark_str
+
+import (
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_base"
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/quark/quark_val"
+)
+
+// QStr -- кварк строки
+type QStr[M quark_val.CMod] struct {
+	*quark_base.QBase[M, string]
+}
+
+// NewQStr -- создает фиксированную новую кварк строку
+func NewQStr(val string) *QStr[quark_val.Fix] {
+	optMass := quark_base.QBaseOptMass[quark_val.Fix, string](len(val))
+	optVal := quark_base.QBaseOptVal(val)
+	sf := &QStr[quark_val.Fix]{
+		QBase: quark_base.NewQBase(optMass, optVal),
+	}
+	return sf
+}
+
+// NewQStrMut -- создает мутабельную новую кварк строку
+func NewQStrMut(val string) *QStr[quark_val.Mut] {
+	optMass := quark_base.QBaseOptMass[quark_val.Mut, string](len(val))
+	optVal := quark_base.QBaseOptValMut(val)
+	sf := &QStr[quark_val.Mut]{
+		QBase: quark_base.NewQBaseMut(optMass, optVal),
+	}
+	return sf
+}
+
+// Get -- возвращает хранимое значение
+func (sf *QStr[M]) Get() string {
+	return sf.Val().Get()
+}
+
+// Set -- устанавливает хранимое значение
+func Set(sf *QStr[quark_val.Mut], val string) {
+	quark_val.Set(sf.Val(), val)
+}

+ 34 - 0
v4/lev0/quark/quark_str/quark_str_test.go

@@ -0,0 +1,34 @@
+package quark_str
+
+import "testing"
+
+type tester struct {
+	t *testing.T
+}
+
+func TestQStr(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.new()
+	sf.newMut()
+}
+
+// Создание строки с мутацией
+func (sf *tester) newMut() {
+	sf.t.Log("newMut")
+	qs := NewQStrMut("test")
+	Set(qs, "test2")
+	if qs.Get() != "test2" {
+		sf.t.Fatalf("newMut(): bad value")
+	}
+}
+
+// Создание неизменяемой строки
+func (sf *tester) new() {
+	sf.t.Log("new")
+	qs := NewQStr("test")
+	if qs.Get() != "test" {
+		sf.t.Fatalf("new(): bad value")
+	}
+}

+ 44 - 0
v4/lev0/quark/quark_val/quark_val.go

@@ -0,0 +1,44 @@
+// package quark_val -- кварк значения
+package quark_val
+
+// Fix -- фиксированное значение
+type Fix struct{}
+
+// Mut -- изменяемое значение
+type Mut struct{}
+
+// CMod -- ограничитель модификации
+type CMod interface {
+	Fix | Mut
+}
+
+// Field -- кварк поля
+type Field[M CMod, T any] struct {
+	val T // Значение поля
+}
+
+// NewField -- возвращает новое фиксированное значение
+func NewField[T any](val T) *Field[Fix, T] {
+	sf := &Field[Fix, T]{
+		val: val,
+	}
+	return sf
+}
+
+// NewFieldMut -- возвращает новое мутабельное значение
+func NewFieldMut[T any](val T) *Field[Mut, T] {
+	sf := &Field[Mut, T]{
+		val: val,
+	}
+	return sf
+}
+
+// Получить значение поля
+func (sf *Field[M, T]) Get() T {
+	return sf.val
+}
+
+// Set -- установить мутабельное значение
+func Set[T any](sf *Field[Mut, T], val T) {
+	sf.val = val
+}

+ 48 - 0
v4/lev0/quark/quark_val/quark_val_test.go

@@ -0,0 +1,48 @@
+package quark_val
+
+import "testing"
+
+type tester struct {
+	t *testing.T
+}
+
+func TestQuarkVal(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.newFix()
+	sf.newMut()
+}
+
+// Создание нового мутабельного значения
+func (sf *tester) newMut() {
+	sf.t.Log("newMut")
+	f := NewFieldMut(10)
+	if f == nil {
+		sf.t.Fatalf("newMut(): f==nil")
+	}
+	if v := f.Get(); v != 10 {
+		sf.t.Fatalf("newMut(): v!=10")
+	}
+	Set(f, 32)
+	if v := f.Get(); v != 32 {
+		sf.t.Fatalf("newMut(): v(%v)!=32", v)
+	}
+}
+
+// Создание нового фиксированного значения
+func (sf *tester) newFix() {
+	sf.t.Log("newFix")
+	f := NewField(10)
+	if f == nil {
+		sf.t.Fatalf("newFix(): f==nil")
+	}
+	if v := f.Get(); v != 10 {
+		sf.t.Fatalf("newFix(): v!=10")
+	}
+	// Эту функцию вызвать нельзя -- тип другой
+	// Set(f, 34)
+	// if v := f.Get(); v != 10 {
+	// 	sf.t.Fatalf("newFix(): v(%v)!=10", v)
+	// }
+}

+ 1 - 1
v4/lev1/ctx_value/ctx_value.go

@@ -5,9 +5,9 @@ import (
 	"fmt"
 	"sync"
 
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"
 )
 

+ 1 - 1
v4/lev1/kbus_msg/msg_pub/msg_pub.go

@@ -2,8 +2,8 @@
 package msg_pub
 
 import (
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 // PublishReq -- запрос на публикацию

+ 1 - 1
v4/lev1/kbus_msg/msg_serve/msg_serve.go

@@ -2,8 +2,8 @@
 package msg_serve
 
 import (
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 // ServeReq -- входящий запрос на обслуживание

+ 1 - 1
v4/lev1/kbus_msg/msg_sub/msg_sub.go

@@ -2,8 +2,8 @@
 package msg_sub
 
 import (
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 // SubscribeReq -- входящий запрос на подписку

+ 1 - 1
v4/lev1/kbus_msg/msg_unsub/msg_unsub.go

@@ -2,8 +2,8 @@
 package msg_unsub
 
 import (
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 // UnsubReq -- запрос на отписку от топика

+ 1 - 1
v4/lev1/lev1.go

@@ -5,8 +5,8 @@ import (
 	"context"
 	"fmt"
 
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/kint"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/local_ctx"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/log_buf"

+ 79 - 0
v4/lev1/lev1_test.go

@@ -0,0 +1,79 @@
+// package lev1 -- объединяющий пакет уровня
+package lev1
+
+import (
+	"testing"
+)
+
+type tester struct {
+	t *testing.T
+}
+
+func TestLev1(t *testing.T) {
+	sf := &tester{
+		t: t,
+	}
+	sf.makeEnv()
+	sf.optPrefix()
+	sf.optIsTerm()
+	sf.newLogBuf()
+	sf.newSafeString()
+	sf.newSafeIntFromStr()
+	sf.newSafeIntGetenv()
+}
+
+func (sf *tester) newSafeIntGetenv() {
+	sf.t.Log("newSafeIntGetenv")
+	safeInt := NewSafeIntGetenv("test_env")
+	if safeInt == nil {
+		sf.t.Fatalf("newSafeIntGetenv(): safeInt==nil")
+	}
+}
+
+func (sf *tester) newSafeIntFromStr() {
+	sf.t.Log("newSafeIntFromStr")
+	safeInt := NewSafeIntFromStr("123")
+	if safeInt == nil {
+		sf.t.Fatalf("newSafeIntFromStr(): safeInt==nil")
+	}
+}
+
+func (sf *tester) newSafeString() {
+	sf.t.Log("newSafeString")
+	safeString := NewSafeString()
+	if safeString == nil {
+		sf.t.Fatalf("newSafeString(): safeString==nil")
+	}
+}
+
+func (sf *tester) newLogBuf() {
+	sf.t.Log("newLogBuf")
+	logBuf := NewLogBuf()
+	if logBuf == nil {
+		sf.t.Fatalf("newLogBuf(): logBuf==nil")
+	}
+}
+
+func (sf *tester) optIsTerm() {
+	sf.t.Log("optIsTerm")
+	optIsTerm := OptIsTerm(true)
+	if optIsTerm == nil {
+		sf.t.Fatalf("optIsTerm(): optIsTerm==nil")
+	}
+}
+
+func (sf *tester) optPrefix() {
+	sf.t.Log("optPrefix")
+	optPref := OptPrefix("test_prefix")
+	if optPref == nil {
+		sf.t.Fatalf("optPrefix(): optPref==nil")
+	}
+}
+
+func (sf *tester) makeEnv() {
+	sf.t.Log("makeEnv")
+	env := MakeEnv()
+	if env == nil {
+		sf.t.Fatalf("makeEnv(): env==nil")
+	}
+}

+ 1 - 3
v4/lev1/local_ctx/local_ctx.go

@@ -6,11 +6,9 @@ import (
 	"fmt"
 	"sync"
 
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/ctx_value"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
-
-	// . "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/log_buf"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/lst_sort"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/option"

+ 1 - 1
v4/lev1/log_buf/log_buf.go

@@ -5,8 +5,8 @@ import (
 	"fmt"
 	"sync"
 
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/log_msg"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_bool"
 )

+ 1 - 1
v4/lev1/log_buf/log_buf_test.go

@@ -3,8 +3,8 @@ package log_buf
 import (
 	"testing"
 
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 type tester struct {

+ 1 - 1
v4/lev1/log_msg/log_msg.go

@@ -4,9 +4,9 @@ package log_msg
 import (
 	"fmt"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 const (

+ 1 - 1
v4/lev1/lst_sort/lst_sort.go

@@ -5,8 +5,8 @@ import (
 	"sort"
 	"sync"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 // LstSort -- сортированный список значений контекста

+ 1 - 1
v4/lev1/mock_env/mock_env.go

@@ -5,7 +5,7 @@ import (
 	"os"
 	"strings"
 
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 )
 
 type IMockEnv interface {

+ 4 - 4
v4/lev1/mock_env/mock_env_test.go

@@ -31,8 +31,8 @@ func (sf *tester) reset() {
 func (sf *tester) resetBad1() {
 	sf.t.Log("resetBad1")
 	defer func() {
-		if panic_ := recover(); panic_ == nil {
-			sf.t.Fatalf("resetBad1(): panic=%v", panic_)
+		if pan := recover(); pan == nil {
+			sf.t.Fatalf("resetBad1(): panic=%v", pan)
 		}
 	}()
 	sf.me.Reset("")
@@ -42,8 +42,8 @@ func (sf *tester) resetBad1() {
 func (sf *tester) make() {
 	sf.t.Log("make")
 	defer func() {
-		if panic_ := recover(); panic_ != nil {
-			sf.t.Fatalf("make(): panic=%v", panic_)
+		if pan := recover(); pan != nil {
+			sf.t.Fatalf("make(): panic=%v", pan)
 		}
 	}()
 	env := MakeEnv()

+ 1 - 1
v4/lev1/option/option.go

@@ -3,8 +3,8 @@ package option
 import (
 	"reflect"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 // Option -- результат возможно содержащий nil

+ 1 - 1
v4/lev1/result/result.go

@@ -5,8 +5,8 @@ import (
 	"reflect"
 	"runtime"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 // Result — обёртка вокруг результата с возможной ошибкой

+ 1 - 1
v4/lev2/kbus_http/client_bus_http/client_bus_http.go

@@ -133,7 +133,7 @@ func (sf *ClientBusHttp) Subscribe(handler IBusHandlerSubscribe) IResult[bool] {
 		err := fmt.Errorf("ClientBusHttp.Subscribe(): in unmarshal response, err=\n\t%v", err)
 		return NewErr[bool](err)
 	}
-	if string(resp.Status_) != "ok" {
+	if resp.Status_ != "ok" {
 		err := fmt.Errorf("ClientBusHttp.Subscribe(): resp!='ok', err=\n\t%v", resp.Status_)
 		return NewErr[bool](err)
 	}

+ 3 - 3
v4/lev2/kmodule/kmodule.go

@@ -5,9 +5,9 @@ import (
 	"fmt"
 	"time"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/local_ctx"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_int"
@@ -80,7 +80,7 @@ func (sf *kModule) Ctx() ILocalCtx {
 
 // Run -- запускает модуль в работу
 func (sf *kModule) Run() {
-	Hassert(false, "kModule.Run(): module='%v', parent not realised this method", sf.name)
+	Hassert(false, "kModule.Run(): module='%v', parent not realized this method", sf.name)
 }
 
 // Name -- возвращает уникальное имя модуля
@@ -90,7 +90,7 @@ func (sf *kModule) Name() AModuleName {
 
 // IsWork -- возвращает признак состояния работы
 func (sf *kModule) IsWork() bool {
-	Hassert(false, "kModule.IsWork(): module='%v', parent not realised this method", sf.name)
+	Hassert(false, "kModule.IsWork(): module='%v', parent not realized this method", sf.name)
 	return false
 }
 

+ 1 - 1
v4/lev2/kmodule/mod_stat/mod_stat.go

@@ -11,9 +11,9 @@ package mod_stat
 import (
 	"time"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_int"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/kmodule/mod_stat/mod_stat_day"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/kmodule/mod_stat/mod_stat_sec"

+ 2 - 0
v4/lev2/kmodule/mod_stat/mod_stat_day/mod_stat_day.go

@@ -114,6 +114,7 @@ func (sf *ModStatDay) Svg() string {
 		cnv.Line(40, 280, 40, 38, "fill:true;stroke:black;stroke-width:4")
 		cnv.Line(40, 280, 442, 280, "fill:true;stroke:black;stroke-width:4")
 		count := 0
+
 		for x := 40; x < 460; x += 20 {
 			cnv.Line(x, 40, x, 280, "fill:true;stroke:gray")
 			if count%5 == 0 {
@@ -122,6 +123,7 @@ func (sf *ModStatDay) Svg() string {
 			count++
 		}
 		count = 0
+
 		for y := 40; y < 300; y += 20 {
 			cnv.Line(40, y, 440, y, "fill:true;stroke:gray")
 			if count%5 == 0 {

+ 1 - 1
v4/lev2/kmonolit/kmonolit.go

@@ -4,9 +4,9 @@ package kmonolit
 import (
 	"fmt"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/local_ctx"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_bool"

+ 1 - 1
v4/lev2/kmonolit/kmonolit_test.go

@@ -4,9 +4,9 @@ import (
 	"os"
 	"testing"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/mock_env"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/kctx"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/kmodule"

+ 1 - 1
v4/lev2/kserv_http/kserv_http.go

@@ -15,8 +15,8 @@ import (
 	"github.com/gofiber/fiber/v2/middleware/filesystem"
 	"github.com/gofiber/fiber/v2/middleware/monitor"
 
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/local_ctx"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/log_buf"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"

+ 1 - 1
v4/lev2/kserv_http/kserv_http_test.go

@@ -4,8 +4,8 @@ import (
 	"os"
 	"testing"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/mock_env"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/kctx"
 )

+ 9 - 0
v4/lev2/kserv_http_std/kserv_http_std_test.go

@@ -0,0 +1,9 @@
+package kserv_http_std
+
+import (
+	"testing"
+)
+
+func TestKservHttpStd(t *testing.T) {
+
+}

+ 1 - 1
v4/lev2/kstore_kv/kstore_kv.go

@@ -9,8 +9,8 @@ import (
 
 	"github.com/dgraph-io/badger/v4"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/local_ctx"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/log_buf"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"

+ 7 - 0
v4/lev2/kstore_kv_std/kstore_kv_std_test.go

@@ -0,0 +1,7 @@
+package kstore_kv_std
+
+import (
+	"testing"
+)
+
+func TestKstoreKvStd(t *testing.T) {}

+ 1 - 1
v4/lev2/kstore_kv_std/kv_bucket_base/kv_bucket_base.go

@@ -2,9 +2,9 @@
 package kv_bucket_base
 
 import (
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 var (

+ 7 - 0
v4/lev2/kstore_kv_std/kv_bucket_base/kv_bucket_base_test.go

@@ -0,0 +1,7 @@
+package kv_bucket_base
+
+import (
+	"testing"
+)
+
+func TestKvBucketBase(t *testing.T) {}

+ 7 - 0
v4/lev2/kstore_kv_std/kv_bucket_ram/kv_bucket_ram_test.go

@@ -0,0 +1,7 @@
+package kv_bucket_ram
+
+import (
+	"testing"
+)
+
+func TestKvBucketRam(t *testing.T) {}

+ 1 - 1
v4/lev2/kstore_kv_std/rec_kv/rec_kv.go

@@ -2,8 +2,8 @@
 package rec_kv
 
 import (
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 var (

+ 1 - 1
v4/lev2/kstore_kv_std/rec_meta/rec_meta.go

@@ -2,9 +2,9 @@
 package rec_meta
 
 import (
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 var (

+ 9 - 9
v4/lev2/kstore_kv_std/rec_meta/rec_meta_test.go

@@ -17,7 +17,7 @@ func TestRecMeta(t *testing.T) {
 	sf.gets()
 }
 
-// Проверка методов мета-информации
+// Проверка методов мета-информации.
 func (sf *tester) gets() {
 	sf.t.Log("gets")
 	if key := sf.meta.Key(); key != "test" {
@@ -43,28 +43,28 @@ func (sf *tester) gets() {
 	}
 }
 
-// Создаёт новую запись
+// Создаёт новую запись.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	sf.new_bad1()
-	sf.new_good1()
+	sf.newBad1()
 }
 
-// Правильное создание записи
-func (sf *tester) new_good1() {
-	sf.t.Log("new_good1")
+// Правильное создание записи.
+func (sf *tester) newBad1() {
+	sf.t.Log("newBad1")
 	defer func() {
 		if _panic := recover(); _panic != nil {
-			sf.t.Fatalf("new_good1(): panic!=nil")
+			sf.t.Fatalf("newBad1(): panic!=nil")
 		}
 	}()
 	sf.meta = NewRecMeta("test")
 	if sf.meta == nil {
-		sf.t.Fatalf("new_good1(): sf.meta==nil")
+		sf.t.Fatalf("newBad1(): sf.meta==nil")
 	}
 }
 
-// Пустой ключ записи
+// Пустой ключ записи.
 func (sf *tester) new_bad1() {
 	sf.t.Log("new_bad1")
 	defer func() {

+ 6 - 6
v4/lev2/kstore_kv_std/rec_val/rec_val.go

@@ -1,17 +1,17 @@
-// package rec_val -- запись KV-хранилища
+// Package rec_val -- запись KV-хранилища.
 package rec_val
 
 import (
+	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 var (
 	hassert = helpers.Hassert
 )
 
-// RecVal -- запись KV-хранилища
+// RecVal -- запись KV-хранилища.
 //
 // Запись в файле значений бакета лежит
 // в виде JSON-строки, без форматирования для того,
@@ -21,7 +21,7 @@ type RecVal struct {
 	Val_ []byte      `json:"val"` // Значение
 }
 
-// NewRecVal -- создание нового значения KV-хранилища
+// NewRecVal -- создание нового значения KV-хранилища.
 func NewRecVal(key kalias.AKey, val []byte) *RecVal {
 	hassert(key != "", "NewRecVal: key is empty")
 	if val == nil {
@@ -35,12 +35,12 @@ func NewRecVal(key kalias.AKey, val []byte) *RecVal {
 	return sf
 }
 
-// Key -- ключ
+// Key -- ключ.
 func (sf *RecVal) Key() kalias.AKey {
 	return sf.Key_
 }
 
-// Val -- значение
+// Val -- значение.
 func (sf *RecVal) Val() []byte {
 	return sf.Val_
 }

+ 13 - 13
v4/lev2/kstore_kv_std/rec_val/rec_val_test.go

@@ -16,34 +16,34 @@ func TestRecVal(t *testing.T) {
 	sf.new()
 }
 
-// Создание нового значения KV-хранилища
+// Создание нового значения KV-хранилища.
 func (sf *tester) new() {
 	sf.t.Log("new")
-	sf.new_bad1()
-	sf.new_good1()
+	sf.newBad1()
+	sf.newGood1()
 }
 
-// Ключ -- не пустая строка
-func (sf *tester) new_good1() {
-	sf.t.Log("new_good1")
+// Ключ -- не пустая строка.
+func (sf *tester) newGood1() {
+	sf.t.Log("newGood1")
 	sf.recVal = NewRecVal("key", nil)
 	if sf.recVal == nil {
-		sf.t.Fatalf("new_good1(): recVal==nil")
+		sf.t.Fatalf("newGood1(): recVal==nil")
 	}
 	if key_ := sf.recVal.Key(); key_ != "key" {
-		sf.t.Fatalf("new_good1(): key_=%s", key_)
+		sf.t.Fatalf("newGood1(): key_=%s", key_)
 	}
 	if val_ := sf.recVal.Val(); val_ == nil {
-		sf.t.Fatalf("new_good1(): val_ == nil")
+		sf.t.Fatalf("newGood1(): val_ == nil")
 	}
 }
 
-// Вместо ключа -- пустая строка
-func (sf *tester) new_bad1() {
-	sf.t.Log("new_bad1")
+// Вместо ключа -- пустая строка.
+func (sf *tester) newBad1() {
+	sf.t.Log("newBad1")
 	defer func() {
 		if _panic := recover(); _panic == nil {
-			sf.t.Fatalf("new_bad1(): panic==nil")
+			sf.t.Fatalf("newBad1(): panic==nil")
 		}
 	}()
 	_ = NewRecVal("", nil)

+ 12 - 12
v4/lev2/kwg/kwg.go

@@ -1,4 +1,4 @@
-// package kwg -- именованный ожидатель потоков ядра
+// package kwg -- именованный ожидатель потоков ядра.
 //
 // Не позволяет завершиться ядру, если есть хоть один работающий поток
 package kwg
@@ -8,15 +8,15 @@ import (
 	"fmt"
 	"sync"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/log_buf"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_bool"
 )
 
-// kernelWg -- именованный ожидатель потоков ядра
+// kernelWg -- именованный ожидатель потоков ядра.
 type kernelWg struct {
 	sync.RWMutex
 	ctx        context.Context
@@ -30,7 +30,7 @@ var (
 	block  sync.Mutex
 )
 
-// GetKernelWg -- возвращает новый именованный ожидатель потоков ядра
+// GetKernelWg -- возвращает новый именованный ожидатель потоков ядра.
 func GetKernelWg(ctx context.Context) IKernelWg {
 	block.Lock()
 	defer block.Unlock()
@@ -52,24 +52,24 @@ func GetKernelWg(ctx context.Context) IKernelWg {
 	return kernWg
 }
 
-// Log -- возвращает лог ожидателя потоков
+// Log -- возвращает лог ожидателя потоков.
 func (sf *kernelWg) Log() ILogBuf {
 	return sf.log
 }
 
-// Len -- возвращает размер списка ожидания потоков
+// Len -- возвращает размер списка ожидания потоков.
 func (sf *kernelWg) Len() int {
 	sf.RLock()
 	defer sf.RUnlock()
 	return len(sf.dictStream)
 }
 
-// IsWork -- возвращает признак работы ядра
+// IsWork -- возвращает признак работы ядра.
 func (sf *kernelWg) IsWork() bool {
 	return sf.isWork.Get()
 }
 
-// List -- возвращает список имён потоков на ожидании
+// List -- возвращает список имён потоков на ожидании.
 func (sf *kernelWg) List() []AStreamName {
 	sf.RLock()
 	defer sf.RUnlock()
@@ -80,7 +80,7 @@ func (sf *kernelWg) List() []AStreamName {
 	return lst
 }
 
-// Done -- удаляет поток из ожидания
+// Done -- удаляет поток из ожидания.
 func (sf *kernelWg) Done(name AStreamName) {
 	sf.Lock()
 	defer sf.Unlock()
@@ -88,7 +88,7 @@ func (sf *kernelWg) Done(name AStreamName) {
 	sf.log.Debug("Done(): stream(%v) done", name)
 }
 
-// Wait -- блокирующий вызов; возвращает управление, только когда все потоки завершили работу
+// Wait -- блокирующий вызов; возвращает управление, только когда все потоки завершили работу.
 func (sf *kernelWg) Wait() {
 	for {
 		SleepMs()
@@ -99,7 +99,7 @@ func (sf *kernelWg) Wait() {
 	sf.log.Debug("Wait(): done")
 }
 
-// Add -- добавляет поток в ожидание
+// Add -- добавляет поток в ожидание.
 func (sf *kernelWg) Add(name AStreamName) IResult[bool] {
 	sf.Lock()
 	defer sf.Unlock()
@@ -115,7 +115,7 @@ func (sf *kernelWg) Add(name AStreamName) IResult[bool] {
 	return NewRes(true)
 }
 
-// Ожидает окончания работы ожидателя групп
+// Ожидает окончания работы ожидателя групп.
 func (sf *kernelWg) close() {
 	<-sf.ctx.Done()
 	fnDone := func() bool {

+ 8 - 8
v4/lev2/kwg/kwg_test.go

@@ -31,7 +31,7 @@ func TestKernelWG(t *testing.T) {
 	sf.addBad3()
 }
 
-// Попытка добавления после закрытия ожидателя
+// Попытка добавления после закрытия ожидателя.
 func (sf *tester) addBad3() {
 	sf.t.Log("addBad3")
 	res := sf.wg.Add("test_stream")
@@ -46,7 +46,7 @@ func (sf *tester) addBad3() {
 	kernWg.close()
 }
 
-// Убирает имя потока из ожидателя
+// Убирает имя потока из ожидателя.
 func (sf *tester) done() {
 	sf.t.Log("done")
 	sf.wg.Done("test_stream")
@@ -56,7 +56,7 @@ func (sf *tester) done() {
 	}
 }
 
-// Добавление потока ожидания
+// Добавление потока ожидания.
 func (sf *tester) add() {
 	sf.t.Log("add")
 	sf.addGood1()
@@ -64,7 +64,7 @@ func (sf *tester) add() {
 	sf.addBad2()
 }
 
-// Уже есть такое имя потока
+// Уже есть такое имя потока.
 func (sf *tester) addBad2() {
 	sf.t.Log("addBad2")
 	defer func() {
@@ -79,7 +79,7 @@ func (sf *tester) addBad2() {
 	_ = sf.wg.Add("test_stream")
 }
 
-// Пустое имя потока
+// Пустое имя потока.
 func (sf *tester) addBad1() {
 	sf.t.Log("addBad1")
 	defer func() {
@@ -98,7 +98,7 @@ func (sf *tester) addGood1() {
 	}
 }
 
-// Ожидание завершения ожидателя потоков
+// Ожидание завершения ожидателя потоков.
 func (sf *tester) wait() {
 	sf.t.Log("wait")
 	go sf.wg.Wait()
@@ -112,7 +112,7 @@ func (sf *tester) wait() {
 	}
 }
 
-// Создаёт ожидатель потоков ядра
+// Создаёт ожидатель потоков ядра.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	sf.newBad1()
@@ -142,7 +142,7 @@ func (sf *tester) newGood1() {
 	}
 }
 
-// Нет контекста ядра
+// Нет контекста ядра.
 func (sf *tester) newBad1() {
 	sf.t.Log("newBad1")
 	defer func() {

+ 10 - 0
v4/lev2/lev2_test.go

@@ -0,0 +1,10 @@
+// package lev2 -- объединяющий пакет уровня
+package lev2
+
+import (
+	"testing"
+)
+
+func TestLev2(t *testing.T) {
+	t.Log("Тест уровня 2")
+}

+ 4 - 4
v4/lev2/log_topic/log_topic.go

@@ -1,4 +1,4 @@
-// package log_topic -- элемент лога шины топика
+// package log_topic -- элемент лога шины топика.
 package log_topic
 
 import (
@@ -9,13 +9,13 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"
 )
 
-// LogTopic -- элемент лога шины
+// LogTopic -- элемент лога шины.
 type LogTopic struct {
 	topic  ATopic     // Топик, куда публиковать лог
 	client IBusClient // С помощью чего публиковать лог
 }
 
-// NewLogTopic -- возвращает новый элемент лога
+// NewLogTopic -- возвращает новый элемент лога.
 func NewLogTopic(topic ATopic, client IBusClient) IResult[*LogTopic] {
 	if topic == "" {
 		err := fmt.Errorf("NewLogTopic(): topic is empty")
@@ -32,7 +32,7 @@ func NewLogTopic(topic ATopic, client IBusClient) IResult[*LogTopic] {
 	return NewRes(sf)
 }
 
-// Pub -- публикует сообщение в топик
+// Pub -- публикует сообщение в топик.
 func (sf *LogTopic) Pub(binMsg []byte) IResult[bool] {
 	res := sf.client.Publish(sf.topic, binMsg)
 	if res.IsErr() {

+ 3 - 3
v4/lev2/log_topic/log_topic_test.go

@@ -18,14 +18,14 @@ func TestLogTopic(t *testing.T) {
 	sf.new()
 }
 
-// Создаёт новый топик для логирования
+// Создаёт новый топик для логирования.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	sf.newBad1()
 	sf.newGood1()
 }
 
-// Правильное создание топика
+// Правильное создание топика.
 func (sf *tester) newGood1() {
 	sf.t.Log("newGood1")
 	busClient := client_bus_local.NewClientBusLocal().Hassert("newGood1()")
@@ -43,7 +43,7 @@ func (sf *tester) newGood1() {
 	}
 }
 
-// Нет топика лога
+// Нет топика лога.
 func (sf *tester) newBad1() {
 	res := NewLogTopic("", nil)
 	if res.IsOk() {

+ 1 - 1
v4/lev2/mock_hand_serve/mock_hand_serve.go

@@ -5,9 +5,9 @@ import (
 	"fmt"
 	"sync"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_bool"
 )

+ 4 - 4
v4/lev2/mock_hand_serve/mock_hand_serve_test.go

@@ -17,7 +17,7 @@ func TestMockHandleServe(t *testing.T) {
 	sf.back()
 }
 
-// Проверка обратного вызова
+// Проверка обратного вызова.
 func (sf *tester) back() {
 	sf.t.Log("back")
 	sf.backBad1()
@@ -35,7 +35,7 @@ func (sf *tester) backGood1() {
 	}
 }
 
-// Что-то сломалось
+// Что-то сломалось.
 func (sf *tester) backBad1() {
 	sf.t.Log("backBad1")
 	sf.hand.IsBad_.Set()
@@ -46,7 +46,7 @@ func (sf *tester) backBad1() {
 	sf.hand.IsBad_.Reset()
 }
 
-// Создание мок-обработчика запросов
+// Создание мок-обработчика запросов.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	sf.newBad1()
@@ -67,7 +67,7 @@ func (sf *tester) newGood1() {
 	}
 }
 
-// Нет топика для создания
+// Нет топика для создания.
 func (sf *tester) newBad1() {
 	sf.t.Log("newBad1")
 	defer func() {

+ 9 - 12
v4/lev2/mock_hand_sub_http/mock_hand_sub_http.go

@@ -1,20 +1,17 @@
-// package handler_http_sub -- обработчик подписки по HTTP
+// package handler_http_sub -- обработчик подписки по HTTP.
 package mock_hand_sub_http
 
 import (
-	// "bytes"
 	"crypto/rand"
 	"log"
 	"sync"
 
-	// "net/http"
-
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
-// MockHandSubHttp -- обработчик подписки по HTTP
+// MockHandSubHttp -- обработчик подписки по HTTP.
 type MockHandSubHttp struct {
 	Name_    AHandlerName // Уникальное имя обработчика
 	Topic_   ATopic       // Имя топика, на который подписан обработчик
@@ -23,7 +20,7 @@ type MockHandSubHttp struct {
 	block    sync.RWMutex
 }
 
-// NewMockHandSubHttp -- возвращает новый обработчик подписки по HTTP
+// NewMockHandSubHttp -- возвращает новый обработчик подписки по HTTP.
 func NewMockHandSubHttp(topic ATopic, webHook string) IBusHandlerSubscribe {
 	Hassert(topic != "", "NewHandlerHttpSub(): topic is empty")
 	Hassert(webHook != "", "NewHandlerHttpSub(): webHook is empty")
@@ -35,12 +32,12 @@ func NewMockHandSubHttp(topic ATopic, webHook string) IBusHandlerSubscribe {
 	return sf
 }
 
-// Topic -- возвращает имя топика, на который подписан обработчик
+// Topic -- возвращает имя топика, на который подписан обработчик.
 func (sf *MockHandSubHttp) Topic() ATopic {
 	return sf.Topic_
 }
 
-// SetName -- устанавливает имя обработчика
+// SetName -- устанавливает имя обработчика.
 func (sf *MockHandSubHttp) SetName(name AHandlerName) {
 	sf.block.Lock()
 	defer sf.block.Unlock()
@@ -48,14 +45,14 @@ func (sf *MockHandSubHttp) SetName(name AHandlerName) {
 	sf.Name_ = name
 }
 
-// Name -- возвращает уникальное имя обработчика
+// Name -- возвращает уникальное имя обработчика.
 func (sf *MockHandSubHttp) Name() AHandlerName {
 	sf.block.RLock()
 	defer sf.block.RUnlock()
 	return sf.Name_
 }
 
-// FnBack -- обратный вызов по приходу сообщения
+// FnBack -- обратный вызов по приходу сообщения.
 func (sf *MockHandSubHttp) FnBack(binMsg []byte) {
 	sf.block.Lock()
 	defer sf.block.Unlock()
@@ -63,7 +60,7 @@ func (sf *MockHandSubHttp) FnBack(binMsg []byte) {
 	sf.BinMsg_ = binMsg
 }
 
-// Msg -- возвращает строковое представление хранимого сообщения
+// Msg -- возвращает строковое представление хранимого сообщения.
 func (sf *MockHandSubHttp) Msg() string {
 	sf.block.Lock()
 	defer sf.block.Unlock()

+ 6 - 6
v4/lev2/mock_hand_sub_http/mock_hand_sub_http_test.go

@@ -49,7 +49,7 @@ func TestHandlerHttpSub(t *testing.T) {
 	sf.ctx.Wg().Wait()
 }
 
-// Проверка работы обратного вызова
+// Проверка работы обратного вызова.
 func (sf *tester) back() {
 	sf.t.Log("back")
 	sf.backBad1()
@@ -61,7 +61,7 @@ func (sf *tester) backGood1() {
 	sf.hand.FnBack([]byte("test_msg"))
 }
 
-// Что-то случилось на той стороне
+// Что-то случилось на той стороне.
 func (sf *tester) backBad2() {
 	sf.t.Log("backBad2")
 	kernServ := kserv_http.GetKernelServHttp().Hassert("backBad2()")
@@ -74,7 +74,7 @@ func (sf *tester) backBad2() {
 	sf.isBad = false
 }
 
-// Эндпоинт на HTTP-сервере
+// Эндпоинт на HTTP-сервере.
 func (sf *tester) testLocal(ctx *fiber.Ctx) error {
 	if sf.isBad {
 		return ctx.SendStatus(400)
@@ -82,13 +82,13 @@ func (sf *tester) testLocal(ctx *fiber.Ctx) error {
 	return ctx.SendString("ok")
 }
 
-// Нет обработчика для запроса
+// Нет обработчика для запроса.
 func (sf *tester) backBad1() {
 	sf.t.Log("backBad1")
 	sf.hand.FnBack([]byte("hello_test"))
 }
 
-// Создание HTTP-обработчика подписки
+// Создание HTTP-обработчика подписки.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	sf.newBad1()
@@ -121,7 +121,7 @@ func (sf *tester) newGood1() {
 	}
 }
 
-// Нет нужных полей
+// Нет нужных полей.
 func (sf *tester) newBad1() {
 	sf.t.Log("newBad1")
 	defer func() {

+ 1 - 1
v4/lev2/mock_hand_sub_local/mock_hand_sub.go

@@ -5,9 +5,9 @@ import (
 	"crypto/rand"
 	"sync"
 
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kalias"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
 )
 
 type MockHandlerSub struct {

+ 6 - 6
v4/lev2/wui/hx_swap/hx_swap.go

@@ -1,4 +1,4 @@
-// package hx_swap -- атрибут HTMX (политика замены)
+// package hx_swap -- атрибут HTMX (политика замены).
 package hx_swap
 
 import (
@@ -7,34 +7,34 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxSwap -- атрибут HTMX (политика замены)
+// HxSwap -- атрибут HTMX (политика замены).
 type HxSwap struct {
 	sync.RWMutex
 	val string
 }
 
-// NewHxSwap -- возвращает новую политику замены
+// NewHxSwap -- возвращает новую политику замены.
 func NewHxSwap() *HxSwap {
 	sf := &HxSwap{}
 	_ = IHxSwap(sf)
 	return sf
 }
 
-// String -- возвращает строковое представление тэга
+// String -- возвращает строковое представление тэга.
 func (sf *HxSwap) String() string {
 	sf.RLock()
 	defer sf.RUnlock()
 	return `hx-swap="` + sf.val + `"`
 }
 
-// Get -- возвращает хранимое значение политики замена
+// Get -- возвращает хранимое значение политики замена.
 func (sf *HxSwap) Get() string {
 	sf.RLock()
 	defer sf.RUnlock()
 	return sf.val
 }
 
-// Set -- устанавливает значение политики обмена
+// Set -- устанавливает значение политики обмена.
 func (sf *HxSwap) Set(val string) {
 	sf.Lock()
 	defer sf.Unlock()

+ 6 - 6
v4/lev2/wui/hx_swap_oob/hx_swap_oob.go

@@ -1,4 +1,4 @@
-// package hx_swap_oob -- объект внеполосной подкачки
+// package hx_swap_oob -- объект внеполосной подкачки.
 package hx_swap_oob
 
 import (
@@ -7,34 +7,34 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxSwapOob -- объект внеполосной подкачки
+// HxSwapOob -- объект внеполосной подкачки.
 type HxSwapOob struct {
 	sync.RWMutex
 	val string
 }
 
-// NewHxSwapOob -- возвращает новую внеполосную подкачку
+// NewHxSwapOob -- возвращает новую внеполосную подкачку.
 func NewHxSwapOob() *HxSwapOob {
 	sf := &HxSwapOob{}
 	_ = IHxSwapOob(sf)
 	return sf
 }
 
-// String -- возвращает строковое представление тэга
+// String -- возвращает строковое представление тэга.
 func (sf *HxSwapOob) String() string {
 	sf.RLock()
 	defer sf.RUnlock()
 	return `hx-swap-oob="` + sf.val + `"`
 }
 
-// Get -- возвращает хранимое значение внеполосной подкачки
+// Get -- возвращает хранимое значение внеполосной подкачки.
 func (sf *HxSwapOob) Get() string {
 	sf.RLock()
 	defer sf.RUnlock()
 	return sf.val
 }
 
-// Set -- устанавливает значение внеполосной подкачки
+// Set -- устанавливает значение внеполосной подкачки.
 func (sf *HxSwapOob) Set(val string) {
 	sf.Lock()
 	defer sf.Unlock()

+ 9 - 6
v4/lev2/wui/hx_target/hx_target.go

@@ -1,4 +1,4 @@
-// package hx_target -- атрибут HTMX (цель замены)
+// package hx_target -- атрибут HTMX (цель замены).
 package hx_target
 
 import (
@@ -7,34 +7,37 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxSwap -- атрибут HTMX (цель замены)
+// HxSwap -- атрибут HTMX (цель замены).
 type HxSwap struct {
 	sync.RWMutex
 	val string
 }
 
-// NewHxTarget -- возвращает новую цель замены
+// NewHxTarget -- возвращает новую цель замены.
 func NewHxTarget() *HxSwap {
 	sf := &HxSwap{}
 	_ = IHxTarget(sf)
+
 	return sf
 }
 
-// String -- возвращает строковое представление тэга
+// String -- возвращает строковое представление тэга.
 func (sf *HxSwap) String() string {
 	sf.RLock()
 	defer sf.RUnlock()
+
 	return `hx-target="` + sf.val + `"`
 }
 
-// Get -- возвращает хранимое значение цели замена
+// Get -- возвращает хранимое значение цели замена.
 func (sf *HxSwap) Get() string {
 	sf.RLock()
 	defer sf.RUnlock()
+
 	return sf.val
 }
 
-// Set -- устанавливает значение цели обмена
+// Set -- устанавливает значение цели обмена.
 func (sf *HxSwap) Set(val string) {
 	sf.Lock()
 	defer sf.Unlock()

+ 4 - 0
v4/lev2/wui/hx_target/hx_target_test.go

@@ -6,16 +6,20 @@ import (
 
 func TestHxTarget(t *testing.T) {
 	swap := NewHxTarget()
+
 	if swap == nil {
 		t.Fatalf("swap==nil")
 	}
+
 	if tag := swap.Get(); tag != "" {
 		t.Fatalf("tag not empty")
 	}
 	swap.Set("#main")
+
 	if tag := swap.Get(); tag != "#main" {
 		t.Fatalf("tag bad")
 	}
+
 	if str := swap.String(); str != `hx-target="#main"` {
 		t.Fatalf("str is bad")
 	}

+ 9 - 6
v4/lev2/wui/hx_trigger/hx_trigger.go

@@ -1,4 +1,4 @@
-// package hx_trigger -- атрибут HTMX (триггер запроса)
+// package hx_trigger -- атрибут HTMX (триггер запроса).
 package hx_trigger
 
 import (
@@ -7,34 +7,37 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxTrigger -- атрибут HTMX (триггер запроса)
+// HxTrigger -- атрибут HTMX (триггер запроса).
 type HxTrigger struct {
 	sync.RWMutex
 	val string
 }
 
-// NewHxTrigger -- возвращает новый триггер запроса
+// NewHxTrigger -- возвращает новый триггер запроса.
 func NewHxTrigger() *HxTrigger {
 	sf := &HxTrigger{}
 	_ = IHxSwap(sf)
+
 	return sf
 }
 
-// String -- возвращает строковое представление тэга
+// String -- возвращает строковое представление тэга.
 func (sf *HxTrigger) String() string {
 	sf.RLock()
 	defer sf.RUnlock()
+
 	return `hx-trigger="` + sf.val + `"`
 }
 
-// Get -- возвращает хранимое значение триггера запроса
+// Get -- возвращает хранимое значение триггера запроса.
 func (sf *HxTrigger) Get() string {
 	sf.RLock()
 	defer sf.RUnlock()
+
 	return sf.val
 }
 
-// Set -- устанавливает значение триггера запроса
+// Set -- устанавливает значение триггера запроса.
 func (sf *HxTrigger) Set(val string) {
 	sf.Lock()
 	defer sf.Unlock()

+ 5 - 0
v4/lev2/wui/hx_trigger/hx_trigger_test.go

@@ -6,16 +6,21 @@ import (
 
 func TestHxTrigger(t *testing.T) {
 	swap := NewHxTrigger()
+
 	if swap == nil {
 		t.Fatalf("swap==nil")
 	}
+
 	if tag := swap.Get(); tag != "" {
 		t.Fatalf("tag not empty")
 	}
+
 	swap.Set("every 1s")
+
 	if tag := swap.Get(); tag != "every 1s" {
 		t.Fatalf("tag bad")
 	}
+
 	if str := swap.String(); str != `hx-trigger="every 1s"` {
 		t.Fatalf("str is bad")
 	}

+ 6 - 6
v4/lev2/wui/hx_url/hx_url.go

@@ -1,4 +1,4 @@
-// package hx_url -- атрибут HTMX (URL запроса)
+// package hx_url -- атрибут HTMX (URL запроса).
 package hx_url
 
 import (
@@ -7,13 +7,13 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxUrl -- атрибут HTMX (URL запроса)
+// HxUrl -- атрибут HTMX (URL запроса).
 type HxUrl struct {
 	method IHxUrlMethod
 	patch  IHxUrlPatch
 }
 
-// NewHxUrl -- возвращает новый URL запроса
+// NewHxUrl -- возвращает новый URL запроса.
 func NewHxUrl(patch string) *HxUrl {
 	sf := &HxUrl{
 		method: hx_url_method.NewHxUrlMethod(),
@@ -23,17 +23,17 @@ func NewHxUrl(patch string) *HxUrl {
 	return sf
 }
 
-// String -- возвращает строковое представление тэга
+// String -- возвращает строковое представление тэга.
 func (sf *HxUrl) String() string {
 	return sf.method.Get() + `="` + sf.patch.Get() + `"`
 }
 
-// Method -- возвращает метод запроса
+// Method -- возвращает метод запроса.
 func (sf *HxUrl) Method() IHxUrlMethod {
 	return sf.method
 }
 
-// Patch -- возвращает путь запроса
+// Patch -- возвращает путь запроса.
 func (sf *HxUrl) Patch() IHxUrlPatch {
 	return sf.patch
 }

+ 5 - 5
v4/lev2/wui/hx_url_method/hx_url_method.go

@@ -1,4 +1,4 @@
-// package hx_url_method -- атрибут HTMX (метод запроса)
+// package hx_url_method -- атрибут HTMX (метод запроса).
 package hx_url_method
 
 import (
@@ -7,13 +7,13 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxSwap -- атрибут HTMX (метод запроса)
+// HxSwap -- атрибут HTMX (метод запроса).
 type HxSwap struct {
 	sync.RWMutex
 	val string
 }
 
-// NewHxUrlMethod -- возвращает новый метод запроса
+// NewHxUrlMethod -- возвращает новый метод запроса.
 func NewHxUrlMethod() *HxSwap {
 	sf := &HxSwap{
 		val: "hx-post",
@@ -22,14 +22,14 @@ func NewHxUrlMethod() *HxSwap {
 	return sf
 }
 
-// Get -- возвращает хранимое значение метода запроса
+// Get -- возвращает хранимое значение метода запроса.
 func (sf *HxSwap) Get() string {
 	sf.RLock()
 	defer sf.RUnlock()
 	return sf.val
 }
 
-// Set -- устанавливает значение метода запроса
+// Set -- устанавливает значение метода запроса.
 func (sf *HxSwap) Set(val string) {
 	sf.Lock()
 	defer sf.Unlock()

+ 5 - 5
v4/lev2/wui/hx_url_patch/hx_url_patch.go

@@ -4,17 +4,17 @@ package hx_url_patch
 import (
 	"sync"
 
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxUrlPatch -- атрибут HTMX (путь запроса)
+// HxUrlPatch -- атрибут HTMX (путь запроса).
 type HxUrlPatch struct {
 	sync.RWMutex
 	val string
 }
 
-// NewHxUrlPatch -- возвращает новый путь запроса
+// NewHxUrlPatch -- возвращает новый путь запроса.
 func NewHxUrlPatch(patch string) *HxUrlPatch {
 	Hassert(patch != "", "NewHxUrlPatch(): patch isempty")
 	sf := &HxUrlPatch{
@@ -24,14 +24,14 @@ func NewHxUrlPatch(patch string) *HxUrlPatch {
 	return sf
 }
 
-// Get -- возвращает хранимое значение пути запроса
+// Get -- возвращает хранимое значение пути запроса.
 func (sf *HxUrlPatch) Get() string {
 	sf.RLock()
 	defer sf.RUnlock()
 	return sf.val
 }
 
-// Set -- устанавливает значение пути запроса
+// Set -- устанавливает значение пути запроса.
 func (sf *HxUrlPatch) Set(val string) {
 	sf.Lock()
 	defer sf.Unlock()

+ 10 - 10
v4/lev2/wui/hx_vals/hx_vals.go

@@ -1,21 +1,21 @@
-// package hx_vals -- атрибут HTMX (словарь значений)
+// package hx_vals -- атрибут HTMX (словарь значений).
 package hx_vals
 
 import (
 	"encoding/json"
 	"sync"
 
-	. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// HxVals -- атрибут HTMX (словарь значений)
+// HxVals -- атрибут HTMX (словарь значений).
 type HxVals struct {
 	sync.RWMutex
 	dict map[string]any
 }
 
-// NewHxVals -- возвращает новый словарь значений
+// NewHxVals -- возвращает новый словарь значений.
 func NewHxVals() *HxVals {
 	sf := &HxVals{
 		dict: map[string]any{},
@@ -24,28 +24,28 @@ func NewHxVals() *HxVals {
 	return sf
 }
 
-// Len -- возвращает размер словаря
+// Len -- возвращает размер словаря.
 func (sf *HxVals) Len() int {
 	sf.RLock()
 	defer sf.RUnlock()
 	return len(sf.dict)
 }
 
-// Del -- удаляет ключ словаря
+// Del -- удаляет ключ словаря.
 func (sf *HxVals) Del(key string) {
 	sf.Lock()
 	defer sf.Unlock()
 	delete(sf.dict, key)
 }
 
-// Clear -- очищает словарь значений
+// Clear -- очищает словарь значений.
 func (sf *HxVals) Clear() {
 	sf.Lock()
 	defer sf.Unlock()
 	sf.dict = map[string]any{}
 }
 
-// String -- возвращает строковое представление тэга
+// String -- возвращает строковое представление тэга.
 func (sf *HxVals) String() string {
 	sf.RLock()
 	defer sf.RUnlock()
@@ -53,14 +53,14 @@ func (sf *HxVals) String() string {
 	return `hx-vals='` + string(binJson) + `'`
 }
 
-// Get -- возвращает хранимое значение словарь значений
+// Get -- возвращает хранимое значение словарь значений.
 func (sf *HxVals) Get(key string) any {
 	sf.RLock()
 	defer sf.RUnlock()
 	return sf.dict[key]
 }
 
-// Set -- устанавливает значение словарь значений
+// Set -- устанавливает значение словарь значений.
 func (sf *HxVals) Set(key string, val any) {
 	sf.Lock()
 	defer sf.Unlock()

+ 7 - 7
v4/lev2/wui/wbutton/wbutton.go

@@ -1,4 +1,4 @@
-// package wbutton -- WUI-кнопка
+// package wbutton -- WUI-кнопка.
 package wbutton
 
 import (
@@ -14,7 +14,7 @@ import (
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wwidget"
 )
 
-// WuiButton -- WUI-кнопка
+// WuiButton -- WUI-кнопка.
 type WuiButton struct {
 	IWuiWidget
 	text   IWuiText
@@ -22,7 +22,7 @@ type WuiButton struct {
 	hx     IWuiHx
 }
 
-// NewWuiButton -- возвращает новую WUI-кнопку
+// NewWuiButton -- возвращает новую WUI-кнопку.
 func NewWuiButton(text string, fnBack func(map[string]string) IResult[string]) IResult[*WuiButton] {
 	if fnBack == nil {
 		return NewErr[*WuiButton](fmt.Errorf("NewWuiButton(): text=%q, fnBack==nil", text))
@@ -48,17 +48,17 @@ func NewWuiButton(text string, fnBack func(map[string]string) IResult[string]) I
 	return NewRes(sf)
 }
 
-// Hx -- возвращает атрибуты HTMX
+// Hx -- возвращает атрибуты HTMX.
 func (sf *WuiButton) Hx() IWuiHx {
 	return sf.hx
 }
 
-// Text -- возвращает текст кнопки
+// Text -- возвращает текст кнопки.
 func (sf *WuiButton) Text() IWuiText {
 	return sf.text
 }
 
-// Click -- событие нажатия
+// Click -- событие нажатия.
 func (sf *WuiButton) Click(dict map[string]string) IResult[string] {
 	return sf.fnBack(dict)
 }
@@ -67,7 +67,7 @@ const (
 	strBeg = `<span id="{.id}" class="btn btn-primary" {.hx}>{.txt}</span>`
 )
 
-// Html -- возвращает HTML-представление текста
+// Html -- возвращает HTML-представление текста.
 func (sf *WuiButton) Html() string {
 	strRes := strings.ReplaceAll(strBeg, "{.id}", sf.Id())
 	strRes = strings.ReplaceAll(strRes, "{.txt}", sf.text.Get())

+ 2 - 2
v4/lev2/wui/wbutton/wbutton_test.go

@@ -20,7 +20,7 @@ func TestWuiButton(t *testing.T) {
 	sf.new()
 }
 
-// Создание кнопки
+// Создание кнопки.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	btn := NewWuiButton("test_val", sf.fnBack).Hassert("new()")
@@ -42,7 +42,7 @@ func (sf *tester) new() {
 	}
 }
 
-// Функция обратного вызова
+// Функция обратного вызова.
 func (sf *tester) fnBack(dict map[string]string) IResult[string] {
 	sf.chCall <- "test"
 	return NewRes("test_click")

+ 2 - 2
v4/lev2/wui/wctx/wctx.go

@@ -1,4 +1,4 @@
-// package wctx -- глобальный контекст графики
+// package wctx -- глобальный контекст графики.
 package wctx
 
 import (
@@ -16,7 +16,7 @@ var (
 	block sync.Mutex
 )
 
-// GetWuiCtx -- возвращает глобальный контекст графики
+// GetWuiCtx -- возвращает глобальный контекст графики.
 func GetWuiCtx() IResult[IWuiCtx] {
 	block.Lock()
 	defer block.Unlock()

+ 10 - 10
v4/lev2/wui/whx/whx.go

@@ -1,4 +1,4 @@
-// package whx -- HTMX-атрибуты WUI-объекта
+// package whx -- HTMX-атрибуты WUI-объекта.
 package whx
 
 import (
@@ -11,7 +11,7 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// WuiHx -- HTMX-атрибуты WUI-объекта
+// WuiHx -- HTMX-атрибуты WUI-объекта.
 type WuiHx struct {
 	url     IHxUrl
 	trigger IHxTrigger
@@ -21,7 +21,7 @@ type WuiHx struct {
 	vals    IHxVals
 }
 
-// NewWuiHx -- возвращает новые атрибуты HTMX для WUI-объекта
+// NewWuiHx -- возвращает новые атрибуты HTMX для WUI-объекта.
 func NewWuiHx(path string) *WuiHx {
 	sf := &WuiHx{
 		url:     hx_url.NewHxUrl(path),
@@ -35,7 +35,7 @@ func NewWuiHx(path string) *WuiHx {
 	return sf
 }
 
-// String -- возвращает строку тэгов
+// String -- возвращает строку тэгов.
 func (sf *WuiHx) String() string {
 	strOut := sf.url.String() + " " // Не может быть пустым
 	trig := sf.trigger.Get()
@@ -61,32 +61,32 @@ func (sf *WuiHx) String() string {
 	return strOut
 }
 
-// Vals -- возвращает тэг переменных запроса
+// Vals -- возвращает тэг переменных запроса.
 func (sf *WuiHx) Vals() IHxVals {
 	return sf.vals
 }
 
-// Url -- возвращает тэг URL
+// Url -- возвращает тэг URL.
 func (sf *WuiHx) Url() IHxUrl {
 	return sf.url
 }
 
-// Trigger -- возвращает тэг триггера запроса
+// Trigger -- возвращает тэг триггера запроса.
 func (sf *WuiHx) Trigger() IHxTrigger {
 	return sf.trigger
 }
 
-// Target -- возвращает объект цели замены
+// Target -- возвращает объект цели замены.
 func (sf *WuiHx) Target() IHxTarget {
 	return sf.target
 }
 
-// Oob -- возвращает тэг внеполосной замены
+// Oob -- возвращает тэг внеполосной замены.
 func (sf *WuiHx) Oob() IHxSwapOob {
 	return sf.oob
 }
 
-// Swap -- возвращает тэг замены
+// Swap -- возвращает тэг замены.
 func (sf *WuiHx) Swap() IHxSwap {
 	return sf.swap
 }

+ 2 - 2
v4/lev2/wui/whx/whx_test.go

@@ -17,7 +17,7 @@ func TestWhx(t *testing.T) {
 	sf.str()
 }
 
-// Получает строку атрибутов
+// Получает строку атрибутов.
 func (sf *tester) str() {
 	sf.t.Log("str")
 	sf.hx.Url().Method().Set("hx-put")
@@ -33,7 +33,7 @@ func (sf *tester) str() {
 	}
 }
 
-// Создаёт новые тэги HTMX
+// Создаёт новые тэги HTMX.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	hx := NewWuiHx("/wui/click/123")

+ 5 - 5
v4/lev2/wui/wlabel/wlabel.go

@@ -1,4 +1,4 @@
-// package wlabel -- WUI метка
+// package wlabel -- WUI метка.
 package wlabel
 
 import (
@@ -13,13 +13,13 @@ import (
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wwidget"
 )
 
-// WuiLabel -- WUI метка
+// WuiLabel -- WUI метка.
 type WuiLabel struct {
 	IWuiWidget
 	text IWuiText
 }
 
-// NewWuiLabel -- возвращает новую метку
+// NewWuiLabel -- возвращает новую метку.
 func NewWuiLabel(text string) IResult[*WuiLabel] {
 	sf := &WuiLabel{
 		IWuiWidget: wwidget.NewWuiWidget(),
@@ -40,7 +40,7 @@ func NewWuiLabel(text string) IResult[*WuiLabel] {
 	return NewRes(sf)
 }
 
-// Text -- возвращает текст метки
+// Text -- возвращает текст метки.
 func (sf *WuiLabel) Text() IWuiText {
 	return sf.text
 }
@@ -49,7 +49,7 @@ const (
 	strBeg = `<div id="{.id}">{.txt}</div>`
 )
 
-// Html -- возвращает HTML-представление текста
+// Html -- возвращает HTML-представление текста.
 func (sf *WuiLabel) Html() string {
 	strRes := strings.ReplaceAll(strBeg, "{.id}", sf.Id())
 	strRes = strings.ReplaceAll(strRes, "{.txt}", sf.text.Get())

+ 1 - 1
v4/lev2/wui/wlabel/wlabel_test.go

@@ -15,7 +15,7 @@ func TestWuiLabel(t *testing.T) {
 	sf.new()
 }
 
-// Создаёт новую метку
+// Создаёт новую метку.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	lbl := NewWuiLabel("test_lbl").Hassert("new()")

+ 6 - 6
v4/lev2/wui/wtext/wtext.go

@@ -1,4 +1,4 @@
-// package wtext -- WUI текст
+// package wtext -- WUI текст.
 package wtext
 
 import (
@@ -9,14 +9,14 @@ import (
 	"gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wwidget"
 )
 
-// WuiText -- текст для WUI
+// WuiText -- текст для WUI.
 type WuiText struct {
 	IWuiWidget
 	sync.RWMutex
 	val string
 }
 
-// NewWuiText -- возвращает новый текст WUI
+// NewWuiText -- возвращает новый текст WUI.
 func NewWuiText(val string) *WuiText {
 	sf := &WuiText{
 		IWuiWidget: wwidget.NewWuiWidget(),
@@ -30,7 +30,7 @@ const (
 	strBeg = `<span id="{.id}">{.txt}</span>`
 )
 
-// Html -- возвращает HTML-представление текста
+// Html -- возвращает HTML-представление текста.
 func (sf *WuiText) Html() string {
 	sf.RLock()
 	defer sf.RUnlock()
@@ -39,14 +39,14 @@ func (sf *WuiText) Html() string {
 	return strRes
 }
 
-// Set -- устанавливает хранимое значение
+// Set -- устанавливает хранимое значение.
 func (sf *WuiText) Set(val string) {
 	sf.Lock()
 	defer sf.Unlock()
 	sf.val = val
 }
 
-// Get -- возвращает хранимое значение
+// Get -- возвращает хранимое значение.
 func (sf *WuiText) Get() string {
 	sf.RLock()
 	defer sf.RUnlock()

+ 3 - 3
v4/lev2/wui/wtext/wtext_test.go

@@ -18,7 +18,7 @@ func TestWuiText(t *testing.T) {
 	sf.html()
 }
 
-// Получает HTML
+// Получает HTML.
 func (sf *tester) html() {
 	sf.t.Log("html")
 	if txt := sf.txt.Html(); txt == "" {
@@ -26,7 +26,7 @@ func (sf *tester) html() {
 	}
 }
 
-// Устанавливает новый текст
+// Устанавливает новый текст.
 func (sf *tester) set() {
 	sf.t.Log("set")
 	sf.txt.Set("test_val2")
@@ -35,7 +35,7 @@ func (sf *tester) set() {
 	}
 }
 
-// Получение нового WuiText
+// Получение нового WuiText.
 func (sf *tester) new() {
 	sf.t.Log("new")
 	sf.txt = NewWuiText("test_text")

+ 1 - 1
v4/lev2/wui/wtypes/ihx_swap.go

@@ -46,7 +46,7 @@ package wtypes
 //
 // hx-swap="beforeend scroll:bottom"
 //
-// hx-swap="innerHTML show:top"
+// hx-swap="innerHTML show:top".
 type IHxSwap interface {
 	// Get -- возвращает политику замены элемента
 	Get() string

+ 1 - 1
v4/lev2/wui/wtypes/ihx_swap_oob.go

@@ -18,7 +18,7 @@ package wtypes
 //
 // hx-swap-oob="beforeend:#table2"
 //
-// hx-swap-oob="true"
+// hx-swap-oob="true".
 type IHxSwapOob interface {
 	// Get -- получает атрибут HTMX
 	Get() string

+ 1 - 1
v4/lev2/wui/wtypes/ihx_target.go

@@ -36,7 +36,7 @@ package wtypes
 // В этом примере используется hx-target="this" для создания ссылки,
 // которая обновляется сама по себе при нажатии:
 //
-// <a hx-post="/new-link" hx-target="this" hx-swap="outerHTML">New link</a>
+// <a hx-post="/new-link" hx-target="this" hx-swap="outerHTML">New link</a>.
 type IHxTarget interface {
 	// Set -- устанавливает цель атрибута
 	Set(string)

+ 1 - 1
v4/lev2/wui/wtypes/ihx_trigger.go

@@ -146,7 +146,7 @@ package wtypes
 //
 //	Для относительной позиции:
 //
-// hx-vals='js:{x: event.offsetX, y: event.offsetY}'
+// hx-vals='js:{x: event.offsetX, y: event.offsetY}'.
 type IHxTrigger interface {
 	// Set -- устанавливает триггер атрибута
 	Set(string)

+ 1 - 1
v4/lev2/wui/wtypes/ihx_url.go

@@ -1,6 +1,6 @@
 package wtypes
 
-// IHxUrl -- атрибут метода HTMX
+// IHxUrl -- атрибут метода HTMX.
 type IHxUrl interface {
 	// Method -- возвращает метод атрибута
 	Method() IHxUrlMethod

+ 1 - 1
v4/lev2/wui/wtypes/iwui_ctx.go

@@ -5,7 +5,7 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
 )
 
-// IWuiCtx -- контекст веб-интерфейса
+// IWuiCtx -- контекст веб-интерфейса.
 type IWuiCtx interface {
 	ILocalCtx
 }

+ 1 - 1
v4/lev2/wui/wtypes/iwui_hx.go

@@ -1,6 +1,6 @@
 package wtypes
 
-// IWuiHx -- атрибуты HTMX
+// IWuiHx -- атрибуты HTMX.
 type IWuiHx interface {
 	// Url -- возвращает URL HTMX
 	Url() IHxUrl

+ 1 - 1
v4/lev2/wui/wtypes/iwui_label.go

@@ -1,6 +1,6 @@
 package wtypes
 
-// IWuiLabel -- текстовая метка
+// IWuiLabel -- текстовая метка.
 type IWuiLabel interface {
 	IWuiWidget
 	// Text -- возвращает текст метки

+ 1 - 1
v4/lev2/wui/wtypes/iwui_text.go

@@ -1,6 +1,6 @@
 package wtypes
 
-// IWuiText -- текст WUI
+// IWuiText -- текст WUI.
 type IWuiText interface {
 	IWuiWidget
 	// Get -- возвращает текст

+ 1 - 1
v4/lev2/wui/wtypes/iwui_widget.go

@@ -1,6 +1,6 @@
 package wtypes
 
-// IWuiWidget -- WUI виджет
+// IWuiWidget -- WUI виджет.
 type IWuiWidget interface {
 	// Id -- возвращает Id виджета
 	Id() string

+ 3 - 3
v4/lev2/wui/wui.go

@@ -1,4 +1,4 @@
-// package wui -- пакет веб-интерфейса
+// package wui -- пакет веб-интерфейса.
 package wui
 
 import (
@@ -11,7 +11,7 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// NewWuiButton -- возвращает новую WUI-кнопку
+// NewWuiButton -- возвращает новую WUI-кнопку.
 func NewWuiButton(text string, fnClick func(map[string]string) IResult[string]) IResult[IWuiButton] {
 	resBtn := wbutton.NewWuiButton(text, fnClick)
 	if resBtn.IsErr() {
@@ -21,7 +21,7 @@ func NewWuiButton(text string, fnClick func(map[string]string) IResult[string])
 	return NewRes(IWuiButton(resBtn.Val()))
 }
 
-// GetWuiCtx -- возвращает контекст WUI
+// GetWuiCtx -- возвращает контекст WUI.
 func GetWuiCtx() IResult[IWuiCtx] {
 	resWuiCtx := wctx.GetWuiCtx()
 	if resWuiCtx.IsErr() {

+ 2 - 2
v4/lev2/wui/wui_test.go

@@ -18,7 +18,7 @@ func TestWui(t *testing.T) {
 	sf.get()
 }
 
-// Получает различные WUI-компоненты
+// Получает различные WUI-компоненты.
 func (sf *tester) get() {
 	sf.t.Log("get")
 	wCtx := GetWuiCtx()
@@ -31,7 +31,7 @@ func (sf *tester) get() {
 	}
 }
 
-// Функция обратного вызова
+// Функция обратного вызова.
 func (sf *tester) fnClick(dict map[string]string) IResult[string] {
 	sf.t.Log("fnClick")
 	return NewRes("test_click")

+ 5 - 5
v4/lev2/wui/wwidget/wui_widget.go

@@ -1,4 +1,4 @@
-// package wwidget -- базовый виджет WUI
+// package wwidget -- базовый виджет WUI.
 package wwidget
 
 import (
@@ -8,12 +8,12 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/wui/wtypes"
 )
 
-// WuiWidget -- базовый виджет WUI
+// WuiWidget -- базовый виджет WUI.
 type WuiWidget struct {
 	id string
 }
 
-// NewWuiWidget -- возвращает новый базовый виджет WUI
+// NewWuiWidget -- возвращает новый базовый виджет WUI.
 func NewWuiWidget() *WuiWidget {
 	sf := &WuiWidget{
 		id: "wui_" + rand.Text(),
@@ -22,7 +22,7 @@ func NewWuiWidget() *WuiWidget {
 	return sf
 }
 
-// Id - возвращает ID виджета
+// Id - возвращает ID виджета.
 func (sf *WuiWidget) Id() string {
 	return sf.id
 }
@@ -31,7 +31,7 @@ const (
 	strBeg = `<div id="{.id}"> WuiWidget.Html(): id={.id}, not implemented </div>`
 )
 
-// Html -- возвращает HTML представление виджета
+// Html -- возвращает HTML представление виджета.
 func (sf *WuiWidget) Html() string {
 	strRes := strings.ReplaceAll(strBeg, "{.id}", sf.id)
 	return strRes

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio