@things-factory/headless-twin 10.1.8 → 10.1.10
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/migrations/1786800000000-CreateTwinReferenceReconciliations.d.ts +5 -0
- package/dist-server/migrations/1786800000000-CreateTwinReferenceReconciliations.js +44 -0
- package/dist-server/migrations/1786800000000-CreateTwinReferenceReconciliations.js.map +1 -0
- package/dist-server/migrations/index.js +3 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/service/index.d.ts +4 -2
- package/dist-server/service/index.js +2 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/index.d.ts +5 -2
- package/dist-server/service/reference/index.js +5 -2
- package/dist-server/service/reference/index.js.map +1 -1
- package/dist-server/service/reference/reference-fill-run.js +13 -0
- package/dist-server/service/reference/reference-fill-run.js.map +1 -1
- package/dist-server/service/reference/reference-reconciliation-query.d.ts +5 -0
- package/dist-server/service/reference/reference-reconciliation-query.js +42 -0
- package/dist-server/service/reference/reference-reconciliation-query.js.map +1 -0
- package/dist-server/service/reference/reference-reconciliation-writer.d.ts +10 -0
- package/dist-server/service/reference/reference-reconciliation-writer.js +33 -0
- package/dist-server/service/reference/reference-reconciliation-writer.js.map +1 -0
- package/dist-server/service/reference/reference-reconciliation.d.ts +19 -0
- package/dist-server/service/reference/reference-reconciliation.js +33 -0
- package/dist-server/service/reference/reference-reconciliation.js.map +1 -0
- package/dist-server/service/reference/twin-reference-reconciliation.d.ts +19 -0
- package/dist-server/service/reference/twin-reference-reconciliation.js +95 -0
- package/dist-server/service/reference/twin-reference-reconciliation.js.map +1 -0
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +9 -0
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +41 -0
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/package.json +2 -2
- package/server/migrations/1786800000000-CreateTwinReferenceReconciliations.ts +39 -0
- package/server/migrations/index.ts +3 -1
- package/server/service/index.ts +2 -0
- package/server/service/reference/index.ts +5 -2
- package/server/service/reference/reference-fill-run.ts +15 -1
- package/server/service/reference/reference-reconciliation-query.ts +28 -0
- package/server/service/reference/reference-reconciliation-writer.ts +38 -0
- package/server/service/reference/reference-reconciliation.ts +55 -0
- package/server/service/reference/twin-reference-reconciliation.ts +27 -0
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +38 -0
- package/test/reference-reconciliation.test.ts +39 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreateTwinReferenceReconciliations1786800000000 = void 0;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
5
|
+
/* 번호 대사는 원본 범위를 실제로 읽은 뒤의 최신 상태를 재기동 뒤에도 보존한다. */
|
|
6
|
+
const TABLE = 'twin_reference_reconciliations';
|
|
7
|
+
class CreateTwinReferenceReconciliations1786800000000 {
|
|
8
|
+
async up(queryRunner) {
|
|
9
|
+
if (await queryRunner.hasTable(TABLE))
|
|
10
|
+
return;
|
|
11
|
+
const dateType = queryRunner.connection.driver.mappedDataTypes.createDate;
|
|
12
|
+
await queryRunner.createTable(new typeorm_1.Table({
|
|
13
|
+
name: TABLE,
|
|
14
|
+
columns: [
|
|
15
|
+
{ name: 'id', type: 'varchar', length: '36', isPrimary: true },
|
|
16
|
+
{ name: 'domain_id', type: 'varchar', length: '36', isNullable: true },
|
|
17
|
+
{ name: 'instance_id', type: 'varchar', isNullable: false },
|
|
18
|
+
{ name: 'scope', type: 'varchar', isNullable: false },
|
|
19
|
+
{ name: 'status', type: 'varchar', isNullable: false },
|
|
20
|
+
{ name: 'source_first', type: 'int', isNullable: true },
|
|
21
|
+
{ name: 'source_last', type: 'int', isNullable: true },
|
|
22
|
+
{ name: 'cursor_seq', type: 'int', isNullable: true },
|
|
23
|
+
{ name: 'first_seq_seen', type: 'int', isNullable: true },
|
|
24
|
+
{ name: 'head_missing', type: 'int', isNullable: true },
|
|
25
|
+
{ name: 'behind', type: 'int', isNullable: true },
|
|
26
|
+
{ name: 'reason', type: 'varchar', isNullable: true },
|
|
27
|
+
{ name: 'checked_at', type: dateType, isNullable: false },
|
|
28
|
+
{ name: 'created_at', type: dateType, isNullable: true },
|
|
29
|
+
{ name: 'updated_at', type: dateType, isNullable: true }
|
|
30
|
+
]
|
|
31
|
+
}), true);
|
|
32
|
+
const table = await queryRunner.getTable(TABLE);
|
|
33
|
+
if (!table)
|
|
34
|
+
return;
|
|
35
|
+
await queryRunner.createIndex(table, new typeorm_1.TableIndex({ name: 'uq_twin_reference_reconciliation_0', columnNames: ['domain_id', 'instance_id', 'scope'], isUnique: true }));
|
|
36
|
+
await queryRunner.createIndex(table, new typeorm_1.TableIndex({ name: 'ix_twin_reference_reconciliation_0', columnNames: ['domain_id', 'instance_id', 'checked_at'] }));
|
|
37
|
+
}
|
|
38
|
+
async down(queryRunner) {
|
|
39
|
+
if (await queryRunner.hasTable(TABLE))
|
|
40
|
+
await queryRunner.dropTable(TABLE);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.CreateTwinReferenceReconciliations1786800000000 = CreateTwinReferenceReconciliations1786800000000;
|
|
44
|
+
//# sourceMappingURL=1786800000000-CreateTwinReferenceReconciliations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1786800000000-CreateTwinReferenceReconciliations.js","sourceRoot":"","sources":["../../server/migrations/1786800000000-CreateTwinReferenceReconciliations.ts"],"names":[],"mappings":"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","sourcesContent":["import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm'\n\n/* 번호 대사는 원본 범위를 실제로 읽은 뒤의 최신 상태를 재기동 뒤에도 보존한다. */\nconst TABLE = 'twin_reference_reconciliations'\n\nexport class CreateTwinReferenceReconciliations1786800000000 implements MigrationInterface {\n public async up(queryRunner: QueryRunner): Promise<void> {\n if (await queryRunner.hasTable(TABLE)) return\n const dateType = queryRunner.connection.driver.mappedDataTypes.createDate as string\n await queryRunner.createTable(new Table({\n name: TABLE,\n columns: [\n { name: 'id', type: 'varchar', length: '36', isPrimary: true },\n { name: 'domain_id', type: 'varchar', length: '36', isNullable: true },\n { name: 'instance_id', type: 'varchar', isNullable: false },\n { name: 'scope', type: 'varchar', isNullable: false },\n { name: 'status', type: 'varchar', isNullable: false },\n { name: 'source_first', type: 'int', isNullable: true },\n { name: 'source_last', type: 'int', isNullable: true },\n { name: 'cursor_seq', type: 'int', isNullable: true },\n { name: 'first_seq_seen', type: 'int', isNullable: true },\n { name: 'head_missing', type: 'int', isNullable: true },\n { name: 'behind', type: 'int', isNullable: true },\n { name: 'reason', type: 'varchar', isNullable: true },\n { name: 'checked_at', type: dateType, isNullable: false },\n { name: 'created_at', type: dateType, isNullable: true },\n { name: 'updated_at', type: dateType, isNullable: true }\n ]\n }), true)\n const table = await queryRunner.getTable(TABLE)\n if (!table) return\n await queryRunner.createIndex(table, new TableIndex({ name: 'uq_twin_reference_reconciliation_0', columnNames: ['domain_id', 'instance_id', 'scope'], isUnique: true }))\n await queryRunner.createIndex(table, new TableIndex({ name: 'ix_twin_reference_reconciliation_0', columnNames: ['domain_id', 'instance_id', 'checked_at'] }))\n }\n\n public async down(queryRunner: QueryRunner): Promise<void> {\n if (await queryRunner.hasTable(TABLE)) await queryRunner.dropTable(TABLE)\n }\n}\n"]}
|
|
@@ -12,6 +12,7 @@ const _1786300000000_IndexEventTypeByTime_js_1 = require("./1786300000000-IndexE
|
|
|
12
12
|
const _1786400000000_IndexEventCreatedAt_js_1 = require("./1786400000000-IndexEventCreatedAt.js");
|
|
13
13
|
const _1786500000000_CreateTwinSubjectEvents_js_1 = require("./1786500000000-CreateTwinSubjectEvents.js");
|
|
14
14
|
const _1786700000000_AddDispatchRetryColumns_js_1 = require("./1786700000000-AddDispatchRetryColumns.js");
|
|
15
|
+
const _1786800000000_CreateTwinReferenceReconciliations_js_1 = require("./1786800000000-CreateTwinReferenceReconciliations.js");
|
|
15
16
|
const _1786600000000_RenameOperatoMesAdapterType_js_1 = require("./1786600000000-RenameOperatoMesAdapterType.js");
|
|
16
17
|
exports.migrations = [
|
|
17
18
|
_1786000000000_RenameTwinInstanceBoardToModel_js_1.RenameTwinInstanceBoardToModel1786000000000,
|
|
@@ -21,6 +22,7 @@ exports.migrations = [
|
|
|
21
22
|
_1786400000000_IndexEventCreatedAt_js_1.IndexEventCreatedAt1786400000000,
|
|
22
23
|
_1786500000000_CreateTwinSubjectEvents_js_1.CreateTwinSubjectEvents1786500000000,
|
|
23
24
|
_1786600000000_RenameOperatoMesAdapterType_js_1.RenameOperatoMesAdapterType1786600000000,
|
|
24
|
-
_1786700000000_AddDispatchRetryColumns_js_1.AddDispatchRetryColumns1786700000000
|
|
25
|
+
_1786700000000_AddDispatchRetryColumns_js_1.AddDispatchRetryColumns1786700000000,
|
|
26
|
+
_1786800000000_CreateTwinReferenceReconciliations_js_1.CreateTwinReferenceReconciliations1786800000000
|
|
25
27
|
];
|
|
26
28
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/migrations/index.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,wHAA+G;AAC/G,8HAAqH;AACrH,kGAAyF;AACzF,oGAA2F;AAC3F,kGAAyF;AACzF,0GAAiG;AACjG,0GAAiG;AACjG,kHAAyG;AAE5F,QAAA,UAAU,GAAG;IACxB,8FAA2C;IAC3C,oGAA8C;IAC9C,wEAAgC;IAChC,0EAAiC;IACjC,wEAAgC;IAChC,gFAAoC;IACpC,wFAAwC;IACxC,gFAAoC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/migrations/index.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,wHAA+G;AAC/G,8HAAqH;AACrH,kGAAyF;AACzF,oGAA2F;AAC3F,kGAAyF;AACzF,0GAAiG;AACjG,0GAAiG;AACjG,gIAAuH;AACvH,kHAAyG;AAE5F,QAAA,UAAU,GAAG;IACxB,8FAA2C;IAC3C,oGAA8C;IAC9C,wEAAgC;IAChC,0EAAiC;IACjC,wEAAgC;IAChC,gFAAoC;IACpC,wFAAwC;IACxC,gFAAoC;IACpC,sGAA+C;CAChD,CAAA","sourcesContent":["/*\n * 마이그레이션 묶음 — 프레임워크 규약(`@things-factory/shell` · `auth-base` 와 같은 방식).\n * 파일마다 하나씩 export 하고 여기서 모아 `server/index.ts` 가 내보낸다.\n */\nimport { RenameTwinInstanceBoardToModel1786000000000 } from './1786000000000-RenameTwinInstanceBoardToModel.js'\nimport { PromoteEventActionAndSyncWarnings1786100000000 } from './1786100000000-PromoteEventActionAndSyncWarnings.js'\nimport { CarryLiveFeedCursor1786200000000 } from './1786200000000-CarryLiveFeedCursor.js'\nimport { IndexEventTypeByTime1786300000000 } from './1786300000000-IndexEventTypeByTime.js'\nimport { IndexEventCreatedAt1786400000000 } from './1786400000000-IndexEventCreatedAt.js'\nimport { CreateTwinSubjectEvents1786500000000 } from './1786500000000-CreateTwinSubjectEvents.js'\nimport { AddDispatchRetryColumns1786700000000 } from './1786700000000-AddDispatchRetryColumns.js'\nimport { CreateTwinReferenceReconciliations1786800000000 } from './1786800000000-CreateTwinReferenceReconciliations.js'\nimport { RenameOperatoMesAdapterType1786600000000 } from './1786600000000-RenameOperatoMesAdapterType.js'\n\nexport const migrations = [\n RenameTwinInstanceBoardToModel1786000000000,\n PromoteEventActionAndSyncWarnings1786100000000,\n CarryLiveFeedCursor1786200000000,\n IndexEventTypeByTime1786300000000,\n IndexEventCreatedAt1786400000000,\n CreateTwinSubjectEvents1786500000000,\n RenameOperatoMesAdapterType1786600000000,\n AddDispatchRetryColumns1786700000000,\n CreateTwinReferenceReconciliations1786800000000\n]\n"]}
|
|
@@ -14,16 +14,18 @@ export * from './reference/reference-adapter.js';
|
|
|
14
14
|
export * from './reference/live-cadence.js';
|
|
15
15
|
export * from './reference/reference-live.js';
|
|
16
16
|
export * from './reference/twin-reference.js';
|
|
17
|
+
export * from './reference/twin-reference-reconciliation.js';
|
|
18
|
+
export * from './reference/reference-reconciliation.js';
|
|
17
19
|
export * from './reference/connection-portability.js';
|
|
18
20
|
export * from './reference/template-registry.js';
|
|
19
21
|
export * from './reference/reference-probe.js';
|
|
20
22
|
export * from './reference/reference-assessment.js';
|
|
21
23
|
export * from './twin-lifecycle/domain-catalog.js';
|
|
22
24
|
export * from './actuation/index.js';
|
|
23
|
-
export declare const entities: (typeof import("./twin-event/twin-event.js").TwinEvent | typeof import("./twin-subject/twin-subject-event.js").TwinSubjectEvent | typeof import("./twin-instance/twin-instance.js").TwinInstance | typeof import("./reference/twin-reference.js").TwinReference | typeof import("./twin-structure/twin-structure.js").TwinStructure | typeof import("./twin-space/twin-space.js").TwinSpace | typeof import("./twin-space/twin-space-representation.js").TwinSpaceRepresentation | typeof import("./twin-space/twin-space-area.js").TwinSpaceArea | typeof import("./twin-space/twin-area.js").TwinArea | typeof import("./twin-model/twin-location.js").TwinLocation | typeof import("./twin-model/twin-equipment.js").TwinEquipment | typeof import("./twin-model/twin-operation.js").TwinOperation | typeof import("./twin-target/twin-target.js").TwinTarget | typeof import("./twin-audit/twin-audit-event.js").TwinAuditEvent | typeof import("./twin-ingest-window/twin-ingest-window.js").TwinIngestWindow | typeof import("./actuation/twin-command.js").TwinCommandRecord | typeof import("./actuation/actuation-rule.js").TwinActuationRule)[];
|
|
25
|
+
export declare const entities: (typeof import("./twin-event/twin-event.js").TwinEvent | typeof import("./twin-subject/twin-subject-event.js").TwinSubjectEvent | typeof import("./twin-instance/twin-instance.js").TwinInstance | typeof import("./reference/twin-reference.js").TwinReference | typeof import("./twin-structure/twin-structure.js").TwinStructure | typeof import("./twin-space/twin-space.js").TwinSpace | typeof import("./twin-space/twin-space-representation.js").TwinSpaceRepresentation | typeof import("./twin-space/twin-space-area.js").TwinSpaceArea | typeof import("./twin-space/twin-area.js").TwinArea | typeof import("./twin-model/twin-location.js").TwinLocation | typeof import("./twin-model/twin-equipment.js").TwinEquipment | typeof import("./twin-model/twin-operation.js").TwinOperation | typeof import("./twin-target/twin-target.js").TwinTarget | typeof import("./twin-audit/twin-audit-event.js").TwinAuditEvent | typeof import("./twin-ingest-window/twin-ingest-window.js").TwinIngestWindow | typeof import("./actuation/twin-command.js").TwinCommandRecord | typeof import("./reference/twin-reference-reconciliation.js").TwinReferenceReconciliation | typeof import("./actuation/actuation-rule.js").TwinActuationRule)[];
|
|
24
26
|
export declare const subscribers: any[];
|
|
25
27
|
export declare const schema: {
|
|
26
28
|
typeDefs: {};
|
|
27
|
-
resolverClasses: (typeof import("./twin-state/twin-state-subscription.js").TwinStateSubscription | typeof import("./actuation/actuation-rule-resolver.js").TwinActuationRuleResolver | typeof import("./actuation/twin-command-resolver.js").TwinCommandResolver | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectQuery | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectMutation | typeof import("./twin-audit/twin-audit-query.js").TwinAuditQuery | typeof import("./twin-space/twin-space-resolver.js").TwinSpaceResolver | typeof import("./twin-target/twin-target-resolver.js").TwinTargetResolver | typeof import("./reference/connection-portability-resolver.js").ConnectionPortabilityResolver | typeof import("./reference/reference-resolver.js").TwinReferenceResolver | typeof import("./reference/reference-progress-subscription.js").TwinReferenceProgressSubscription | typeof import("./twin-control/twin-control-mutation.js").TwinControlMutation | typeof import("./twin-model/twin-model-query.js").TwinModelQuery | typeof import("./twin-model/twin-model-item-query.js").TwinModelItemQuery | typeof import("./twin-model/twin-model-tree-query.js").TwinModelTreeQuery | typeof import("./twin-model/twin-model-mutation.js").TwinModelMutation | typeof import("./twin-model/twin-lineage-query.js").TwinLineageQuery | typeof import("./twin-ingest-window/twin-ingest-window-query.js").TwinIngestWindowQuery | typeof import("./twin-lifecycle/twin-lifecycle-mutation.js").TwinLifecycleMutation | typeof import("./twin-journal/twin-journal-query.js").TwinJournalQuery | typeof import("./twin-forecast/twin-forecast-query.js").TwinForecastQuery | typeof import("./twin-attention/twin-attention-query.js").TwinAttentionQuery | typeof import("./twin-metrics/twin-metrics-query.js").TwinMetricsQuery | typeof import("./twin-readiness/twin-readiness-query.js").TwinReadinessQuery | typeof import("./twin-backfill/twin-backfill-resolver.js").TwinBackfillResolver)[];
|
|
29
|
+
resolverClasses: (typeof import("./twin-state/twin-state-subscription.js").TwinStateSubscription | typeof import("./actuation/actuation-rule-resolver.js").TwinActuationRuleResolver | typeof import("./actuation/twin-command-resolver.js").TwinCommandResolver | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectQuery | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectMutation | typeof import("./twin-audit/twin-audit-query.js").TwinAuditQuery | typeof import("./twin-space/twin-space-resolver.js").TwinSpaceResolver | typeof import("./twin-target/twin-target-resolver.js").TwinTargetResolver | typeof import("./reference/connection-portability-resolver.js").ConnectionPortabilityResolver | typeof import("./reference/reference-resolver.js").TwinReferenceResolver | typeof import("./reference/reference-progress-subscription.js").TwinReferenceProgressSubscription | typeof import("./reference/reference-reconciliation-query.js").TwinReferenceReconciliationQuery | typeof import("./twin-control/twin-control-mutation.js").TwinControlMutation | typeof import("./twin-model/twin-model-query.js").TwinModelQuery | typeof import("./twin-model/twin-model-item-query.js").TwinModelItemQuery | typeof import("./twin-model/twin-model-tree-query.js").TwinModelTreeQuery | typeof import("./twin-model/twin-model-mutation.js").TwinModelMutation | typeof import("./twin-model/twin-lineage-query.js").TwinLineageQuery | typeof import("./twin-ingest-window/twin-ingest-window-query.js").TwinIngestWindowQuery | typeof import("./twin-lifecycle/twin-lifecycle-mutation.js").TwinLifecycleMutation | typeof import("./twin-journal/twin-journal-query.js").TwinJournalQuery | typeof import("./twin-forecast/twin-forecast-query.js").TwinForecastQuery | typeof import("./twin-attention/twin-attention-query.js").TwinAttentionQuery | typeof import("./twin-metrics/twin-metrics-query.js").TwinMetricsQuery | typeof import("./twin-readiness/twin-readiness-query.js").TwinReadinessQuery | typeof import("./twin-backfill/twin-backfill-resolver.js").TwinBackfillResolver)[];
|
|
28
30
|
directives: {};
|
|
29
31
|
};
|
|
@@ -19,6 +19,8 @@ tslib_1.__exportStar(require("./reference/reference-adapter.js"), exports);
|
|
|
19
19
|
tslib_1.__exportStar(require("./reference/live-cadence.js"), exports);
|
|
20
20
|
tslib_1.__exportStar(require("./reference/reference-live.js"), exports);
|
|
21
21
|
tslib_1.__exportStar(require("./reference/twin-reference.js"), exports);
|
|
22
|
+
tslib_1.__exportStar(require("./reference/twin-reference-reconciliation.js"), exports);
|
|
23
|
+
tslib_1.__exportStar(require("./reference/reference-reconciliation.js"), exports);
|
|
22
24
|
tslib_1.__exportStar(require("./reference/connection-portability.js"), exports);
|
|
23
25
|
tslib_1.__exportStar(require("./reference/template-registry.js"), exports);
|
|
24
26
|
/* 커넥터 점검 하네스 — 실 원본에 붙여 무엇이 채워지고 무엇이 비는지 센다(앱을 띄우지 않는다). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,qEAA0C;AAC1C,2EAAgD;AAChD,qFAA0D;AAC1D,2EAAgD;AAChD,qEAA0C;AAC1C,oFAAyD;AACzD,0EAA+C;AAC/C,uEAA4C;AAC5C,6EAAkD;AAClD,kFAAuD;AACvD,0EAA+C;AAC/C,4EAAiD;AACjD,2EAAgD;AAChD,sEAA2C;AAC3C,wEAA6C;AAC7C,wEAA6C;AAC7C,gFAAqD;AACrD,2EAAgD;AAChD,4DAA4D;AAC5D,yEAA8C;AAC9C,iEAAiE;AACjE,8EAAmD;AACnD,sEAAsE;AACtE,6EAAkD;AAClD;;;GAGG;AACH,+DAAoC;AAEpC,mCAAmC;AACnC,oDAAqE;AACrE,sDAA4G;AAC5G,oDAAsG;AACtG,uDAA2E;AAC3E,wDAA6E;AAC7E,oDAAsG;AACtG,qDAAyG;AACzG,mDAA6G;AAC7G,2EAA2E;AAC3E,mDAA6G;AAC7G,qDAAuE;AACvE,uDAA2E;AAC3E,qDAAsG;AACtG,mEAAmE;AACnE,6DAA4H;AAC5H,yDAA+E;AAC/E,uDAA2E;AAC3E,wDAA6E;AAC7E,yDAA+E;AAC/E,uDAA2E;AAC3E,yDAA+E;AAC/E,qDAAqD;AACrD,wDAA6E;AAEhE,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAAiB;IACpB,GAAG,mBAAmB;IACtB,GAAG,oBAAwB;IAC3B,GAAG,mBAAiB;IACpB,GAAG,mBAAoB;IACvB,GAAG,mBAAqB;IACxB,GAAG,mBAAiB;IACpB,GAAG,mBAAkB;IACrB,GAAG,mBAAqB;IACxB,GAAG,mBAAqB;IACxB,4DAA4D;IAC5D,GAAG,oBAAiB;CACrB,CAAA;AAEY,QAAA,WAAW,GAAG,EAAE,CAAA;AAEhB,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE,EAAE;IACZ,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,qBAAkB;QACrB,GAAG,qBAAoB;QACvB,GAAG,qBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,oBAAkB;QACrB,GAAG,qBAAqB;QACxB,GAAG,qBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,qBAAsB;QACzB,GAAG,qBAAqB;QACxB,GAAG,qBAAyB;QAC5B,GAAG,qBAAkB;QACrB,GAAG,oBAAkB;QACrB,GAAG,oBAAmB;QACtB,GAAG,oBAAsB;QACzB,GAAG,oBAAsB;QACzB,GAAG,oBAAoB;KACxB;IACD,UAAU,EAAE,EAAE;CACf,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './twin-event/twin-event.js'\nexport * from './twin-audit/twin-audit-event.js'\nexport * from './twin-ingest-window/twin-ingest-window.js'\nexport * from './twin-instance/twin-instance.js'\nexport * from './twin-space/twin-space.js'\nexport * from './twin-space/twin-space-representation.js'\nexport * from './twin-space/twin-space-area.js'\nexport * from './twin-target/twin-target.js'\nexport * from './twin-structure/twin-structure.js'\nexport * from './twin-state/twin-state-subscription.js'\nexport * from './reference/reference-master.js'\nexport * from './reference/reference-registry.js'\nexport * from './reference/reference-adapter.js'\nexport * from './reference/live-cadence.js'\nexport * from './reference/reference-live.js'\nexport * from './reference/twin-reference.js'\nexport * from './reference/connection-portability.js'\nexport * from './reference/template-registry.js'\n/* 커넥터 점검 하네스 — 실 원본에 붙여 무엇이 채워지고 무엇이 비는지 센다(앱을 띄우지 않는다). */\nexport * from './reference/reference-probe.js'\n/* 읽은 뒤의 총평(사실 층) — 화면이 표로 그리고, 더 알고 싶을 때 AI 가 이 사실만 근거로 서술한다. */\nexport * from './reference/reference-assessment.js'\n/* 도메인 어휘 카탈로그 조합(커널 SSOT 노출) — 서버측 소비자(board-ai 도구 등)가 같은 형태를 쓰도록. */\nexport * from './twin-lifecycle/domain-catalog.js'\n/*\n * 조치 — 앱이 결재선을 채우고(§`setApprovalGateway`) 판정을 옮긴다(§`settleByApproval`). 이 줄이 없으면\n * 자리는 있는데 앱이 닿을 길이 없다.\n */\nexport * from './actuation/index.js'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as TwinEventEntities } from './twin-event/index.js'\nimport { entities as TwinSubjectEntities, resolvers as TwinSubjectResolvers } from './twin-subject/index.js'\nimport { entities as TwinAuditEntities, resolvers as TwinAuditResolvers } from './twin-audit/index.js'\nimport { entities as TwinInstanceEntities } from './twin-instance/index.js'\nimport { entities as TwinStructureEntities } from './twin-structure/index.js'\nimport { entities as TwinSpaceEntities, resolvers as TwinSpaceResolvers } from './twin-space/index.js'\nimport { entities as TwinTargetEntities, resolvers as TwinTargetResolvers } from './twin-target/index.js'\nimport { entities as TwinReferenceEntities, resolvers as TwinReferenceResolvers } from './reference/index.js'\n/* 현장에 내리는 조치 — 승인이 재기동을 넘어 살아야 하므로 표로 든다(§`actuation/twin-command.ts`). */\nimport { entities as TwinActuationEntities, resolvers as TwinActuationResolvers } from './actuation/index.js'\nimport { resolvers as TwinStateResolvers } from './twin-state/index.js'\nimport { resolvers as TwinControlResolvers } from './twin-control/index.js'\nimport { entities as TwinModelEntities, resolvers as TwinModelResolvers } from './twin-model/index.js'\n/* 인제스트 창 — 동기화 추이를 재기동 뒤에도 이어 보기 위한 행(리졸버 없음: 메모리 장부가 조회를 답한다). */\nimport { entities as TwinIngestWindowEntities, resolvers as TwinIngestWindowResolvers } from './twin-ingest-window/index.js'\nimport { resolvers as TwinLifecycleResolvers } from './twin-lifecycle/index.js'\nimport { resolvers as TwinJournalResolvers } from './twin-journal/index.js'\nimport { resolvers as TwinForecastResolvers } from './twin-forecast/index.js'\nimport { resolvers as TwinAttentionResolvers } from './twin-attention/index.js'\nimport { resolvers as TwinMetricsResolvers } from './twin-metrics/index.js'\nimport { resolvers as TwinReadinessResolvers } from './twin-readiness/index.js'\n/* 지난 기록 채우기 — 저널에만 쓰는 문. 라이브 유입과 판단이 달라 문을 따로 둔다. */\nimport { resolvers as TwinBackfillResolvers } from './twin-backfill/index.js'\n\nexport const entities = [\n /* ENTITIES */\n ...TwinEventEntities,\n ...TwinSubjectEntities,\n ...TwinIngestWindowEntities,\n ...TwinAuditEntities,\n ...TwinInstanceEntities,\n ...TwinStructureEntities,\n ...TwinSpaceEntities,\n ...TwinTargetEntities,\n ...TwinReferenceEntities,\n ...TwinActuationEntities,\n /* 구조 투영 — model JSON 에서 풀어낸 표준 엔티티(ADR-0032). 원본에서 온 캐시. */\n ...TwinModelEntities\n]\n\nexport const subscribers = []\n\nexport const schema = {\n typeDefs: {},\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...TwinStateResolvers,\n ...TwinControlResolvers,\n ...TwinLifecycleResolvers,\n ...TwinJournalResolvers,\n ...TwinAuditResolvers,\n ...TwinForecastResolvers,\n ...TwinAttentionResolvers,\n ...TwinMetricsResolvers,\n ...TwinReadinessResolvers,\n ...TwinBackfillResolvers,\n ...TwinIngestWindowResolvers,\n ...TwinModelResolvers,\n ...TwinSpaceResolvers,\n ...TwinTargetResolvers,\n ...TwinReferenceResolvers,\n ...TwinActuationResolvers,\n ...TwinSubjectResolvers\n ],\n directives: {}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,qEAA0C;AAC1C,2EAAgD;AAChD,qFAA0D;AAC1D,2EAAgD;AAChD,qEAA0C;AAC1C,oFAAyD;AACzD,0EAA+C;AAC/C,uEAA4C;AAC5C,6EAAkD;AAClD,kFAAuD;AACvD,0EAA+C;AAC/C,4EAAiD;AACjD,2EAAgD;AAChD,sEAA2C;AAC3C,wEAA6C;AAC7C,wEAA6C;AAC7C,uFAA4D;AAC5D,kFAAuD;AACvD,gFAAqD;AACrD,2EAAgD;AAChD,4DAA4D;AAC5D,yEAA8C;AAC9C,iEAAiE;AACjE,8EAAmD;AACnD,sEAAsE;AACtE,6EAAkD;AAClD;;;GAGG;AACH,+DAAoC;AAEpC,mCAAmC;AACnC,oDAAqE;AACrE,sDAA4G;AAC5G,oDAAsG;AACtG,uDAA2E;AAC3E,wDAA6E;AAC7E,oDAAsG;AACtG,qDAAyG;AACzG,mDAA6G;AAC7G,2EAA2E;AAC3E,mDAA6G;AAC7G,qDAAuE;AACvE,uDAA2E;AAC3E,qDAAsG;AACtG,mEAAmE;AACnE,6DAA4H;AAC5H,yDAA+E;AAC/E,uDAA2E;AAC3E,wDAA6E;AAC7E,yDAA+E;AAC/E,uDAA2E;AAC3E,yDAA+E;AAC/E,qDAAqD;AACrD,wDAA6E;AAEhE,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAAiB;IACpB,GAAG,mBAAmB;IACtB,GAAG,oBAAwB;IAC3B,GAAG,mBAAiB;IACpB,GAAG,mBAAoB;IACvB,GAAG,mBAAqB;IACxB,GAAG,mBAAiB;IACpB,GAAG,mBAAkB;IACrB,GAAG,mBAAqB;IACxB,GAAG,mBAAqB;IACxB,4DAA4D;IAC5D,GAAG,oBAAiB;CACrB,CAAA;AAEY,QAAA,WAAW,GAAG,EAAE,CAAA;AAEhB,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE,EAAE;IACZ,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,qBAAkB;QACrB,GAAG,qBAAoB;QACvB,GAAG,qBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,oBAAkB;QACrB,GAAG,qBAAqB;QACxB,GAAG,qBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,qBAAsB;QACzB,GAAG,qBAAqB;QACxB,GAAG,qBAAyB;QAC5B,GAAG,qBAAkB;QACrB,GAAG,oBAAkB;QACrB,GAAG,oBAAmB;QACtB,GAAG,oBAAsB;QACzB,GAAG,oBAAsB;QACzB,GAAG,oBAAoB;KACxB;IACD,UAAU,EAAE,EAAE;CACf,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './twin-event/twin-event.js'\nexport * from './twin-audit/twin-audit-event.js'\nexport * from './twin-ingest-window/twin-ingest-window.js'\nexport * from './twin-instance/twin-instance.js'\nexport * from './twin-space/twin-space.js'\nexport * from './twin-space/twin-space-representation.js'\nexport * from './twin-space/twin-space-area.js'\nexport * from './twin-target/twin-target.js'\nexport * from './twin-structure/twin-structure.js'\nexport * from './twin-state/twin-state-subscription.js'\nexport * from './reference/reference-master.js'\nexport * from './reference/reference-registry.js'\nexport * from './reference/reference-adapter.js'\nexport * from './reference/live-cadence.js'\nexport * from './reference/reference-live.js'\nexport * from './reference/twin-reference.js'\nexport * from './reference/twin-reference-reconciliation.js'\nexport * from './reference/reference-reconciliation.js'\nexport * from './reference/connection-portability.js'\nexport * from './reference/template-registry.js'\n/* 커넥터 점검 하네스 — 실 원본에 붙여 무엇이 채워지고 무엇이 비는지 센다(앱을 띄우지 않는다). */\nexport * from './reference/reference-probe.js'\n/* 읽은 뒤의 총평(사실 층) — 화면이 표로 그리고, 더 알고 싶을 때 AI 가 이 사실만 근거로 서술한다. */\nexport * from './reference/reference-assessment.js'\n/* 도메인 어휘 카탈로그 조합(커널 SSOT 노출) — 서버측 소비자(board-ai 도구 등)가 같은 형태를 쓰도록. */\nexport * from './twin-lifecycle/domain-catalog.js'\n/*\n * 조치 — 앱이 결재선을 채우고(§`setApprovalGateway`) 판정을 옮긴다(§`settleByApproval`). 이 줄이 없으면\n * 자리는 있는데 앱이 닿을 길이 없다.\n */\nexport * from './actuation/index.js'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as TwinEventEntities } from './twin-event/index.js'\nimport { entities as TwinSubjectEntities, resolvers as TwinSubjectResolvers } from './twin-subject/index.js'\nimport { entities as TwinAuditEntities, resolvers as TwinAuditResolvers } from './twin-audit/index.js'\nimport { entities as TwinInstanceEntities } from './twin-instance/index.js'\nimport { entities as TwinStructureEntities } from './twin-structure/index.js'\nimport { entities as TwinSpaceEntities, resolvers as TwinSpaceResolvers } from './twin-space/index.js'\nimport { entities as TwinTargetEntities, resolvers as TwinTargetResolvers } from './twin-target/index.js'\nimport { entities as TwinReferenceEntities, resolvers as TwinReferenceResolvers } from './reference/index.js'\n/* 현장에 내리는 조치 — 승인이 재기동을 넘어 살아야 하므로 표로 든다(§`actuation/twin-command.ts`). */\nimport { entities as TwinActuationEntities, resolvers as TwinActuationResolvers } from './actuation/index.js'\nimport { resolvers as TwinStateResolvers } from './twin-state/index.js'\nimport { resolvers as TwinControlResolvers } from './twin-control/index.js'\nimport { entities as TwinModelEntities, resolvers as TwinModelResolvers } from './twin-model/index.js'\n/* 인제스트 창 — 동기화 추이를 재기동 뒤에도 이어 보기 위한 행(리졸버 없음: 메모리 장부가 조회를 답한다). */\nimport { entities as TwinIngestWindowEntities, resolvers as TwinIngestWindowResolvers } from './twin-ingest-window/index.js'\nimport { resolvers as TwinLifecycleResolvers } from './twin-lifecycle/index.js'\nimport { resolvers as TwinJournalResolvers } from './twin-journal/index.js'\nimport { resolvers as TwinForecastResolvers } from './twin-forecast/index.js'\nimport { resolvers as TwinAttentionResolvers } from './twin-attention/index.js'\nimport { resolvers as TwinMetricsResolvers } from './twin-metrics/index.js'\nimport { resolvers as TwinReadinessResolvers } from './twin-readiness/index.js'\n/* 지난 기록 채우기 — 저널에만 쓰는 문. 라이브 유입과 판단이 달라 문을 따로 둔다. */\nimport { resolvers as TwinBackfillResolvers } from './twin-backfill/index.js'\n\nexport const entities = [\n /* ENTITIES */\n ...TwinEventEntities,\n ...TwinSubjectEntities,\n ...TwinIngestWindowEntities,\n ...TwinAuditEntities,\n ...TwinInstanceEntities,\n ...TwinStructureEntities,\n ...TwinSpaceEntities,\n ...TwinTargetEntities,\n ...TwinReferenceEntities,\n ...TwinActuationEntities,\n /* 구조 투영 — model JSON 에서 풀어낸 표준 엔티티(ADR-0032). 원본에서 온 캐시. */\n ...TwinModelEntities\n]\n\nexport const subscribers = []\n\nexport const schema = {\n typeDefs: {},\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...TwinStateResolvers,\n ...TwinControlResolvers,\n ...TwinLifecycleResolvers,\n ...TwinJournalResolvers,\n ...TwinAuditResolvers,\n ...TwinForecastResolvers,\n ...TwinAttentionResolvers,\n ...TwinMetricsResolvers,\n ...TwinReadinessResolvers,\n ...TwinBackfillResolvers,\n ...TwinIngestWindowResolvers,\n ...TwinModelResolvers,\n ...TwinSpaceResolvers,\n ...TwinTargetResolvers,\n ...TwinReferenceResolvers,\n ...TwinActuationResolvers,\n ...TwinSubjectResolvers\n ],\n directives: {}\n}\n"]}
|
|
@@ -2,10 +2,13 @@ import { ConnectionPortabilityResolver } from './connection-portability-resolver
|
|
|
2
2
|
import { TwinReferenceResolver } from './reference-resolver.js';
|
|
3
3
|
import { TwinReferenceProgressSubscription } from './reference-progress-subscription.js';
|
|
4
4
|
import { TwinReference } from './twin-reference.js';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { TwinReferenceReconciliation } from './twin-reference-reconciliation.js';
|
|
6
|
+
import { TwinReferenceReconciliationQuery } from './reference-reconciliation-query.js';
|
|
7
|
+
export declare const entities: (typeof TwinReference | typeof TwinReferenceReconciliation)[];
|
|
8
|
+
export declare const resolvers: (typeof ConnectionPortabilityResolver | typeof TwinReferenceResolver | typeof TwinReferenceProgressSubscription | typeof TwinReferenceReconciliationQuery)[];
|
|
7
9
|
export * from './control-routing.js';
|
|
8
10
|
export * from './reference-progress.js';
|
|
9
11
|
export * from './reference-probe.js';
|
|
10
12
|
export * from './reference-fill.js';
|
|
13
|
+
export * from './reference-reconciliation.js';
|
|
11
14
|
export * from './connection-portability.js';
|
|
@@ -6,8 +6,10 @@ const connection_portability_resolver_js_1 = require("./connection-portability-r
|
|
|
6
6
|
const reference_resolver_js_1 = require("./reference-resolver.js");
|
|
7
7
|
const reference_progress_subscription_js_1 = require("./reference-progress-subscription.js");
|
|
8
8
|
const twin_reference_js_1 = require("./twin-reference.js");
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const twin_reference_reconciliation_js_1 = require("./twin-reference-reconciliation.js");
|
|
10
|
+
const reference_reconciliation_query_js_1 = require("./reference-reconciliation-query.js");
|
|
11
|
+
exports.entities = [twin_reference_js_1.TwinReference, twin_reference_reconciliation_js_1.TwinReferenceReconciliation];
|
|
12
|
+
exports.resolvers = [reference_resolver_js_1.TwinReferenceResolver, reference_progress_subscription_js_1.TwinReferenceProgressSubscription, connection_portability_resolver_js_1.ConnectionPortabilityResolver, reference_reconciliation_query_js_1.TwinReferenceReconciliationQuery];
|
|
11
13
|
tslib_1.__exportStar(require("./control-routing.js"), exports);
|
|
12
14
|
/* 진행 단계 어휘 — **모든 커넥터가 같은 낱말로 말한다.** 화면은 단계마다 번역 하나만 두면 된다. */
|
|
13
15
|
tslib_1.__exportStar(require("./reference-progress.js"), exports);
|
|
@@ -15,5 +17,6 @@ tslib_1.__exportStar(require("./reference-progress.js"), exports);
|
|
|
15
17
|
tslib_1.__exportStar(require("./reference-probe.js"), exports);
|
|
16
18
|
/* 놓친 구간을 backfill 해 채우는 길 — 밀어 받는 길과 같은 번호 검사를 지난다. */
|
|
17
19
|
tslib_1.__exportStar(require("./reference-fill.js"), exports);
|
|
20
|
+
tslib_1.__exportStar(require("./reference-reconciliation.js"), exports);
|
|
18
21
|
tslib_1.__exportStar(require("./connection-portability.js"), exports);
|
|
19
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/reference/index.ts"],"names":[],"mappings":";;;;AAAA,6FAAoF;AACpF,mEAA+D;AAC/D,6FAAwF;AACxF,2DAAmD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/reference/index.ts"],"names":[],"mappings":";;;;AAAA,6FAAoF;AACpF,mEAA+D;AAC/D,6FAAwF;AACxF,2DAAmD;AACnD,yFAAgF;AAChF,2FAAsF;AAEzE,QAAA,QAAQ,GAAG,CAAC,iCAAa,EAAE,8DAA2B,CAAC,CAAA;AACvD,QAAA,SAAS,GAAG,CAAC,6CAAqB,EAAE,sEAAiC,EAAE,kEAA6B,EAAE,oEAAgC,CAAC,CAAA;AACpJ,+DAAoC;AACpC,+DAA+D;AAC/D,kEAAuC;AACvC,4DAA4D;AAC5D,+DAAoC;AACpC,uDAAuD;AACvD,8DAAmC;AACnC,wEAA6C;AAC7C,sEAA2C","sourcesContent":["import { ConnectionPortabilityResolver } from './connection-portability-resolver.js'\nimport { TwinReferenceResolver } from './reference-resolver.js'\nimport { TwinReferenceProgressSubscription } from './reference-progress-subscription.js'\nimport { TwinReference } from './twin-reference.js'\nimport { TwinReferenceReconciliation } from './twin-reference-reconciliation.js'\nimport { TwinReferenceReconciliationQuery } from './reference-reconciliation-query.js'\n\nexport const entities = [TwinReference, TwinReferenceReconciliation]\nexport const resolvers = [TwinReferenceResolver, TwinReferenceProgressSubscription, ConnectionPortabilityResolver, TwinReferenceReconciliationQuery]\nexport * from './control-routing.js'\n/* 진행 단계 어휘 — **모든 커넥터가 같은 낱말로 말한다.** 화면은 단계마다 번역 하나만 두면 된다. */\nexport * from './reference-progress.js'\n/* 커넥터 점검 하네스 — 실 원본에 붙여 무엇이 채워지고 무엇이 비는지 센다(앱을 띄우지 않는다). */\nexport * from './reference-probe.js'\n/* 놓친 구간을 backfill 해 채우는 길 — 밀어 받는 길과 같은 번호 검사를 지난다. */\nexport * from './reference-fill.js'\nexport * from './reference-reconciliation.js'\nexport * from './connection-portability.js'\n"]}
|
|
@@ -34,6 +34,7 @@ const reference_adapter_js_1 = require("./reference-adapter.js");
|
|
|
34
34
|
const hook_contract_js_1 = require("./hook-contract.js");
|
|
35
35
|
const reference_fill_js_1 = require("./reference-fill.js");
|
|
36
36
|
const live_ingest_js_1 = require("./live-ingest.js");
|
|
37
|
+
const reference_reconciliation_writer_js_1 = require("./reference-reconciliation-writer.js");
|
|
37
38
|
const fill_dedupe_js_1 = require("./fill-dedupe.js");
|
|
38
39
|
const index_js_1 = require("../../engine/index.js");
|
|
39
40
|
const log_js_1 = require("../../engine/log.js");
|
|
@@ -69,6 +70,8 @@ async function runFill(args) {
|
|
|
69
70
|
* 가 나아간 만큼만 적으므로, 잃은 구간을 되찾은 뒤 커서는 원래 자리보다 앞서지 않습니다.
|
|
70
71
|
*/
|
|
71
72
|
const cursor = from === undefined ? (ref.liveCursor ?? {}) : (0, hook_contract_js_1.withSeq)(ref.liveCursor, cursorKey, from);
|
|
73
|
+
/* fillSince가 커서를 저장할 때 이 지역 값도 같이 바꾼다. 저장본을 다시 읽어 대사를 늦추지 않는다. */
|
|
74
|
+
let persistedCursor = cursor;
|
|
72
75
|
const outcome = await (0, reference_fill_js_1.fillSince)({
|
|
73
76
|
cfg: ref.connectionConfig ?? {},
|
|
74
77
|
site,
|
|
@@ -100,9 +103,19 @@ async function runFill(args) {
|
|
|
100
103
|
},
|
|
101
104
|
saveCursor: async (next) => {
|
|
102
105
|
await repo.update({ id: ref.id }, { liveCursor: next });
|
|
106
|
+
persistedCursor = next;
|
|
103
107
|
},
|
|
104
108
|
...(maxPages ? { maxPages } : {})
|
|
105
109
|
});
|
|
110
|
+
/* 원본이 실제로 답한 범위가 있을 때만 기록한다. 범위를 모르는 것은 "정상"으로 덮지 않는다. */
|
|
111
|
+
await (0, reference_reconciliation_writer_js_1.recordReferenceReconciliation)({
|
|
112
|
+
domainId,
|
|
113
|
+
instanceId,
|
|
114
|
+
scope,
|
|
115
|
+
sourceRange: outcome.sourceRange,
|
|
116
|
+
cursorSeq: (0, hook_contract_js_1.seqOf)(persistedCursor, cursorKey),
|
|
117
|
+
firstSeqSeen: (0, hook_contract_js_1.firstSeqOf)(persistedCursor, cursorKey)
|
|
118
|
+
}).catch(err => (0, log_js_1.twinWarn)(`[twin-reconcile] "${instanceId}" · ${scope} 상태를 적지 못했습니다: ${err?.message ?? err}`));
|
|
106
119
|
const say = `[twin-fill] "${instanceId}" · ${scope} — ${outcome.applied}건 넣음 · ${outcome.pages}쪽 · ${outcome.stopped}${outcome.detail ? ` (${outcome.detail})` : ''}`;
|
|
107
120
|
/* 끝난 것과 막힌 것을 다른 소리로 남깁니다 — 구멍은 사람이 원본을 봐야 합니다. */
|
|
108
121
|
if (outcome.stopped === 'done' || outcome.stopped === 'no-progress')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-fill-run.js","sourceRoot":"","sources":["../../../server/service/reference/reference-fill-run.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * **빠진 구간을 다시 받는 일을 실제로 부른다** — `fillSince` 에 연결과 어댑터를 물려 주는 자리.\n *\n * ── 왜 이 파일이 없었나 ─────────────────────────────────────────────────────\n * `fillSince` 는 2026-08-31 에 만들어졌고 **부르는 곳이 0곳이었습니다.** 2026-09-05 에 저는 그것을\n * 못 보고 같은 일을 하는 것을 하나 더 만들었습니다(`backfillScope`). 두 벌이 되었고 둘 다 아무도\n * 부르지 않았습니다.\n *\n * 그래서 이런 상태였습니다.\n *\n * ```\n * 번호가 비면 어디서부터 다시 받을지 안다 ✔ takeInSequence 가 판정한다\n * 그 빈 것을 메운다 ✖ 부를 곳이 없다\n * ```\n *\n * 실제로 plant 아웃박스의 1~20 을 잃고 되찾을 방법이 없는 상태를 만났습니다.\n *\n * ── 같은 검사를 지난다 ──────────────────────────────────────────────────────\n * `fillSince` 가 `takeInSequence` 를 지나고, 유입은 `liveIngest` 를 지납니다 — 밀어 받는 길과 같은\n * 함수입니다. 여기서 따로 만들면 두 길이 다른 규칙으로 받고, 한쪽만 수정되는 날이 옵니다.\n *\n * ── 커서 열쇠는 프레임워크가 만든다 ─────────────────────────────────────────\n * 열쇠는 `instanceId::scope` 이고 그것을 아는 것은 프레임워크입니다. 어댑터가 만들면 두 곳이 같은\n * 규칙을 갖게 되고, 어긋나는 날 **다시 받기가 자기 커서를 0부터 새로 셉니다.**\n */\nimport { getRepository } from '@things-factory/shell'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\nimport { TwinReference } from './twin-reference.js'\nimport { getAdapter, type SeqReport, type SiteDescriptor } from './reference-adapter.js'\nimport { pushCursorKey, withSeq } from './hook-contract.js'\nimport { fillSince, type FillOutcome } from './reference-fill.js'\nimport { liveIngest } from './live-ingest.js'\nimport { journalIdentities, spanOf } from './fill-dedupe.js'\nimport { TwinEngine } from '../../engine/index.js'\nimport { twinLog, twinWarn } from '../../engine/log.js'\n\n/** 한 연결의 한 단위를 다시 받은 결과. */\nexport interface FillRunResult extends Partial<FillOutcome> {\n instanceId: string\n scope: string\n /**\n * 못 한 이유 — 성공이면 없습니다.\n *\n * `not-supported` 는 결함이 아닙니다. 주기로 물어보는 원본(chef · ppms)은 다음 주기가 곧 다시\n * 받는 것입니다.\n */\n skipped?: 'unknown-twin' | 'no-source' | 'no-adapter' | 'not-supported'\n}\n\n/**\n * 그 연결의 한 단위를 다시 받는다.\n *\n * **커서를 되돌리지 않습니다.** 받은 것이 번호 검사를 지나야 커서가 나아가고, 그 판정은 밀어 받는\n * 길과 같은 함수가 합니다.\n *\n * @param from 여기서부터 다시 받는다 — 주면 커서를 무시합니다. 잃은 구간을 되찾을 때만 씁니다.\n */\nexport async function runFill(args: {\n domainId: string\n instanceId: string\n scope: string\n from?: number\n maxPages?: number\n}): Promise<FillRunResult> {\n const { domainId, instanceId, scope, from, maxPages } = args\n const base: FillRunResult = { instanceId, scope }\n\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return { ...base, skipped: 'unknown-twin' }\n\n const repo = getRepository(TwinReference)\n const ref = await repo.findOne({ where: { domain: { id: domainId }, source: instanceId } })\n if (!ref) return { ...base, skipped: 'no-source' }\n\n const adapter = getAdapter(ref.adapterType)\n if (!adapter) return { ...base, skipped: 'no-adapter' }\n /* 다시 받기를 안 내는 원본이 있습니다 — 주기로 물어보는 쪽은 다음 주기가 곧 그것입니다. */\n if (typeof adapter.fetchSince !== 'function') return { ...base, skipped: 'not-supported' }\n\n const siteId = (inst.origin as any)?.siteId\n const site: SiteDescriptor = siteId ? { siteId } : { siteId: instanceId }\n const cursorKey = pushCursorKey(instanceId, scope)\n\n /*\n * `from` 을 주면 커서를 그 값으로 갈아 끼웁니다 — **저장된 커서를 건드리지 않습니다.** `fillSince`\n * 가 나아간 만큼만 적으므로, 잃은 구간을 되찾은 뒤 커서는 원래 자리보다 앞서지 않습니다.\n */\n const cursor = from === undefined ? ((ref as any).liveCursor ?? {}) : withSeq((ref as any).liveCursor, cursorKey, from)\n\n const outcome = await fillSince({\n cfg: ref.connectionConfig ?? {},\n site,\n scope,\n cursorKey,\n cursor,\n fetch: (cfg, s, sc, since) => adapter.fetchSince!(cfg, s as SiteDescriptor, sc, since),\n ingest: records => liveIngest(domainId, instanceId, records, 'fill'),\n /*\n * ── 넣기 전에 기억을 채운다 (2026-09-06) ──────────────────────────────────\n * 중복 판정 기억은 프로세스 안에만 있다. 재기동하면 비워지고, 그러면 backfill 이 **이미 저널에\n * 있는 사실도 다시 앉힌다.** 실측으로 39건이 통째로 다시 앉았다(저널 44 → 83).\n *\n * push 경로에서는 안 한다 — 거기서는 재전송이 창 안에 들어와 이미 걸리고, 봉투마다 저널을\n * 조회하면 뜨거운 길이 느려진다.\n */\n beforeIngest: async records => {\n const span = spanOf(records)\n if (!span) return\n const { identities, scanned, truncated } = await journalIdentities(domainId, instanceId, span)\n const added = TwinEngine.warmDeduper(domainId, instanceId, identities)\n if (truncated) {\n twinWarn(\n `[twin-fill] \"${instanceId}\" 의 저널을 ${scanned}줄까지만 읽었습니다 — 그보다 앞의 사실은 ` +\n '다시 앉을 수 있습니다. 구간을 좁혀 부르거나 정체 색인이 필요합니다'\n )\n }\n if (added) twinLog(`[twin-fill] \"${instanceId}\" — 저널에서 정체 ${added}개를 기억에 채웠습니다`)\n },\n saveCursor: async next => {\n await repo.update({ id: (ref as any).id }, { liveCursor: next } as any)\n },\n ...(maxPages ? { maxPages } : {})\n })\n\n const say = `[twin-fill] \"${instanceId}\" · ${scope} — ${outcome.applied}건 넣음 · ${outcome.pages}쪽 · ${outcome.stopped}${outcome.detail ? ` (${outcome.detail})` : ''}`\n /* 끝난 것과 막힌 것을 다른 소리로 남깁니다 — 구멍은 사람이 원본을 봐야 합니다. */\n if (outcome.stopped === 'done' || outcome.stopped === 'no-progress') twinLog(say)\n else twinWarn(say)\n\n return { ...base, ...outcome }\n}\n\n/**\n * **그 번호들이 무엇이었나** — 구멍을 만났을 때 사람이 묻는다.\n *\n * 커넥터는 「418 다음이 420」까지만 말한다. 그 419 가 무엇이었는지 알려면 원본에 물어야 하고,\n * 그 길이 없어서 사람이 아웃박스를 손으로 뒤졌다(2026-09-06).\n *\n * **판정하지 않는다.** 원본이 아는 것을 그대로 낸다 — 「그러니 포기해도 된다」는 되돌릴 수 없는\n * 판단이라 사람의 몫이다.\n */\nexport async function seqReportOf(args: {\n domainId: string\n instanceId: string\n scope: string\n from: number\n to: number\n}): Promise<{ instanceId: string; scope: string; report?: SeqReport; skipped?: 'unknown-twin' | 'no-source' | 'no-adapter' | 'not-supported' }> {\n const { domainId, instanceId, scope, from, to } = args\n const base = { instanceId, scope }\n\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return { ...base, skipped: 'unknown-twin' }\n const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } })\n if (!ref) return { ...base, skipped: 'no-source' }\n const adapter = getAdapter(ref.adapterType)\n if (!adapter) return { ...base, skipped: 'no-adapter' }\n /* 번호를 매기지 않는 원본은 물을 것이 없다 — 결함이 아니다. */\n if (typeof adapter.seqReport !== 'function') return { ...base, skipped: 'not-supported' }\n\n const siteId = (inst.origin as any)?.siteId\n const site: SiteDescriptor | undefined = siteId ? { siteId } : undefined\n return { ...base, report: await adapter.seqReport(ref.connectionConfig ?? {}, site, scope, from, to) }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reference-fill-run.js","sourceRoot":"","sources":["../../../server/service/reference/reference-fill-run.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * **빠진 구간을 다시 받는 일을 실제로 부른다** — `fillSince` 에 연결과 어댑터를 물려 주는 자리.\n *\n * ── 왜 이 파일이 없었나 ─────────────────────────────────────────────────────\n * `fillSince` 는 2026-08-31 에 만들어졌고 **부르는 곳이 0곳이었습니다.** 2026-09-05 에 저는 그것을\n * 못 보고 같은 일을 하는 것을 하나 더 만들었습니다(`backfillScope`). 두 벌이 되었고 둘 다 아무도\n * 부르지 않았습니다.\n *\n * 그래서 이런 상태였습니다.\n *\n * ```\n * 번호가 비면 어디서부터 다시 받을지 안다 ✔ takeInSequence 가 판정한다\n * 그 빈 것을 메운다 ✖ 부를 곳이 없다\n * ```\n *\n * 실제로 plant 아웃박스의 1~20 을 잃고 되찾을 방법이 없는 상태를 만났습니다.\n *\n * ── 같은 검사를 지난다 ──────────────────────────────────────────────────────\n * `fillSince` 가 `takeInSequence` 를 지나고, 유입은 `liveIngest` 를 지납니다 — 밀어 받는 길과 같은\n * 함수입니다. 여기서 따로 만들면 두 길이 다른 규칙으로 받고, 한쪽만 수정되는 날이 옵니다.\n *\n * ── 커서 열쇠는 프레임워크가 만든다 ─────────────────────────────────────────\n * 열쇠는 `instanceId::scope` 이고 그것을 아는 것은 프레임워크입니다. 어댑터가 만들면 두 곳이 같은\n * 규칙을 갖게 되고, 어긋나는 날 **다시 받기가 자기 커서를 0부터 새로 셉니다.**\n */\nimport { getRepository } from '@things-factory/shell'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\nimport { TwinReference } from './twin-reference.js'\nimport { getAdapter, type SeqReport, type SiteDescriptor } from './reference-adapter.js'\nimport { firstSeqOf, pushCursorKey, seqOf, withSeq } from './hook-contract.js'\nimport { fillSince, type FillOutcome } from './reference-fill.js'\nimport { liveIngest } from './live-ingest.js'\nimport { recordReferenceReconciliation } from './reference-reconciliation-writer.js'\nimport { journalIdentities, spanOf } from './fill-dedupe.js'\nimport { TwinEngine } from '../../engine/index.js'\nimport { twinLog, twinWarn } from '../../engine/log.js'\n\n/** 한 연결의 한 단위를 다시 받은 결과. */\nexport interface FillRunResult extends Partial<FillOutcome> {\n instanceId: string\n scope: string\n /**\n * 못 한 이유 — 성공이면 없습니다.\n *\n * `not-supported` 는 결함이 아닙니다. 주기로 물어보는 원본(chef · ppms)은 다음 주기가 곧 다시\n * 받는 것입니다.\n */\n skipped?: 'unknown-twin' | 'no-source' | 'no-adapter' | 'not-supported'\n}\n\n/**\n * 그 연결의 한 단위를 다시 받는다.\n *\n * **커서를 되돌리지 않습니다.** 받은 것이 번호 검사를 지나야 커서가 나아가고, 그 판정은 밀어 받는\n * 길과 같은 함수가 합니다.\n *\n * @param from 여기서부터 다시 받는다 — 주면 커서를 무시합니다. 잃은 구간을 되찾을 때만 씁니다.\n */\nexport async function runFill(args: {\n domainId: string\n instanceId: string\n scope: string\n from?: number\n maxPages?: number\n}): Promise<FillRunResult> {\n const { domainId, instanceId, scope, from, maxPages } = args\n const base: FillRunResult = { instanceId, scope }\n\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return { ...base, skipped: 'unknown-twin' }\n\n const repo = getRepository(TwinReference)\n const ref = await repo.findOne({ where: { domain: { id: domainId }, source: instanceId } })\n if (!ref) return { ...base, skipped: 'no-source' }\n\n const adapter = getAdapter(ref.adapterType)\n if (!adapter) return { ...base, skipped: 'no-adapter' }\n /* 다시 받기를 안 내는 원본이 있습니다 — 주기로 물어보는 쪽은 다음 주기가 곧 그것입니다. */\n if (typeof adapter.fetchSince !== 'function') return { ...base, skipped: 'not-supported' }\n\n const siteId = (inst.origin as any)?.siteId\n const site: SiteDescriptor = siteId ? { siteId } : { siteId: instanceId }\n const cursorKey = pushCursorKey(instanceId, scope)\n\n /*\n * `from` 을 주면 커서를 그 값으로 갈아 끼웁니다 — **저장된 커서를 건드리지 않습니다.** `fillSince`\n * 가 나아간 만큼만 적으므로, 잃은 구간을 되찾은 뒤 커서는 원래 자리보다 앞서지 않습니다.\n */\n const cursor = from === undefined ? ((ref as any).liveCursor ?? {}) : withSeq((ref as any).liveCursor, cursorKey, from)\n /* fillSince가 커서를 저장할 때 이 지역 값도 같이 바꾼다. 저장본을 다시 읽어 대사를 늦추지 않는다. */\n let persistedCursor: unknown = cursor\n\n const outcome = await fillSince({\n cfg: ref.connectionConfig ?? {},\n site,\n scope,\n cursorKey,\n cursor,\n fetch: (cfg, s, sc, since) => adapter.fetchSince!(cfg, s as SiteDescriptor, sc, since),\n ingest: records => liveIngest(domainId, instanceId, records, 'fill'),\n /*\n * ── 넣기 전에 기억을 채운다 (2026-09-06) ──────────────────────────────────\n * 중복 판정 기억은 프로세스 안에만 있다. 재기동하면 비워지고, 그러면 backfill 이 **이미 저널에\n * 있는 사실도 다시 앉힌다.** 실측으로 39건이 통째로 다시 앉았다(저널 44 → 83).\n *\n * push 경로에서는 안 한다 — 거기서는 재전송이 창 안에 들어와 이미 걸리고, 봉투마다 저널을\n * 조회하면 뜨거운 길이 느려진다.\n */\n beforeIngest: async records => {\n const span = spanOf(records)\n if (!span) return\n const { identities, scanned, truncated } = await journalIdentities(domainId, instanceId, span)\n const added = TwinEngine.warmDeduper(domainId, instanceId, identities)\n if (truncated) {\n twinWarn(\n `[twin-fill] \"${instanceId}\" 의 저널을 ${scanned}줄까지만 읽었습니다 — 그보다 앞의 사실은 ` +\n '다시 앉을 수 있습니다. 구간을 좁혀 부르거나 정체 색인이 필요합니다'\n )\n }\n if (added) twinLog(`[twin-fill] \"${instanceId}\" — 저널에서 정체 ${added}개를 기억에 채웠습니다`)\n },\n saveCursor: async next => {\n await repo.update({ id: (ref as any).id }, { liveCursor: next } as any)\n persistedCursor = next\n },\n ...(maxPages ? { maxPages } : {})\n })\n\n /* 원본이 실제로 답한 범위가 있을 때만 기록한다. 범위를 모르는 것은 \"정상\"으로 덮지 않는다. */\n await recordReferenceReconciliation({\n domainId,\n instanceId,\n scope,\n sourceRange: outcome.sourceRange,\n cursorSeq: seqOf(persistedCursor, cursorKey),\n firstSeqSeen: firstSeqOf(persistedCursor, cursorKey)\n }).catch(err => twinWarn(`[twin-reconcile] \"${instanceId}\" · ${scope} 상태를 적지 못했습니다: ${err?.message ?? err}`))\n\n const say = `[twin-fill] \"${instanceId}\" · ${scope} — ${outcome.applied}건 넣음 · ${outcome.pages}쪽 · ${outcome.stopped}${outcome.detail ? ` (${outcome.detail})` : ''}`\n /* 끝난 것과 막힌 것을 다른 소리로 남깁니다 — 구멍은 사람이 원본을 봐야 합니다. */\n if (outcome.stopped === 'done' || outcome.stopped === 'no-progress') twinLog(say)\n else twinWarn(say)\n\n return { ...base, ...outcome }\n}\n\n/**\n * **그 번호들이 무엇이었나** — 구멍을 만났을 때 사람이 묻는다.\n *\n * 커넥터는 「418 다음이 420」까지만 말한다. 그 419 가 무엇이었는지 알려면 원본에 물어야 하고,\n * 그 길이 없어서 사람이 아웃박스를 손으로 뒤졌다(2026-09-06).\n *\n * **판정하지 않는다.** 원본이 아는 것을 그대로 낸다 — 「그러니 포기해도 된다」는 되돌릴 수 없는\n * 판단이라 사람의 몫이다.\n */\nexport async function seqReportOf(args: {\n domainId: string\n instanceId: string\n scope: string\n from: number\n to: number\n}): Promise<{ instanceId: string; scope: string; report?: SeqReport; skipped?: 'unknown-twin' | 'no-source' | 'no-adapter' | 'not-supported' }> {\n const { domainId, instanceId, scope, from, to } = args\n const base = { instanceId, scope }\n\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return { ...base, skipped: 'unknown-twin' }\n const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } })\n if (!ref) return { ...base, skipped: 'no-source' }\n const adapter = getAdapter(ref.adapterType)\n if (!adapter) return { ...base, skipped: 'no-adapter' }\n /* 번호를 매기지 않는 원본은 물을 것이 없다 — 결함이 아니다. */\n if (typeof adapter.seqReport !== 'function') return { ...base, skipped: 'not-supported' }\n\n const siteId = (inst.origin as any)?.siteId\n const site: SiteDescriptor | undefined = siteId ? { siteId } : undefined\n return { ...base, report: await adapter.seqReport(ref.connectionConfig ?? {}, site, scope, from, to) }\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TwinReferenceReconciliation } from './twin-reference-reconciliation.js';
|
|
2
|
+
export declare class TwinReferenceReconciliationQuery {
|
|
3
|
+
twinReferenceReconciliations(instanceId: string, context: ResolverContext): Promise<TwinReferenceReconciliation[]>;
|
|
4
|
+
twinReferenceReconciliation(instanceId: string, scope: string, context: ResolverContext): Promise<TwinReferenceReconciliation | null>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TwinReferenceReconciliationQuery = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const shell_1 = require("@things-factory/shell");
|
|
7
|
+
const twin_reference_reconciliation_js_1 = require("./twin-reference-reconciliation.js");
|
|
8
|
+
let TwinReferenceReconciliationQuery = class TwinReferenceReconciliationQuery {
|
|
9
|
+
async twinReferenceReconciliations(instanceId, context) {
|
|
10
|
+
return (0, shell_1.getRepository)(twin_reference_reconciliation_js_1.TwinReferenceReconciliation).find({
|
|
11
|
+
where: { domain: { id: context.state.domain.id }, instanceId },
|
|
12
|
+
order: { scope: 'ASC' }
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async twinReferenceReconciliation(instanceId, scope, context) {
|
|
16
|
+
return (0, shell_1.getRepository)(twin_reference_reconciliation_js_1.TwinReferenceReconciliation).findOne({
|
|
17
|
+
where: { domain: { id: context.state.domain.id }, instanceId, scope }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
exports.TwinReferenceReconciliationQuery = TwinReferenceReconciliationQuery;
|
|
22
|
+
tslib_1.__decorate([
|
|
23
|
+
(0, type_graphql_1.Query)(returns => [twin_reference_reconciliation_js_1.TwinReferenceReconciliation], { description: 'All latest durable sequence reconciliations for one Twin. An empty list means no numbered source range has been read yet, not that every source is aligned.' }),
|
|
24
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('instanceId')),
|
|
25
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
26
|
+
tslib_1.__metadata("design:type", Function),
|
|
27
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
28
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
29
|
+
], TwinReferenceReconciliationQuery.prototype, "twinReferenceReconciliations", null);
|
|
30
|
+
tslib_1.__decorate([
|
|
31
|
+
(0, type_graphql_1.Query)(returns => twin_reference_reconciliation_js_1.TwinReferenceReconciliation, { nullable: true, description: 'Latest durable source-range versus Twin-cursor reconciliation for one numbered scope. Null means no backfill has yet obtained a source range; it is not an aligned result.' }),
|
|
32
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('instanceId')),
|
|
33
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('scope')),
|
|
34
|
+
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
35
|
+
tslib_1.__metadata("design:type", Function),
|
|
36
|
+
tslib_1.__metadata("design:paramtypes", [String, String, Object]),
|
|
37
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
38
|
+
], TwinReferenceReconciliationQuery.prototype, "twinReferenceReconciliation", null);
|
|
39
|
+
exports.TwinReferenceReconciliationQuery = TwinReferenceReconciliationQuery = tslib_1.__decorate([
|
|
40
|
+
(0, type_graphql_1.Resolver)()
|
|
41
|
+
], TwinReferenceReconciliationQuery);
|
|
42
|
+
//# sourceMappingURL=reference-reconciliation-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-reconciliation-query.js","sourceRoot":"","sources":["../../../server/service/reference/reference-reconciliation-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAwD;AACxD,iDAAqD;AACrD,yFAAgF;AAGzE,IAAM,gCAAgC,GAAtC,MAAM,gCAAgC;IAErC,AAAN,KAAK,CAAC,4BAA4B,CACb,UAAkB,EAC9B,OAAwB;QAE/B,OAAO,IAAA,qBAAa,EAAC,8DAA2B,CAAC,CAAC,IAAI,CAAC;YACrD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE;YAC9D,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;SACxB,CAAC,CAAA;IACJ,CAAC;IAGK,AAAN,KAAK,CAAC,2BAA2B,CACZ,UAAkB,EACvB,KAAa,EACpB,OAAwB;QAE/B,OAAO,IAAA,qBAAa,EAAC,8DAA2B,CAAC,CAAC,OAAO,CAAC;YACxD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE;SACtE,CAAC,CAAA;IACJ,CAAC;CACF,CAAA;AAtBY,4EAAgC;AAErC;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,8DAA2B,CAAC,EAAE,EAAE,WAAW,EAAE,6JAA6J,EAAE,CAAC;IAE7N,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;oFAMP;AAGK;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,8DAA2B,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4KAA4K,EAAE,CAAC;IAE1P,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,OAAO,CAAC,CAAA;IACZ,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;mFAKP;2CArBU,gCAAgC;IAD5C,IAAA,uBAAQ,GAAE;GACE,gCAAgC,CAsB5C","sourcesContent":["import { Arg, Ctx, Query, Resolver } from 'type-graphql'\nimport { getRepository } from '@things-factory/shell'\nimport { TwinReferenceReconciliation } from './twin-reference-reconciliation.js'\n\n@Resolver()\nexport class TwinReferenceReconciliationQuery {\n @Query(returns => [TwinReferenceReconciliation], { description: 'All latest durable sequence reconciliations for one Twin. An empty list means no numbered source range has been read yet, not that every source is aligned.' })\n async twinReferenceReconciliations(\n @Arg('instanceId') instanceId: string,\n @Ctx() context: ResolverContext\n ): Promise<TwinReferenceReconciliation[]> {\n return getRepository(TwinReferenceReconciliation).find({\n where: { domain: { id: context.state.domain.id }, instanceId },\n order: { scope: 'ASC' }\n })\n }\n\n @Query(returns => TwinReferenceReconciliation, { nullable: true, description: 'Latest durable source-range versus Twin-cursor reconciliation for one numbered scope. Null means no backfill has yet obtained a source range; it is not an aligned result.' })\n async twinReferenceReconciliation(\n @Arg('instanceId') instanceId: string,\n @Arg('scope') scope: string,\n @Ctx() context: ResolverContext\n ): Promise<TwinReferenceReconciliation | null> {\n return getRepository(TwinReferenceReconciliation).findOne({\n where: { domain: { id: context.state.domain.id }, instanceId, scope }\n })\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReferenceRange } from './reference-reconciliation.js';
|
|
2
|
+
/** backfill이 원본 범위를 실제로 읽은 뒤에만 최신 대사 상태를 갱신한다. */
|
|
3
|
+
export declare function recordReferenceReconciliation(args: {
|
|
4
|
+
domainId: string;
|
|
5
|
+
instanceId: string;
|
|
6
|
+
scope: string;
|
|
7
|
+
sourceRange?: ReferenceRange;
|
|
8
|
+
cursorSeq?: number;
|
|
9
|
+
firstSeqSeen?: number;
|
|
10
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.recordReferenceReconciliation = recordReferenceReconciliation;
|
|
4
|
+
const shell_1 = require("@things-factory/shell");
|
|
5
|
+
const twin_reference_reconciliation_js_1 = require("./twin-reference-reconciliation.js");
|
|
6
|
+
const reference_reconciliation_js_1 = require("./reference-reconciliation.js");
|
|
7
|
+
/** backfill이 원본 범위를 실제로 읽은 뒤에만 최신 대사 상태를 갱신한다. */
|
|
8
|
+
async function recordReferenceReconciliation(args) {
|
|
9
|
+
const { domainId, instanceId, scope, sourceRange, cursorSeq, firstSeqSeen } = args;
|
|
10
|
+
if (!sourceRange)
|
|
11
|
+
return;
|
|
12
|
+
const verdict = (0, reference_reconciliation_js_1.reconcileReferenceSequence)({ source: sourceRange, cursorSeq, firstSeqSeen });
|
|
13
|
+
const repo = (0, shell_1.getRepository)(twin_reference_reconciliation_js_1.TwinReferenceReconciliation);
|
|
14
|
+
const previous = await repo.findOne({ where: { domain: { id: domainId }, instanceId, scope } });
|
|
15
|
+
await repo.save(repo.create({
|
|
16
|
+
...(previous ?? {}),
|
|
17
|
+
domain: { id: domainId },
|
|
18
|
+
instanceId,
|
|
19
|
+
scope,
|
|
20
|
+
status: verdict.status,
|
|
21
|
+
/* TypeORM은 undefined를 "갱신하지 않음"으로 읽는다. 현재 대사가 맞아진 뒤 옛 gap 수가
|
|
22
|
+
* 남지 않게 nullable 칸은 명시적으로 null로 지운다. */
|
|
23
|
+
sourceFirst: verdict.sourceFirst ?? null,
|
|
24
|
+
sourceLast: verdict.sourceLast ?? null,
|
|
25
|
+
cursorSeq: verdict.cursorSeq ?? null,
|
|
26
|
+
firstSeqSeen: verdict.firstSeqSeen ?? null,
|
|
27
|
+
headMissing: verdict.headMissing ?? null,
|
|
28
|
+
behind: verdict.behind ?? null,
|
|
29
|
+
reason: verdict.reason ?? null,
|
|
30
|
+
checkedAt: new Date()
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=reference-reconciliation-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-reconciliation-writer.js","sourceRoot":"","sources":["../../../server/service/reference/reference-reconciliation-writer.ts"],"names":[],"mappings":";;AAKA,sEAgCC;AArCD,iDAAqD;AACrD,yFAAgF;AAChF,+EAA+F;AAE/F,kDAAkD;AAC3C,KAAK,UAAU,6BAA6B,CAAC,IAOnD;IACC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI,CAAA;IAClF,IAAI,CAAC,WAAW;QAAE,OAAM;IACxB,MAAM,OAAO,GAAG,IAAA,wDAA0B,EAAC,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAA;IAC5F,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,8DAA2B,CAAC,CAAA;IACvD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;IAC/F,MAAM,IAAI,CAAC,IAAI,CACb,IAAI,CAAC,MAAM,CAAC;QACV,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;QACnB,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAS;QAC/B,UAAU;QACV,KAAK;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB;gDACwC;QACxC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;QACxC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;QACtC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;QACpC,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI;QAC1C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;QACxC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;QAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;QAC9B,SAAS,EAAE,IAAI,IAAI,EAAE;KACtB,CAAC,CACH,CAAA;AACH,CAAC","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { TwinReferenceReconciliation } from './twin-reference-reconciliation.js'\nimport { reconcileReferenceSequence, type ReferenceRange } from './reference-reconciliation.js'\n\n/** backfill이 원본 범위를 실제로 읽은 뒤에만 최신 대사 상태를 갱신한다. */\nexport async function recordReferenceReconciliation(args: {\n domainId: string\n instanceId: string\n scope: string\n sourceRange?: ReferenceRange\n cursorSeq?: number\n firstSeqSeen?: number\n}): Promise<void> {\n const { domainId, instanceId, scope, sourceRange, cursorSeq, firstSeqSeen } = args\n if (!sourceRange) return\n const verdict = reconcileReferenceSequence({ source: sourceRange, cursorSeq, firstSeqSeen })\n const repo = getRepository(TwinReferenceReconciliation)\n const previous = await repo.findOne({ where: { domain: { id: domainId }, instanceId, scope } })\n await repo.save(\n repo.create({\n ...(previous ?? {}),\n domain: { id: domainId } as any,\n instanceId,\n scope,\n status: verdict.status,\n /* TypeORM은 undefined를 \"갱신하지 않음\"으로 읽는다. 현재 대사가 맞아진 뒤 옛 gap 수가\n * 남지 않게 nullable 칸은 명시적으로 null로 지운다. */\n sourceFirst: verdict.sourceFirst ?? null,\n sourceLast: verdict.sourceLast ?? null,\n cursorSeq: verdict.cursorSeq ?? null,\n firstSeqSeen: verdict.firstSeqSeen ?? null,\n headMissing: verdict.headMissing ?? null,\n behind: verdict.behind ?? null,\n reason: verdict.reason ?? null,\n checkedAt: new Date()\n })\n )\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ReferenceRange = {
|
|
2
|
+
first?: number;
|
|
3
|
+
last?: number;
|
|
4
|
+
};
|
|
5
|
+
export type ReferenceReconciliation = {
|
|
6
|
+
sourceFirst?: number;
|
|
7
|
+
sourceLast?: number;
|
|
8
|
+
cursorSeq?: number;
|
|
9
|
+
firstSeqSeen?: number;
|
|
10
|
+
headMissing?: number;
|
|
11
|
+
behind?: number;
|
|
12
|
+
status: 'aligned' | 'gap' | 'unknown';
|
|
13
|
+
reason?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function reconcileReferenceSequence(args: {
|
|
16
|
+
source: ReferenceRange | undefined;
|
|
17
|
+
cursorSeq?: number;
|
|
18
|
+
firstSeqSeen?: number;
|
|
19
|
+
}): ReferenceReconciliation;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reconcileReferenceSequence = reconcileReferenceSequence;
|
|
4
|
+
function reconcileReferenceSequence(args) {
|
|
5
|
+
const { source, cursorSeq, firstSeqSeen } = args;
|
|
6
|
+
const sourceFirst = source?.first;
|
|
7
|
+
const sourceLast = source?.last;
|
|
8
|
+
const base = { sourceFirst, sourceLast, cursorSeq, firstSeqSeen };
|
|
9
|
+
if (sourceFirst === undefined || sourceLast === undefined) {
|
|
10
|
+
return { ...base, status: 'unknown', reason: '원본이 번호 범위의 양 끝을 주지 않아 대사할 수 없습니다' };
|
|
11
|
+
}
|
|
12
|
+
if (cursorSeq === undefined) {
|
|
13
|
+
return { ...base, status: 'unknown', reason: 'Twin이 아직 이 단위의 번호를 받은 적이 없습니다' };
|
|
14
|
+
}
|
|
15
|
+
if (cursorSeq < sourceFirst) {
|
|
16
|
+
return {
|
|
17
|
+
...base,
|
|
18
|
+
status: 'unknown',
|
|
19
|
+
reason: 'Twin 커서가 원본의 첫 번호보다 앞입니다. 원본 번호가 다시 매겨졌거나 다른 단위의 커서입니다'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const behind = Math.max(0, sourceLast - cursorSeq);
|
|
23
|
+
/* 처음 받은 번호를 적지 않았으면 앞머리는 "없음"이 아니라 "모름"이다. */
|
|
24
|
+
const headMissing = firstSeqSeen === undefined ? undefined : Math.max(0, firstSeqSeen - sourceFirst);
|
|
25
|
+
if (behind || headMissing) {
|
|
26
|
+
return { ...base, ...(behind ? { behind } : {}), ...(headMissing ? { headMissing } : {}), status: 'gap' };
|
|
27
|
+
}
|
|
28
|
+
if (firstSeqSeen === undefined) {
|
|
29
|
+
return { ...base, status: 'unknown', reason: '처음 받은 번호가 기록되지 않아 앞머리 연속성은 확인할 수 없습니다' };
|
|
30
|
+
}
|
|
31
|
+
return { ...base, status: 'aligned' };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=reference-reconciliation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-reconciliation.js","sourceRoot":"","sources":["../../../server/service/reference/reference-reconciliation.ts"],"names":[],"mappings":";;AAoBA,gEAkCC;AAlCD,SAAgB,0BAA0B,CAAC,IAI1C;IACC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI,CAAA;IAChD,MAAM,WAAW,GAAG,MAAM,EAAE,KAAK,CAAA;IACjC,MAAM,UAAU,GAAG,MAAM,EAAE,IAAI,CAAA;IAC/B,MAAM,IAAI,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,CAAA;IAEjE,IAAI,WAAW,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC1D,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,kCAAkC,EAAE,CAAA;IACnF,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAA;IAChF,CAAC;IACD,IAAI,SAAS,GAAG,WAAW,EAAE,CAAC;QAC5B,OAAO;YACL,GAAG,IAAI;YACP,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,wDAAwD;SACjE,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,CAAA;IAClD,8CAA8C;IAC9C,MAAM,WAAW,GAAG,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,WAAW,CAAC,CAAA;IACpG,IAAI,MAAM,IAAI,WAAW,EAAE,CAAC;QAC1B,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;IAC3G,CAAC;IACD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,uCAAuC,EAAE,CAAA;IACxF,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;AACvC,CAAC","sourcesContent":["/*\n * 원본 번호 범위와 Twin 커서를 맞춘 **현재 판정**이다.\n *\n * 이 계산은 봉투 수와 저널 행 수를 비교하지 않는다. 재전송과 backfill 때문에 그 둘은 같은\n * 사실을 여러 번 셀 수 있다. 여기서 답하는 것은 번호를 매기는 원본에서만 가능한, 더 좁고\n * 검증 가능한 질문이다: \"원본의 양 끝과 우리가 처음/마지막으로 받은 번호가 이어지는가?\"\n */\nexport type ReferenceRange = { first?: number; last?: number }\n\nexport type ReferenceReconciliation = {\n sourceFirst?: number\n sourceLast?: number\n cursorSeq?: number\n firstSeqSeen?: number\n headMissing?: number\n behind?: number\n status: 'aligned' | 'gap' | 'unknown'\n reason?: string\n}\n\nexport function reconcileReferenceSequence(args: {\n source: ReferenceRange | undefined\n cursorSeq?: number\n firstSeqSeen?: number\n}): ReferenceReconciliation {\n const { source, cursorSeq, firstSeqSeen } = args\n const sourceFirst = source?.first\n const sourceLast = source?.last\n const base = { sourceFirst, sourceLast, cursorSeq, firstSeqSeen }\n\n if (sourceFirst === undefined || sourceLast === undefined) {\n return { ...base, status: 'unknown', reason: '원본이 번호 범위의 양 끝을 주지 않아 대사할 수 없습니다' }\n }\n if (cursorSeq === undefined) {\n return { ...base, status: 'unknown', reason: 'Twin이 아직 이 단위의 번호를 받은 적이 없습니다' }\n }\n if (cursorSeq < sourceFirst) {\n return {\n ...base,\n status: 'unknown',\n reason: 'Twin 커서가 원본의 첫 번호보다 앞입니다. 원본 번호가 다시 매겨졌거나 다른 단위의 커서입니다'\n }\n }\n\n const behind = Math.max(0, sourceLast - cursorSeq)\n /* 처음 받은 번호를 적지 않았으면 앞머리는 \"없음\"이 아니라 \"모름\"이다. */\n const headMissing = firstSeqSeen === undefined ? undefined : Math.max(0, firstSeqSeen - sourceFirst)\n if (behind || headMissing) {\n return { ...base, ...(behind ? { behind } : {}), ...(headMissing ? { headMissing } : {}), status: 'gap' }\n }\n if (firstSeqSeen === undefined) {\n return { ...base, status: 'unknown', reason: '처음 받은 번호가 기록되지 않아 앞머리 연속성은 확인할 수 없습니다' }\n }\n return { ...base, status: 'aligned' }\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
export declare class TwinReferenceReconciliation {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
domain?: Domain;
|
|
5
|
+
domainId?: string;
|
|
6
|
+
instanceId: string;
|
|
7
|
+
scope: string;
|
|
8
|
+
status: 'aligned' | 'gap' | 'unknown';
|
|
9
|
+
sourceFirst?: number;
|
|
10
|
+
sourceLast?: number;
|
|
11
|
+
cursorSeq?: number;
|
|
12
|
+
firstSeqSeen?: number;
|
|
13
|
+
headMissing?: number;
|
|
14
|
+
behind?: number;
|
|
15
|
+
reason?: string;
|
|
16
|
+
checkedAt: Date;
|
|
17
|
+
createdAt?: Date;
|
|
18
|
+
updatedAt?: Date;
|
|
19
|
+
}
|