@things-factory/headless-twin 10.0.18 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/engine/canonical-ingest.d.ts +10 -1
- package/dist-server/engine/canonical-ingest.js +110 -33
- package/dist-server/engine/canonical-ingest.js.map +1 -1
- package/dist-server/engine/event-time.d.ts +25 -0
- package/dist-server/engine/event-time.js +98 -0
- package/dist-server/engine/event-time.js.map +1 -0
- package/dist-server/engine/index.d.ts +2 -0
- package/dist-server/engine/index.js +23 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/ingest-dedupe.d.ts +18 -6
- package/dist-server/engine/ingest-dedupe.js +201 -15
- package/dist-server/engine/ingest-dedupe.js.map +1 -1
- package/dist-server/engine/ingest-health.d.ts +60 -1
- package/dist-server/engine/ingest-health.js +43 -8
- package/dist-server/engine/ingest-health.js.map +1 -1
- package/dist-server/engine/kpi-fold.d.ts +19 -0
- package/dist-server/engine/kpi-fold.js +4 -1
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.js +23 -3
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/oee-accumulator.d.ts +35 -2
- package/dist-server/engine/oee-accumulator.js +56 -4
- package/dist-server/engine/oee-accumulator.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +72 -3
- package/dist-server/engine/twin-engine.js +118 -5
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/index.js +21 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.d.ts +5 -0
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js +66 -0
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js.map +1 -0
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.d.ts +5 -0
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js +79 -0
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js.map +1 -0
- package/dist-server/migrations/index.js +5 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/routes.js +19 -16
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/actuation/actuation-advisor.d.ts +60 -0
- package/dist-server/service/actuation/actuation-advisor.js +53 -0
- package/dist-server/service/actuation/actuation-advisor.js.map +1 -0
- package/dist-server/service/actuation/actuation-outcome.d.ts +52 -0
- package/dist-server/service/actuation/actuation-outcome.js +34 -0
- package/dist-server/service/actuation/actuation-outcome.js.map +1 -0
- package/dist-server/service/actuation/actuation-rule-resolver.d.ts +19 -0
- package/dist-server/service/actuation/actuation-rule-resolver.js +161 -0
- package/dist-server/service/actuation/actuation-rule-resolver.js.map +1 -0
- package/dist-server/service/actuation/actuation-rule.d.ts +55 -0
- package/dist-server/service/actuation/actuation-rule.js +130 -0
- package/dist-server/service/actuation/actuation-rule.js.map +1 -0
- package/dist-server/service/actuation/approval-gateway.d.ts +20 -0
- package/dist-server/service/actuation/approval-gateway.js +14 -0
- package/dist-server/service/actuation/approval-gateway.js.map +1 -0
- package/dist-server/service/actuation/catch-up-effects.d.ts +16 -0
- package/dist-server/service/actuation/catch-up-effects.js +120 -0
- package/dist-server/service/actuation/catch-up-effects.js.map +1 -0
- package/dist-server/service/actuation/command-dispatcher.d.ts +230 -0
- package/dist-server/service/actuation/command-dispatcher.js +269 -0
- package/dist-server/service/actuation/command-dispatcher.js.map +1 -0
- package/dist-server/service/actuation/command-store.d.ts +11 -0
- package/dist-server/service/actuation/command-store.js +219 -0
- package/dist-server/service/actuation/command-store.js.map +1 -0
- package/dist-server/service/actuation/dispatch-after-approval.d.ts +11 -0
- package/dist-server/service/actuation/dispatch-after-approval.js +73 -0
- package/dist-server/service/actuation/dispatch-after-approval.js.map +1 -0
- package/dist-server/service/actuation/effect-match.d.ts +53 -0
- package/dist-server/service/actuation/effect-match.js +65 -0
- package/dist-server/service/actuation/effect-match.js.map +1 -0
- package/dist-server/service/actuation/index.d.ts +25 -0
- package/dist-server/service/actuation/index.js +30 -0
- package/dist-server/service/actuation/index.js.map +1 -0
- package/dist-server/service/actuation/retry-plan.d.ts +63 -0
- package/dist-server/service/actuation/retry-plan.js +99 -0
- package/dist-server/service/actuation/retry-plan.js.map +1 -0
- package/dist-server/service/actuation/rule-evaluate.d.ts +127 -0
- package/dist-server/service/actuation/rule-evaluate.js +205 -0
- package/dist-server/service/actuation/rule-evaluate.js.map +1 -0
- package/dist-server/service/actuation/rule-loop.d.ts +5 -0
- package/dist-server/service/actuation/rule-loop.js +156 -0
- package/dist-server/service/actuation/rule-loop.js.map +1 -0
- package/dist-server/service/actuation/rule-runner.d.ts +52 -0
- package/dist-server/service/actuation/rule-runner.js +135 -0
- package/dist-server/service/actuation/rule-runner.js.map +1 -0
- package/dist-server/service/actuation/rule-wiring.d.ts +24 -0
- package/dist-server/service/actuation/rule-wiring.js +80 -0
- package/dist-server/service/actuation/rule-wiring.js.map +1 -0
- package/dist-server/service/actuation/settle-effects.d.ts +21 -0
- package/dist-server/service/actuation/settle-effects.js +101 -0
- package/dist-server/service/actuation/settle-effects.js.map +1 -0
- package/dist-server/service/actuation/sweep-dispatch.d.ts +37 -0
- package/dist-server/service/actuation/sweep-dispatch.js +125 -0
- package/dist-server/service/actuation/sweep-dispatch.js.map +1 -0
- package/dist-server/service/actuation/twin-command-resolver.d.ts +120 -0
- package/dist-server/service/actuation/twin-command-resolver.js +406 -0
- package/dist-server/service/actuation/twin-command-resolver.js.map +1 -0
- package/dist-server/service/actuation/twin-command.d.ts +111 -0
- package/dist-server/service/actuation/twin-command.js +170 -0
- package/dist-server/service/actuation/twin-command.js.map +1 -0
- package/dist-server/service/actuation/twin-now.d.ts +41 -0
- package/dist-server/service/actuation/twin-now.js +65 -0
- package/dist-server/service/actuation/twin-now.js.map +1 -0
- package/dist-server/service/index.d.ts +5 -2
- package/dist-server/service/index.js +28 -17
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/actuation-routing.d.ts +60 -0
- package/dist-server/service/reference/actuation-routing.js +126 -0
- package/dist-server/service/reference/actuation-routing.js.map +1 -0
- package/dist-server/service/reference/actuation-target.d.ts +65 -0
- package/dist-server/service/reference/actuation-target.js +94 -0
- package/dist-server/service/reference/actuation-target.js.map +1 -0
- package/dist-server/service/reference/connection-portability-resolver.d.ts +29 -0
- package/dist-server/service/reference/connection-portability-resolver.js +181 -0
- package/dist-server/service/reference/connection-portability-resolver.js.map +1 -0
- package/dist-server/service/reference/connection-portability.d.ts +138 -0
- package/dist-server/service/reference/connection-portability.js +230 -0
- package/dist-server/service/reference/connection-portability.js.map +1 -0
- package/dist-server/service/reference/fill-cadence.d.ts +17 -0
- package/dist-server/service/reference/fill-cadence.js +31 -0
- package/dist-server/service/reference/fill-cadence.js.map +1 -0
- package/dist-server/service/reference/fill-dedupe.d.ts +16 -0
- package/dist-server/service/reference/fill-dedupe.js +63 -0
- package/dist-server/service/reference/fill-dedupe.js.map +1 -0
- package/dist-server/service/reference/fill-loop.d.ts +35 -0
- package/dist-server/service/reference/fill-loop.js +187 -0
- package/dist-server/service/reference/fill-loop.js.map +1 -0
- package/dist-server/service/reference/fill-span.d.ts +10 -0
- package/dist-server/service/reference/fill-span.js +37 -0
- package/dist-server/service/reference/fill-span.js.map +1 -0
- package/dist-server/service/reference/hook-contract.d.ts +149 -22
- package/dist-server/service/reference/hook-contract.js +236 -23
- package/dist-server/service/reference/hook-contract.js.map +1 -1
- package/dist-server/service/reference/hook-rejected.d.ts +25 -0
- package/dist-server/service/reference/hook-rejected.js +48 -0
- package/dist-server/service/reference/hook-rejected.js.map +1 -0
- package/dist-server/service/reference/hook-store.d.ts +3 -0
- package/dist-server/service/reference/hook-store.js +28 -0
- package/dist-server/service/reference/hook-store.js.map +1 -0
- package/dist-server/service/reference/index.d.ts +4 -1
- package/dist-server/service/reference/index.js +5 -1
- package/dist-server/service/reference/index.js.map +1 -1
- package/dist-server/service/reference/live-cadence.d.ts +68 -0
- package/dist-server/service/reference/live-cadence.js +44 -0
- package/dist-server/service/reference/live-cadence.js.map +1 -0
- package/dist-server/service/reference/live-feed-lease.d.ts +15 -0
- package/dist-server/service/reference/live-feed-lease.js +56 -0
- package/dist-server/service/reference/live-feed-lease.js.map +1 -0
- package/dist-server/service/reference/live-ingest.d.ts +21 -0
- package/dist-server/service/reference/live-ingest.js +66 -0
- package/dist-server/service/reference/live-ingest.js.map +1 -0
- package/dist-server/service/reference/reference-adapter.d.ts +355 -2
- package/dist-server/service/reference/reference-adapter.js +19 -1
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-fill-run.d.ts +50 -0
- package/dist-server/service/reference/reference-fill-run.js +142 -0
- package/dist-server/service/reference/reference-fill-run.js.map +1 -0
- package/dist-server/service/reference/reference-fill.d.ts +90 -0
- package/dist-server/service/reference/reference-fill.js +141 -0
- package/dist-server/service/reference/reference-fill.js.map +1 -0
- package/dist-server/service/reference/reference-hook.d.ts +64 -6
- package/dist-server/service/reference/reference-hook.js +265 -27
- package/dist-server/service/reference/reference-hook.js.map +1 -1
- package/dist-server/service/reference/reference-link-groups.d.ts +10 -0
- package/dist-server/service/reference/reference-link-groups.js +15 -0
- package/dist-server/service/reference/reference-link-groups.js.map +1 -0
- package/dist-server/service/reference/reference-links.d.ts +14 -0
- package/dist-server/service/reference/reference-links.js +73 -0
- package/dist-server/service/reference/reference-links.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +1 -0
- package/dist-server/service/reference/reference-live.js +32 -6
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +56 -2
- package/dist-server/service/reference/reference-master.js +33 -3
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +27 -0
- package/dist-server/service/reference/reference-resolver.js +79 -1
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +31 -3
- package/dist-server/service/twin-event/twin-event-keys.js +36 -4
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +1 -0
- package/dist-server/service/twin-event/twin-event.js +11 -2
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-model/choose-recipe.d.ts +15 -0
- package/dist-server/service/twin-model/choose-recipe.js +33 -0
- package/dist-server/service/twin-model/choose-recipe.js.map +1 -0
- package/dist-server/service/twin-model/external-resolver.d.ts +76 -0
- package/dist-server/service/twin-model/external-resolver.js +109 -0
- package/dist-server/service/twin-model/external-resolver.js.map +1 -0
- package/dist-server/service/twin-model/project-structure.js +16 -3
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/twin-equipment.d.ts +17 -0
- package/dist-server/service/twin-model/twin-equipment.js +10 -0
- package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
- package/dist-server/service/twin-model/twin-lineage-query.js +75 -1
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.js +94 -5
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
- package/dist-server/service/twin-space/twin-space.d.ts +29 -0
- package/dist-server/service/twin-space/twin-space.js +13 -0
- package/dist-server/service/twin-space/twin-space.js.map +1 -1
- package/dist-server/service/twin-subject/event-subjects.d.ts +3 -1
- package/dist-server/service/twin-subject/event-subjects.js +8 -1
- package/dist-server/service/twin-subject/event-subjects.js.map +1 -1
- package/dist-shared/entity-delta.js +1 -1
- package/dist-shared/entity-delta.js.map +1 -1
- package/package.json +7 -7
- package/server/engine/canonical-ingest.ts +119 -33
- package/server/engine/event-time.ts +96 -0
- package/server/engine/index.ts +20 -0
- package/server/engine/ingest-dedupe.ts +203 -27
- package/server/engine/ingest-health.ts +91 -6
- package/server/engine/kpi-fold.ts +24 -1
- package/server/engine/kpi-query.ts +23 -4
- package/server/engine/oee-accumulator.ts +80 -8
- package/server/engine/twin-engine.ts +119 -5
- package/server/index.ts +23 -0
- package/server/migrations/1786600000000-RenameOperatoMesAdapterType.ts +66 -0
- package/server/migrations/1786700000000-AddDispatchRetryColumns.ts +82 -0
- package/server/migrations/index.ts +5 -1
- package/server/routes.ts +19 -20
- package/server/service/actuation/actuation-advisor.ts +124 -0
- package/server/service/actuation/actuation-outcome.ts +97 -0
- package/server/service/actuation/actuation-rule-resolver.ts +147 -0
- package/server/service/actuation/actuation-rule.ts +139 -0
- package/server/service/actuation/approval-gateway.ts +44 -0
- package/server/service/actuation/catch-up-effects.ts +152 -0
- package/server/service/actuation/command-dispatcher.ts +511 -0
- package/server/service/actuation/command-store.ts +236 -0
- package/server/service/actuation/dispatch-after-approval.ts +71 -0
- package/server/service/actuation/effect-match.ts +124 -0
- package/server/service/actuation/index.ts +27 -0
- package/server/service/actuation/retry-plan.ts +145 -0
- package/server/service/actuation/rule-evaluate.ts +352 -0
- package/server/service/actuation/rule-loop.ts +160 -0
- package/server/service/actuation/rule-runner.ts +216 -0
- package/server/service/actuation/rule-wiring.ts +84 -0
- package/server/service/actuation/settle-effects.ts +115 -0
- package/server/service/actuation/sweep-dispatch.ts +179 -0
- package/server/service/actuation/twin-command-resolver.ts +387 -0
- package/server/service/actuation/twin-command.ts +232 -0
- package/server/service/actuation/twin-now.ts +97 -0
- package/server/service/index.ts +11 -0
- package/server/service/reference/actuation-routing.ts +165 -0
- package/server/service/reference/actuation-target.ts +163 -0
- package/server/service/reference/connection-portability-resolver.ts +171 -0
- package/server/service/reference/connection-portability.ts +285 -0
- package/server/service/reference/fill-cadence.ts +29 -0
- package/server/service/reference/fill-dedupe.ts +66 -0
- package/server/service/reference/fill-loop.ts +153 -0
- package/server/service/reference/fill-span.ts +31 -0
- package/server/service/reference/hook-contract.ts +280 -22
- package/server/service/reference/hook-rejected.ts +53 -0
- package/server/service/reference/hook-store.ts +28 -0
- package/server/service/reference/index.ts +5 -1
- package/server/service/reference/live-cadence.ts +129 -0
- package/server/service/reference/live-feed-lease.ts +56 -0
- package/server/service/reference/live-ingest.ts +94 -0
- package/server/service/reference/reference-adapter.ts +392 -5
- package/server/service/reference/reference-fill-run.ts +165 -0
- package/server/service/reference/reference-fill.ts +223 -0
- package/server/service/reference/reference-hook.ts +355 -32
- package/server/service/reference/reference-link-groups.ts +21 -0
- package/server/service/reference/reference-links.ts +77 -0
- package/server/service/reference/reference-live.ts +30 -5
- package/server/service/reference/reference-master.ts +90 -5
- package/server/service/reference/reference-resolver.ts +77 -2
- package/server/service/twin-event/twin-event-keys.ts +37 -4
- package/server/service/twin-event/twin-event.ts +18 -1
- package/server/service/twin-model/choose-recipe.ts +31 -0
- package/server/service/twin-model/external-resolver.ts +161 -0
- package/server/service/twin-model/project-structure.ts +16 -3
- package/server/service/twin-model/twin-equipment.ts +22 -0
- package/server/service/twin-model/twin-lineage-query.ts +82 -1
- package/server/service/twin-model/twin-model-item-query.ts +95 -4
- package/server/service/twin-model/twin-model-tree-query.ts +1 -1
- package/server/service/twin-space/twin-space.ts +39 -0
- package/server/service/twin-subject/event-subjects.ts +8 -1
- package/shared/entity-delta.ts +1 -1
- package/test/ack-shape-alignment.test.ts +97 -0
- package/test/actuation-approval-door.test.ts +421 -0
- package/test/actuation-dispatch.test.ts +209 -0
- package/test/actuation-effect.test.ts +162 -0
- package/test/actuation-outcome.test.ts +152 -0
- package/test/actuation-rule-runner.test.ts +281 -0
- package/test/actuation-rule.test.ts +266 -0
- package/test/actuation-seam.test.ts +249 -0
- package/test/actuation-settle.test.ts +164 -0
- package/test/actuation-target.test.ts +145 -0
- package/test/canonical-ingest-vocabularies.test.ts +49 -2
- package/test/capability-mapping.test.ts +1 -1
- package/test/choose-recipe.test.ts +117 -0
- package/test/command-store-writes-columns.test.ts +126 -0
- package/test/connection-portability-doors.test.ts +87 -0
- package/test/connector-capability-declaration.test.ts +54 -4
- package/test/declaration-reaches-model.test.ts +43 -0
- package/test/dedupe-transformation.test.ts +112 -0
- package/test/entity-delta.test.ts +1 -1
- package/test/event-subjects.test.ts +2 -1
- package/test/external-resolver.test.ts +171 -0
- package/test/fill-dedupe.test.ts +59 -0
- package/test/fill-loop.test.ts +35 -0
- package/test/first-envelope.test.ts +153 -0
- package/test/fold-shift-dst.test.ts +110 -0
- package/test/home-in-area.test.ts +90 -0
- package/test/hook-rejected-shape.test.ts +86 -0
- package/test/hook-response-sum.test.ts +193 -0
- package/test/hook-sequence.test.ts +333 -0
- package/test/ingest-dedupe.test.ts +56 -0
- package/test/ingest-expected-quiet.test.ts +111 -0
- package/test/ingest-idempotent.test.ts +79 -0
- package/test/ingest-reconcile-four-ways.test.ts +97 -0
- package/test/ingest-rules-header.test.ts +100 -0
- package/test/ingest-window-rolls-unobserved.test.ts +72 -0
- package/test/intake-mapping-parity.test.ts +175 -0
- package/test/live-cadence.test.ts +170 -0
- package/test/live-feed-lease.test.ts +63 -0
- package/test/live-mirror-parity.test.ts +1 -1
- package/test/oee-accumulator.test.ts +3 -3
- package/test/oee-site-calendar.test.ts +146 -0
- package/test/order-and-transaction-split.test.ts +107 -0
- package/test/reference-fill.test.ts +235 -0
- package/test/reference-hook.test.ts +114 -23
- package/test/reference-links.test.ts +56 -0
- package/test/retry-plan.test.ts +139 -0
- package/test/sweep-dispatch.test.ts +191 -0
- package/test/twin-event-keys.test.ts +20 -6
- package/test/twin-model-item-db.test.ts +6 -6
- package/test/twin-model-tree-db.test.ts +6 -6
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FILL_PERIOD_MS = exports.FILL_DEBOUNCE_MS = void 0;
|
|
37
|
+
exports.fillOnce = fillOnce;
|
|
38
|
+
exports.requestFill = requestFill;
|
|
39
|
+
exports.fillAllOnce = fillAllOnce;
|
|
40
|
+
exports.startFillLoop = startFillLoop;
|
|
41
|
+
exports.stopFillLoop = stopFillLoop;
|
|
42
|
+
/*
|
|
43
|
+
* **빠진 구간을 언제 다시 받나** — 세 자리에서 부른다.
|
|
44
|
+
*
|
|
45
|
+
* ── 왜 세 자리인가 (인티그레이션 레인의 측정 권고, 2026-09-05) ──────────────
|
|
46
|
+
* ```
|
|
47
|
+
* 1 재기동 직후 한 번 우리가 내려가 있는 동안 그쪽이 보낸 것을 못 받았다
|
|
48
|
+
* 2 구멍을 본 그 자리 번호가 비었다는 것을 아는 순간이 가장 이르다
|
|
49
|
+
* 3 10분 그물 위 둘이 다 놓치는 경우 — 그쪽이 아예 안 보낸 구간
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* **30초가 아니다.** 그 권고에 근거가 있었다 — backfill 은 원본에 조회를 걸고 저널을 읽는다.
|
|
53
|
+
* 30초마다 돌면 얻는 것 없이 두 쪽을 계속 친다. 놓친 구간은 10분 뒤에 받아도 사실이 달라지지 않는다.
|
|
54
|
+
*
|
|
55
|
+
* 규칙 주기(`rule-loop.ts`, 30초)에 얹지 않는 이유가 그것이다. 얹으면 간격이 그쪽에 묶이고, 나중에
|
|
56
|
+
* 규칙 주기를 바꾸면 이쪽이 소리 없이 따라간다.
|
|
57
|
+
*
|
|
58
|
+
* ── 겹쳐 돌지 않는다 ────────────────────────────────────────────────────────
|
|
59
|
+
* 한 바퀴가 간격보다 오래 걸리면 다음 발화가 겹친다. 겹치면 두 바퀴가 같은 구간을 받고, 그 순간
|
|
60
|
+
* 중복 판정이 두 벌의 기억을 갖는다.
|
|
61
|
+
*
|
|
62
|
+
* ── 도는 트윈만 본다 ────────────────────────────────────────────────────────
|
|
63
|
+
* 멈춘 트윈에는 사실을 넣을 커널이 없다. `runFill` 이 그것을 다시 확인하지만 여기서 미리 걸러
|
|
64
|
+
* 헛조회를 안 만든다.
|
|
65
|
+
*/
|
|
66
|
+
const log_js_1 = require("../../engine/log.js");
|
|
67
|
+
const twin_engine_js_1 = require("../../engine/twin-engine.js");
|
|
68
|
+
const reference_fill_run_js_1 = require("./reference-fill-run.js");
|
|
69
|
+
/* 간격은 가벼운 파일에 있다 — 테스트가 붙게(§`fill-cadence.ts`). 부르는 쪽이 한 곳에서 가져가게 다시 내보낸다. */
|
|
70
|
+
var fill_cadence_js_1 = require("./fill-cadence.js");
|
|
71
|
+
Object.defineProperty(exports, "FILL_DEBOUNCE_MS", { enumerable: true, get: function () { return fill_cadence_js_1.FILL_DEBOUNCE_MS; } });
|
|
72
|
+
Object.defineProperty(exports, "FILL_PERIOD_MS", { enumerable: true, get: function () { return fill_cadence_js_1.FILL_PERIOD_MS; } });
|
|
73
|
+
const fill_cadence_js_2 = require("./fill-cadence.js");
|
|
74
|
+
/** 어느 단위를 언제 마지막으로 받았나 — `domainId::instanceId::scope`. */
|
|
75
|
+
const lastRun = new Map();
|
|
76
|
+
let timer;
|
|
77
|
+
let running = false;
|
|
78
|
+
function recently(key, now) {
|
|
79
|
+
const last = lastRun.get(key);
|
|
80
|
+
return last !== undefined && now - last < fill_cadence_js_2.FILL_DEBOUNCE_MS;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 그 단위를 한 번 받는다 — **커서부터 이어서.**
|
|
84
|
+
*
|
|
85
|
+
* `from` 을 주지 않는다. 밑에서부터 다시 받는 것은 사람이 정하는 일이고(`fillTwinScope(from:)`),
|
|
86
|
+
* 주기가 스스로 할 일이 아니다 — 그것을 주기로 돌리면 매번 원본의 전 구간을 훑는다.
|
|
87
|
+
*/
|
|
88
|
+
async function fillOnce(domainId, instanceId, scope) {
|
|
89
|
+
const key = `${domainId}::${instanceId}::${scope}`;
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
if (recently(key, now))
|
|
92
|
+
return undefined;
|
|
93
|
+
lastRun.set(key, now);
|
|
94
|
+
const out = await (0, reference_fill_run_js_1.runFill)({ domainId, instanceId, scope });
|
|
95
|
+
/*
|
|
96
|
+
* 받은 것이 있을 때만 말한다. 「받을 것이 없었다」를 10분마다 적으면 로그가 그 줄로 차고, 그러면
|
|
97
|
+
* 진짜 받은 날을 못 본다.
|
|
98
|
+
*/
|
|
99
|
+
if (out.applied)
|
|
100
|
+
(0, log_js_1.twinLog)(`[twin-fill] "${instanceId}" · ${scope} — 빠진 구간에서 ${out.applied}건을 받았습니다`);
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* **구멍을 본 자리에서 부른다** — 기다리지 않는다.
|
|
105
|
+
*
|
|
106
|
+
* 훅의 응답이 이것 때문에 늦으면 보내는 쪽이 되풀이하고, 그러면 같은 봉투가 두 번 온다. 조치를
|
|
107
|
+
* 잇는 자리(`settleEffects`)와 같은 규율이다.
|
|
108
|
+
*/
|
|
109
|
+
function requestFill(domainId, instanceId, scope) {
|
|
110
|
+
void fillOnce(domainId, instanceId, scope).catch(err => (0, log_js_1.twinWarn)(`[twin-fill] "${instanceId}" · ${scope} 를 받지 못했습니다 — ${err?.message ?? err}`));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 도는 트윈을 한 바퀴 돈다.
|
|
114
|
+
*
|
|
115
|
+
* **어느 단위를 받을지는 커서가 말한다.** 커서에 적힌 흐름이 그동안 받아 온 것들이고, 그것이
|
|
116
|
+
* 없으면 아직 아무것도 안 받은 연결이라 받을 구간도 없다.
|
|
117
|
+
*/
|
|
118
|
+
async function fillAllOnce() {
|
|
119
|
+
let tried = 0;
|
|
120
|
+
let applied = 0;
|
|
121
|
+
for (const { domainId, instanceId } of twin_engine_js_1.TwinEngine.ownedTwins()) {
|
|
122
|
+
for (const scope of await scopesOf(domainId, instanceId)) {
|
|
123
|
+
tried++;
|
|
124
|
+
const out = await fillOnce(domainId, instanceId, scope);
|
|
125
|
+
applied += out?.applied ?? 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { tried, applied };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 그 연결이 받아 온 단위들 — **커서에 적힌 것.**
|
|
132
|
+
*
|
|
133
|
+
* 커서 열쇠는 `instanceId::scope` 다(§`pushCursorKey`). 여기서 그 규칙을 다시 적지 않고 앞부분을
|
|
134
|
+
* 떼어 낸다 — 두 곳이 같은 규칙을 가지면 어긋나는 날 이 주기가 엉뚱한 단위를 받는다.
|
|
135
|
+
*/
|
|
136
|
+
async function scopesOf(domainId, instanceId) {
|
|
137
|
+
const { getRepository } = await Promise.resolve().then(() => __importStar(require('@things-factory/shell')));
|
|
138
|
+
const { TwinReference } = await Promise.resolve().then(() => __importStar(require('./twin-reference.js')));
|
|
139
|
+
const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } });
|
|
140
|
+
const streams = ref?.liveCursor?.streams;
|
|
141
|
+
if (!streams || typeof streams !== 'object')
|
|
142
|
+
return [];
|
|
143
|
+
const prefix = `${instanceId}::`;
|
|
144
|
+
return Object.keys(streams)
|
|
145
|
+
.filter(k => k.startsWith(prefix))
|
|
146
|
+
.map(k => k.slice(prefix.length))
|
|
147
|
+
.filter(s => s);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 주기를 세운다 — 두 번 불러도 하나만 선다.
|
|
151
|
+
*
|
|
152
|
+
* **기동에서 한 번 먼저 돈다**(§1). 우리가 내려가 있는 동안 그쪽이 보낸 것을 그때 받는다. 첫 바퀴를
|
|
153
|
+
* 10분 뒤로 미루면 그 10분 동안 화면이 빈 구간을 그대로 보인다.
|
|
154
|
+
*/
|
|
155
|
+
function startFillLoop(periodMs = fill_cadence_js_2.FILL_PERIOD_MS) {
|
|
156
|
+
if (timer)
|
|
157
|
+
return;
|
|
158
|
+
const turn = () => {
|
|
159
|
+
if (running)
|
|
160
|
+
return;
|
|
161
|
+
running = true;
|
|
162
|
+
fillAllOnce()
|
|
163
|
+
.then(({ tried, applied }) => {
|
|
164
|
+
if (applied)
|
|
165
|
+
(0, log_js_1.twinLog)(`[twin-fill] 한 바퀴 — ${tried}개 단위에서 ${applied}건을 받았습니다`);
|
|
166
|
+
})
|
|
167
|
+
.catch(err => (0, log_js_1.twinError)(`[twin-fill] 주기가 실패했습니다 — ${err?.message ?? err}`))
|
|
168
|
+
.finally(() => {
|
|
169
|
+
running = false;
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
timer = setInterval(turn, periodMs);
|
|
173
|
+
/*
|
|
174
|
+
* 기동 직후 한 번. 조금 늦춰서 돈다 — 커널이 되살아나기 전에 부르면 도는 트윈이 0개라 아무것도
|
|
175
|
+
* 안 받고, 그러면 그 한 번이 없는 것과 같다.
|
|
176
|
+
*/
|
|
177
|
+
setTimeout(turn, 15_000).unref?.();
|
|
178
|
+
}
|
|
179
|
+
/** 주기를 내린다 — 테스트가 재기동을 넣을 수 있게. */
|
|
180
|
+
function stopFillLoop() {
|
|
181
|
+
if (timer)
|
|
182
|
+
clearInterval(timer);
|
|
183
|
+
timer = undefined;
|
|
184
|
+
running = false;
|
|
185
|
+
lastRun.clear();
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=fill-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill-loop.js","sourceRoot":"","sources":["../../../server/service/reference/fill-loop.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * **빠진 구간을 언제 다시 받나** — 세 자리에서 부른다.\n *\n * ── 왜 세 자리인가 (인티그레이션 레인의 측정 권고, 2026-09-05) ──────────────\n * ```\n * 1 재기동 직후 한 번 우리가 내려가 있는 동안 그쪽이 보낸 것을 못 받았다\n * 2 구멍을 본 그 자리 번호가 비었다는 것을 아는 순간이 가장 이르다\n * 3 10분 그물 위 둘이 다 놓치는 경우 — 그쪽이 아예 안 보낸 구간\n * ```\n *\n * **30초가 아니다.** 그 권고에 근거가 있었다 — backfill 은 원본에 조회를 걸고 저널을 읽는다.\n * 30초마다 돌면 얻는 것 없이 두 쪽을 계속 친다. 놓친 구간은 10분 뒤에 받아도 사실이 달라지지 않는다.\n *\n * 규칙 주기(`rule-loop.ts`, 30초)에 얹지 않는 이유가 그것이다. 얹으면 간격이 그쪽에 묶이고, 나중에\n * 규칙 주기를 바꾸면 이쪽이 소리 없이 따라간다.\n *\n * ── 겹쳐 돌지 않는다 ────────────────────────────────────────────────────────\n * 한 바퀴가 간격보다 오래 걸리면 다음 발화가 겹친다. 겹치면 두 바퀴가 같은 구간을 받고, 그 순간\n * 중복 판정이 두 벌의 기억을 갖는다.\n *\n * ── 도는 트윈만 본다 ────────────────────────────────────────────────────────\n * 멈춘 트윈에는 사실을 넣을 커널이 없다. `runFill` 이 그것을 다시 확인하지만 여기서 미리 걸러\n * 헛조회를 안 만든다.\n */\nimport { twinError, twinLog, twinWarn } from '../../engine/log.js'\nimport { TwinEngine } from '../../engine/twin-engine.js'\n\nimport { runFill, type FillRunResult } from './reference-fill-run.js'\n\n/* 간격은 가벼운 파일에 있다 — 테스트가 붙게(§`fill-cadence.ts`). 부르는 쪽이 한 곳에서 가져가게 다시 내보낸다. */\nexport { FILL_DEBOUNCE_MS, FILL_PERIOD_MS } from './fill-cadence.js'\nimport { FILL_DEBOUNCE_MS, FILL_PERIOD_MS } from './fill-cadence.js'\n\n/** 어느 단위를 언제 마지막으로 받았나 — `domainId::instanceId::scope`. */\nconst lastRun = new Map<string, number>()\n\nlet timer: any\nlet running = false\n\nfunction recently(key: string, now: number): boolean {\n const last = lastRun.get(key)\n return last !== undefined && now - last < FILL_DEBOUNCE_MS\n}\n\n/**\n * 그 단위를 한 번 받는다 — **커서부터 이어서.**\n *\n * `from` 을 주지 않는다. 밑에서부터 다시 받는 것은 사람이 정하는 일이고(`fillTwinScope(from:)`),\n * 주기가 스스로 할 일이 아니다 — 그것을 주기로 돌리면 매번 원본의 전 구간을 훑는다.\n */\nexport async function fillOnce(domainId: string, instanceId: string, scope: string): Promise<FillRunResult | undefined> {\n const key = `${domainId}::${instanceId}::${scope}`\n const now = Date.now()\n if (recently(key, now)) return undefined\n lastRun.set(key, now)\n\n const out = await runFill({ domainId, instanceId, scope })\n /*\n * 받은 것이 있을 때만 말한다. 「받을 것이 없었다」를 10분마다 적으면 로그가 그 줄로 차고, 그러면\n * 진짜 받은 날을 못 본다.\n */\n if (out.applied) twinLog(`[twin-fill] \"${instanceId}\" · ${scope} — 빠진 구간에서 ${out.applied}건을 받았습니다`)\n return out\n}\n\n/**\n * **구멍을 본 자리에서 부른다** — 기다리지 않는다.\n *\n * 훅의 응답이 이것 때문에 늦으면 보내는 쪽이 되풀이하고, 그러면 같은 봉투가 두 번 온다. 조치를\n * 잇는 자리(`settleEffects`)와 같은 규율이다.\n */\nexport function requestFill(domainId: string, instanceId: string, scope: string): void {\n void fillOnce(domainId, instanceId, scope).catch(err =>\n twinWarn(`[twin-fill] \"${instanceId}\" · ${scope} 를 받지 못했습니다 — ${err?.message ?? err}`)\n )\n}\n\n/**\n * 도는 트윈을 한 바퀴 돈다.\n *\n * **어느 단위를 받을지는 커서가 말한다.** 커서에 적힌 흐름이 그동안 받아 온 것들이고, 그것이\n * 없으면 아직 아무것도 안 받은 연결이라 받을 구간도 없다.\n */\nexport async function fillAllOnce(): Promise<{ tried: number; applied: number }> {\n let tried = 0\n let applied = 0\n for (const { domainId, instanceId } of TwinEngine.ownedTwins()) {\n for (const scope of await scopesOf(domainId, instanceId)) {\n tried++\n const out = await fillOnce(domainId, instanceId, scope)\n applied += out?.applied ?? 0\n }\n }\n return { tried, applied }\n}\n\n/**\n * 그 연결이 받아 온 단위들 — **커서에 적힌 것.**\n *\n * 커서 열쇠는 `instanceId::scope` 다(§`pushCursorKey`). 여기서 그 규칙을 다시 적지 않고 앞부분을\n * 떼어 낸다 — 두 곳이 같은 규칙을 가지면 어긋나는 날 이 주기가 엉뚱한 단위를 받는다.\n */\nasync function scopesOf(domainId: string, instanceId: string): Promise<string[]> {\n const { getRepository } = await import('@things-factory/shell')\n const { TwinReference } = await import('./twin-reference.js')\n const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } })\n const streams = (ref as any)?.liveCursor?.streams\n if (!streams || typeof streams !== 'object') return []\n\n const prefix = `${instanceId}::`\n return Object.keys(streams)\n .filter(k => k.startsWith(prefix))\n .map(k => k.slice(prefix.length))\n .filter(s => s)\n}\n\n/**\n * 주기를 세운다 — 두 번 불러도 하나만 선다.\n *\n * **기동에서 한 번 먼저 돈다**(§1). 우리가 내려가 있는 동안 그쪽이 보낸 것을 그때 받는다. 첫 바퀴를\n * 10분 뒤로 미루면 그 10분 동안 화면이 빈 구간을 그대로 보인다.\n */\nexport function startFillLoop(periodMs = FILL_PERIOD_MS): void {\n if (timer) return\n\n const turn = () => {\n if (running) return\n running = true\n fillAllOnce()\n .then(({ tried, applied }) => {\n if (applied) twinLog(`[twin-fill] 한 바퀴 — ${tried}개 단위에서 ${applied}건을 받았습니다`)\n })\n .catch(err => twinError(`[twin-fill] 주기가 실패했습니다 — ${err?.message ?? err}`))\n .finally(() => {\n running = false\n })\n }\n\n timer = setInterval(turn, periodMs)\n /*\n * 기동 직후 한 번. 조금 늦춰서 돈다 — 커널이 되살아나기 전에 부르면 도는 트윈이 0개라 아무것도\n * 안 받고, 그러면 그 한 번이 없는 것과 같다.\n */\n setTimeout(turn, 15_000).unref?.()\n}\n\n/** 주기를 내린다 — 테스트가 재기동을 넣을 수 있게. */\nexport function stopFillLoop(): void {\n if (timer) clearInterval(timer)\n timer = undefined\n running = false\n lastRun.clear()\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 이 레코드들이 덮는 시각 구간 — 하나도 시각을 말하지 않으면 `undefined`.
|
|
3
|
+
*
|
|
4
|
+
* **구간을 못 정하면 채우지 않는다.** 저널 전체를 읽는 쪽으로 넓히지 않는다 — 그것이 필요한
|
|
5
|
+
* 상황이면 조회가 아니라 unique index 로 풀어야 한다.
|
|
6
|
+
*/
|
|
7
|
+
export declare function spanOf(records: readonly unknown[]): {
|
|
8
|
+
from: Date;
|
|
9
|
+
to: Date;
|
|
10
|
+
} | undefined;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.spanOf = spanOf;
|
|
4
|
+
/*
|
|
5
|
+
* backfill 이 읽을 저널 구간을 정하는 판정.
|
|
6
|
+
*
|
|
7
|
+
* 조회 코드(`fill-dedupe.ts`)와 나눠 둔다 — 그쪽은 TypeORM entity 를 물어서 테스트 프로세스가
|
|
8
|
+
* 적재할 수 없다. 판정만 따로 두면 테스트가 가벼운 import 로 확인할 수 있다.
|
|
9
|
+
*/
|
|
10
|
+
const event_time_js_1 = require("../../engine/event-time.js");
|
|
11
|
+
/**
|
|
12
|
+
* 이 레코드들이 덮는 시각 구간 — 하나도 시각을 말하지 않으면 `undefined`.
|
|
13
|
+
*
|
|
14
|
+
* **구간을 못 정하면 채우지 않는다.** 저널 전체를 읽는 쪽으로 넓히지 않는다 — 그것이 필요한
|
|
15
|
+
* 상황이면 조회가 아니라 unique index 로 풀어야 한다.
|
|
16
|
+
*/
|
|
17
|
+
function spanOf(records) {
|
|
18
|
+
let lo = Number.POSITIVE_INFINITY;
|
|
19
|
+
let hi = Number.NEGATIVE_INFINITY;
|
|
20
|
+
for (const r of records) {
|
|
21
|
+
const t = Date.parse(String((0, event_time_js_1.factEventTime)(r) ?? ''));
|
|
22
|
+
if (!Number.isFinite(t))
|
|
23
|
+
continue;
|
|
24
|
+
if (t < lo)
|
|
25
|
+
lo = t;
|
|
26
|
+
if (t > hi)
|
|
27
|
+
hi = t;
|
|
28
|
+
}
|
|
29
|
+
if (!Number.isFinite(lo))
|
|
30
|
+
return undefined;
|
|
31
|
+
/*
|
|
32
|
+
* 양쪽을 1초씩 넓힌다. 저장된 시각은 밀리초까지 있고 경계에서 같은 값이 부동소수로 갈릴 수 있다 —
|
|
33
|
+
* 넓게 읽는 것은 기억이 조금 더 차는 것뿐이라 손해가 없고, 좁게 읽으면 그 사실이 다시 앉는다.
|
|
34
|
+
*/
|
|
35
|
+
return { from: new Date(lo - 1000), to: new Date(hi + 1000) };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=fill-span.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill-span.js","sourceRoot":"","sources":["../../../server/service/reference/fill-span.ts"],"names":[],"mappings":";;AAcA,wBAeC;AA7BD;;;;;GAKG;AACH,8DAA0D;AAE1D;;;;;GAKG;AACH,SAAgB,MAAM,CAAC,OAA2B;IAChD,IAAI,EAAE,GAAG,MAAM,CAAC,iBAAiB,CAAA;IACjC,IAAI,EAAE,GAAG,MAAM,CAAC,iBAAiB,CAAA;IACjC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAA,6BAAa,EAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QACpD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAQ;QACjC,IAAI,CAAC,GAAG,EAAE;YAAE,EAAE,GAAG,CAAC,CAAA;QAClB,IAAI,CAAC,GAAG,EAAE;YAAE,EAAE,GAAG,CAAC,CAAA;IACpB,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAA;IAC1C;;;OAGG;IACH,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAA;AAC/D,CAAC","sourcesContent":["/*\n * backfill 이 읽을 저널 구간을 정하는 판정.\n *\n * 조회 코드(`fill-dedupe.ts`)와 나눠 둔다 — 그쪽은 TypeORM entity 를 물어서 테스트 프로세스가\n * 적재할 수 없다. 판정만 따로 두면 테스트가 가벼운 import 로 확인할 수 있다.\n */\nimport { factEventTime } from '../../engine/event-time.js'\n\n/**\n * 이 레코드들이 덮는 시각 구간 — 하나도 시각을 말하지 않으면 `undefined`.\n *\n * **구간을 못 정하면 채우지 않는다.** 저널 전체를 읽는 쪽으로 넓히지 않는다 — 그것이 필요한\n * 상황이면 조회가 아니라 unique index 로 풀어야 한다.\n */\nexport function spanOf(records: readonly unknown[]): { from: Date; to: Date } | undefined {\n let lo = Number.POSITIVE_INFINITY\n let hi = Number.NEGATIVE_INFINITY\n for (const r of records) {\n const t = Date.parse(String(factEventTime(r) ?? ''))\n if (!Number.isFinite(t)) continue\n if (t < lo) lo = t\n if (t > hi) hi = t\n }\n if (!Number.isFinite(lo)) return undefined\n /*\n * 양쪽을 1초씩 넓힌다. 저장된 시각은 밀리초까지 있고 경계에서 같은 값이 부동소수로 갈릴 수 있다 —\n * 넓게 읽는 것은 기억이 조금 더 차는 것뿐이라 손해가 없고, 좁게 읽으면 그 사실이 다시 앉는다.\n */\n return { from: new Date(lo - 1000), to: new Date(hi + 1000) }\n}\n\n"]}
|
|
@@ -1,31 +1,22 @@
|
|
|
1
|
+
import { type WebhookResponse } from '@operato/ops-contract';
|
|
1
2
|
/** 훅 요청의 결과 — 상태 코드와 본문을 함께 정한다(부르는 쪽이 그대로 답한다). */
|
|
2
3
|
export interface HookOutcome {
|
|
3
4
|
status: number;
|
|
4
|
-
body:
|
|
5
|
+
body: WebhookResponse;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
|
-
* 응답 코드 규약 —
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* 500 우리 쪽 오류 재시도하면 될 수 있다
|
|
8
|
+
* 응답 코드 규약 — **계약이 정하고 여기서는 그것을 쓴다**(2026-08-31).
|
|
9
|
+
*
|
|
10
|
+
* 예전에는 이 표가 여기 있었고 보내는 쪽(operato-plant)에 또 있었다. 그래서 같은 409 를 양쪽이 다른
|
|
11
|
+
* 뜻으로 썼다 — 여기서는 「그 트윈이 실시간으로 돌지 않는다」, 저쪽에서는 「번호가 비었다」였다.
|
|
12
|
+
* 붙였다면 트윈이 안 도는 동안 보내는 쪽이 같은 구간을 계속 다시 보냈다.
|
|
13
|
+
*
|
|
14
|
+
* 판정표는 만드는 쪽과 읽는 쪽이 합의해야 하는 것이라 계약 층에 있다(`@operato/ops-contract`).
|
|
15
|
+
* `notLive` 가 409 에서 503 으로 옮겨진 이유도 그 파일에 적혀 있다.
|
|
16
|
+
*
|
|
17
|
+
* **여기서 이름을 바꿔 다시 내보내지 않는다.** 부르는 쪽은 `WEBHOOK_STATUS` 를 계약에서 바로 가져온다.
|
|
18
|
+
* 별명을 두면 같은 표에 이름이 둘이 되고, 다음 사람이 어느 쪽이 정본인지 물어야 한다.
|
|
19
19
|
*/
|
|
20
|
-
export declare const HOOK_STATUS: {
|
|
21
|
-
readonly ok: 200;
|
|
22
|
-
readonly badSecret: 401;
|
|
23
|
-
readonly unknownTarget: 404;
|
|
24
|
-
readonly badPayload: 400;
|
|
25
|
-
readonly notLive: 409;
|
|
26
|
-
readonly unsupported: 501;
|
|
27
|
-
readonly failed: 500;
|
|
28
|
-
};
|
|
29
20
|
/** 헤더에서 비밀값을 꺼낸다 — 두 가지 방식을 받는다(밀어 주는 쪽의 관습이 갈린다). */
|
|
30
21
|
export declare function secretFromHeaders(headers: Record<string, unknown> | undefined): string;
|
|
31
22
|
/**
|
|
@@ -35,6 +26,142 @@ export declare function secretFromHeaders(headers: Record<string, unknown> | und
|
|
|
35
26
|
* 있습니다. 그래서 같은 길이면 전부를 비교합니다.
|
|
36
27
|
*/
|
|
37
28
|
export declare function secretMatches(given: string, expected: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* 인증 — **서명이 왔으면 서명을 보고, 아니면 비밀값을 본다.**
|
|
31
|
+
*
|
|
32
|
+
* 두 방식을 두는 이유는 밀어 주는 쪽의 사정이 다르기 때문이다. 이미 붙어 있는 커넥터는 비밀값을 헤더에
|
|
33
|
+
* 싣는 방식으로 만들어졌고, 새로 만드는 것(operato-plant)은 서명을 쓴다. 서명 쪽이 낫다 — 비밀값을 그대로
|
|
34
|
+
* 싣는 방식은 그 요청을 그대로 다시 보내는 것을 막지 못한다.
|
|
35
|
+
*
|
|
36
|
+
* **서명이 왔는데 확인할 수 없으면 거절한다.** 받은 바이트를 들고 있지 않으면(`rawBody` 가 없으면)
|
|
37
|
+
* 파싱한 객체를 다시 문자열로 만들어 확인하고 싶어지는데, 키 순서와 공백이 달라져 어차피 맞지 않는다.
|
|
38
|
+
* 맞지 않는 것을 통과시키면 서명이 있으나 마나 한 것이 된다.
|
|
39
|
+
*/
|
|
40
|
+
export declare function authorizeHook(args: {
|
|
41
|
+
headers: Record<string, unknown> | undefined;
|
|
42
|
+
/** 받은 바이트 그대로. 서명을 쓰지 않는 연결에서는 없어도 된다. */
|
|
43
|
+
rawBody: string | undefined;
|
|
44
|
+
/** 연결 설정에 저장된 비밀값. 서명과 비밀값이 같은 값을 쓴다. */
|
|
45
|
+
secret: string;
|
|
46
|
+
nowMs: number;
|
|
47
|
+
}): {
|
|
48
|
+
ok: true;
|
|
49
|
+
method: 'signature' | 'secret';
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
reason: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* 커서에 적을 열쇠 — **트윈까지 넣는다.**
|
|
56
|
+
*
|
|
57
|
+
* ── 왜 (2026-08-31, MES 레인 질문에서 나옴) ───────────────────────────────
|
|
58
|
+
* 커서는 **연결마다** 한 행(`TwinReference.liveCursor`)에 산다. 그런데 연결 하나가 트윈 여럿을
|
|
59
|
+
* 만든다(`scopeSpec.produced` 가 목록이다). 보내는 쪽의 단위 이름만으로 열쇠를 잡으면, 같은 연결의
|
|
60
|
+
* 두 공장이 같은 이름(`EVENTS`)을 쓸 때 **한 계수기를 함께 쓴다.**
|
|
61
|
+
*
|
|
62
|
+
* 그러면 A 공장이 5까지 올린 뒤 B 공장이 1을 보내면 이미 본 번호가 되어 버려지고, A 가 6을 보내면
|
|
63
|
+
* B 의 번호와 어긋나 끊임없이 「빠졌다」가 된다. 같은 부류가 이 저장소에서 이미 났다 — 두 공장의
|
|
64
|
+
* 같은 번호가 한 자리에 겹쳤다.
|
|
65
|
+
*
|
|
66
|
+
* 보내는 쪽에 트윈 이름을 넣으라고 하지 않는다. 어디로 갈지는 주소가 이미 말했고, 그것을 본문에도
|
|
67
|
+
* 적으라고 하면 둘이 어긋나는 날 어느 쪽을 믿을지 정해야 한다.
|
|
68
|
+
*/
|
|
69
|
+
export declare function pushCursorKey(instanceId: string, scope: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* 커서에서 그 단위의 마지막 번호를 꺼낸다.
|
|
72
|
+
*
|
|
73
|
+
* 커서 모양은 `{ streams: { [흐름]: { since?, seen[], seq? } } }` 이고 **흐름 이름은 어댑터가 정한다**
|
|
74
|
+
* (§`TwinReference.liveCursor`). 밀어 주는 연결에서는 그 이름이 곧 번호를 매기는 단위다.
|
|
75
|
+
*
|
|
76
|
+
* 자리를 따로 만들지 않는 이유는 「재기동을 넘어 사는 진행 위치」가 두 벌이 되기 때문이다. 폴링이 쓰는
|
|
77
|
+
* `since` 와 밀어 주기가 쓰는 `seq` 는 둘 다 「이 흐름을 어디까지 읽었나」이고 원본이 되풀어 줄 수 있다.
|
|
78
|
+
*/
|
|
79
|
+
export declare function seqOf(cursor: unknown, scope: string): number | undefined;
|
|
80
|
+
/** 그 단위의 번호만 바꾼 커서를 만든다 — 다른 흐름과 `since`·`seen` 은 그대로 둔다. */
|
|
81
|
+
export declare function withSeq(cursor: unknown, scope: string, seq: number, firstSeq?: number): Record<string, unknown>;
|
|
82
|
+
/**
|
|
83
|
+
* 이 연결이 이 단위를 **몇 번부터 받기 시작했나** — 적힌 적이 없으면 `undefined`.
|
|
84
|
+
*
|
|
85
|
+
* 「앞이 비었나」를 답하려면 이 값이 있어야 한다. 없으면 **모른다고 말해야 한다** — 0 으로 답하면
|
|
86
|
+
* 1번부터 다 받은 것으로 읽힌다.
|
|
87
|
+
*/
|
|
88
|
+
export declare function firstSeqOf(cursor: unknown, scope: string): number | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* **번호대로 받을 수 있는 데까지 자른다** — 밀어 받는 길과 backfill 하는 길이 이 함수 하나를 지난다.
|
|
91
|
+
*
|
|
92
|
+
* ── 왜 갈라 두나 (2026-08-31) ────────────────────────────────────────────
|
|
93
|
+
* 이 판정이 훅 안에 박혀 있었다. backfill(`fetchSince`)이 같은 판정을 해야 하는데, 거기서 다시 만들면
|
|
94
|
+
* **두 길이 다른 규칙으로 받는다** — 한쪽만 고쳐지는 날이 오고, 어긋난 그 길에서만 사실이 사라진다.
|
|
95
|
+
*
|
|
96
|
+
* 번호 확인을 커넥터가 아니라 프레임워크로 가져온 것과 같은 이유다. 자리가 하나여야 한다.
|
|
97
|
+
*
|
|
98
|
+
* ── 첫 구멍에서 멈춘다 ───────────────────────────────────────────────────
|
|
99
|
+
* 구멍 뒤의 봉투를 받아들이면 그 구멍이 영영 메워지지 않는다. 보내는 쪽은 `expectedSeq` 부터 다시
|
|
100
|
+
* 보내고, 뒤엣것은 그때 함께 온다.
|
|
101
|
+
*/
|
|
102
|
+
export interface SequencedTake {
|
|
103
|
+
/** 받아들인 레코드 — 구멍 앞까지다. */
|
|
104
|
+
records: unknown[];
|
|
105
|
+
/** 번호를 확인했나. 번호를 매기지 않는 원본은 거짓이고, 그 사실을 응답에 남긴다. */
|
|
106
|
+
numbered: boolean;
|
|
107
|
+
/** 커서에 적을 번호 — 받은 것이 없으면 없다. */
|
|
108
|
+
advancedSeq?: number;
|
|
109
|
+
/** 구멍이 났다 — 그 앞부터 다시 받아야 한다. */
|
|
110
|
+
gap?: {
|
|
111
|
+
expectedSeq: number;
|
|
112
|
+
lastSeq: number;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* 이 단위의 **첫 봉투였다** — 그 앞으로 몇 개를 못 봤나.
|
|
116
|
+
*
|
|
117
|
+
* ── 왜 이 칸이 생겼나 (2026-09-06 실측) ──────────────────────────────────
|
|
118
|
+
* plant 아웃박스의 1~20 이 400 으로 거절돼 그 연결에 커서가 없었다. 다음에 온 21번이 첫 봉투로
|
|
119
|
+
* 앉으며 **커서가 21 로 뛰었다.** 커서가 0 이었다면 같은 21이 구멍이었다.
|
|
120
|
+
*
|
|
121
|
+
* 스무 건이 없는데 구멍으로도 안 세어지고, 경고도 없고, backfill 대상도 아니었다 — backfill 은 커서부터
|
|
122
|
+
* 묻는다. 그 사실이 아무 데도 안 남았다.
|
|
123
|
+
*
|
|
124
|
+
* **손실이라고 말하지 않는다.** 원본이 오래 돌고 있는데 트윈을 나중에 붙이면 그 아래는 애초에
|
|
125
|
+
* 안 받기로 한 것이다. 계약은 둘을 구별할 수 없으므로 수만 낸다.
|
|
126
|
+
*
|
|
127
|
+
* 0 이면 1번부터 시작한 것이라 이 칸을 내지 않는다 — 말할 것이 없다.
|
|
128
|
+
*/
|
|
129
|
+
firstUnseenBefore?: number;
|
|
130
|
+
/**
|
|
131
|
+
* 이 묶음의 첫 봉투가 **이 단위의 첫 봉투였다** — 그 번호.
|
|
132
|
+
*
|
|
133
|
+
* `firstUnseenBefore` 와 다르다: 이쪽은 1번부터 시작한 연결에도 있다(값 1). 커서에 적어 두면
|
|
134
|
+
* 나중에 「앞이 비었나」를 답할 수 있다.
|
|
135
|
+
*/
|
|
136
|
+
firstSeq?: number;
|
|
137
|
+
/**
|
|
138
|
+
* 이 묶음에서 **가장 낮게 받아들인 번호** — 하나도 못 받았으면 없다.
|
|
139
|
+
*
|
|
140
|
+
* `firstSeq` 와 다르다. `firstSeq` 는 「이 단위의 첫 봉투였다」일 때만 있고, 이쪽은 언제나 있다.
|
|
141
|
+
*
|
|
142
|
+
* 밑에서부터 다시 받을 때 이것이 필요하다. `from: 0` 으로 부르면 커서가 0 이므로 seq 1 은
|
|
143
|
+
* `next` 판정이고 `firstSeq` 가 안 생긴다 — 그런데 우리는 실제로 1번부터 갖게 된다. 그 사실을
|
|
144
|
+
* 적어야 「앞이 비었나」에 답할 수 있다.
|
|
145
|
+
*/
|
|
146
|
+
lowestSeq?: number;
|
|
147
|
+
/**
|
|
148
|
+
* 받을 수 없는 묶음 — 그 이유.
|
|
149
|
+
*
|
|
150
|
+
* 번호가 있는 봉투와 없는 봉투가 섞였거나 번호가 정수가 아니다. 둘 다 보내는 쪽의 결함이고,
|
|
151
|
+
* **반쯤 확인한 것을 확인했다고 말하지 않는다.**
|
|
152
|
+
*/
|
|
153
|
+
refused?: string;
|
|
154
|
+
}
|
|
155
|
+
export declare function takeInSequence(args: {
|
|
156
|
+
items: {
|
|
157
|
+
record: unknown;
|
|
158
|
+
seq?: number;
|
|
159
|
+
at?: string;
|
|
160
|
+
}[];
|
|
161
|
+
scope?: string;
|
|
162
|
+
/** 이 단위에서 마지막으로 받아들인 번호. 받은 적이 없으면 `undefined`. */
|
|
163
|
+
lastSeq?: number;
|
|
164
|
+
}): SequencedTake;
|
|
38
165
|
/** 이 연결이 이 트윈을 만들었나 — 남의 트윈에 밀어 넣지 못하게. */
|
|
39
166
|
export declare function producedInstance(ref: {
|
|
40
167
|
scopeSpec?: any;
|