@ifc-lite/viewer 1.17.6 → 1.19.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/.turbo/turbo-build.log +20 -15
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +949 -0
- package/dist/assets/{basketViewActivator-86rgogji.js → basketViewActivator-RZy5c3Td.js} +1 -1
- package/dist/assets/decode-worker-Collf_X_.js +1320 -0
- package/dist/assets/{exporters-CcPS9MK5.js → exporters-BraHBeoi.js} +4194 -3025
- package/dist/assets/{geometry.worker-BFUYA08u.js → geometry.worker-DQEZB2rB.js} +1 -1
- package/dist/assets/ifc-lite_bg-4yUkDRD8.wasm +0 -0
- package/dist/assets/index-0XpVr_S5.css +1 -0
- package/dist/assets/{index-Bfms9I4A.js → index-BOi3BuUI.js} +46423 -31181
- package/dist/assets/index-XwKzDuw6.js +22 -0
- package/dist/assets/{native-bridge-DUyLCMZS.js → native-bridge-CpBeOPQa.js} +1 -1
- package/dist/assets/sandbox-Baez7n-t.js +9682 -0
- package/dist/assets/{server-client-BuZK7OST.js → server-client-BB6cMAXE.js} +1 -1
- package/dist/assets/{wasm-bridge-JsqEGDV8.js → wasm-bridge-CAYCUHbE.js} +1 -1
- package/dist/index.html +6 -6
- package/package.json +11 -10
- package/src/apache-arrow.d.ts +30 -0
- package/src/components/viewer/AddElementPanel.tsx +758 -0
- package/src/components/viewer/BulkPropertyEditor.tsx +7 -0
- package/src/components/viewer/ChatPanel.tsx +64 -2
- package/src/components/viewer/CommandPalette.tsx +56 -7
- package/src/components/viewer/EntityContextMenu.tsx +168 -4
- package/src/components/viewer/ExportChangesButton.tsx +25 -5
- package/src/components/viewer/ExportDialog.tsx +19 -1
- package/src/components/viewer/MainToolbar.tsx +73 -12
- package/src/components/viewer/PointCloudPanel.tsx +174 -0
- package/src/components/viewer/PropertiesPanel.tsx +222 -22
- package/src/components/viewer/SearchInline.tsx +669 -0
- package/src/components/viewer/SearchModal.filter.builder.tsx +766 -0
- package/src/components/viewer/SearchModal.filter.tsx +514 -0
- package/src/components/viewer/SearchModal.text.tsx +388 -0
- package/src/components/viewer/SearchModal.tsx +235 -0
- package/src/components/viewer/ToolOverlays.tsx +5 -0
- package/src/components/viewer/ViewerLayout.tsx +24 -4
- package/src/components/viewer/Viewport.tsx +29 -2
- package/src/components/viewer/ViewportContainer.tsx +45 -5
- package/src/components/viewer/ViewportOverlays.tsx +13 -2
- package/src/components/viewer/annotations/AnnotationDropInput.tsx +203 -0
- package/src/components/viewer/annotations/AnnotationLayer.tsx +287 -0
- package/src/components/viewer/annotations/AnnotationPin.tsx +90 -0
- package/src/components/viewer/annotations/AnnotationPopover.tsx +296 -0
- package/src/components/viewer/bcf/BCFTopicDetail.tsx +1 -1
- package/src/components/viewer/lists/ListPanel.tsx +14 -21
- package/src/components/viewer/properties/RawStepCard.tsx +332 -0
- package/src/components/viewer/properties/RawStepRow.tsx +261 -0
- package/src/components/viewer/properties/ScheduleCard.tsx +224 -0
- package/src/components/viewer/properties/TaskEditCard.tsx +510 -0
- package/src/components/viewer/properties/raw-step-format.ts +193 -0
- package/src/components/viewer/schedule/AnimationSettingsPopover.tsx +542 -0
- package/src/components/viewer/schedule/GanttDependencyArrows.tsx +89 -0
- package/src/components/viewer/schedule/GanttDragTooltip.tsx +48 -0
- package/src/components/viewer/schedule/GanttEmptyState.tsx +97 -0
- package/src/components/viewer/schedule/GanttPanel.tsx +295 -0
- package/src/components/viewer/schedule/GanttTaskBar.tsx +199 -0
- package/src/components/viewer/schedule/GanttTaskTree.tsx +250 -0
- package/src/components/viewer/schedule/GanttTimeline.tsx +305 -0
- package/src/components/viewer/schedule/GanttToolbar.tsx +406 -0
- package/src/components/viewer/schedule/GenerateAdvancedPanel.tsx +147 -0
- package/src/components/viewer/schedule/GenerateScheduleDialog.tsx +392 -0
- package/src/components/viewer/schedule/HeightStrategyPanel.tsx +120 -0
- package/src/components/viewer/schedule/generate-schedule.test.ts +439 -0
- package/src/components/viewer/schedule/generate-schedule.ts +648 -0
- package/src/components/viewer/schedule/schedule-animator.test.ts +452 -0
- package/src/components/viewer/schedule/schedule-animator.ts +488 -0
- package/src/components/viewer/schedule/schedule-selection.test.ts +148 -0
- package/src/components/viewer/schedule/schedule-selection.ts +163 -0
- package/src/components/viewer/schedule/schedule-utils.ts +223 -0
- package/src/components/viewer/schedule/useConstructionSequence.ts +156 -0
- package/src/components/viewer/schedule/useGanttBarDrag.test.ts +90 -0
- package/src/components/viewer/schedule/useGanttBarDrag.ts +305 -0
- package/src/components/viewer/schedule/useGanttSelection3DHighlight.ts +152 -0
- package/src/components/viewer/schedule/useOverlayCompositor.ts +108 -0
- package/src/components/viewer/selectionHandlers.ts +446 -0
- package/src/components/viewer/tools/AddElementOverlay.tsx +581 -0
- package/src/components/viewer/useDuplicateShortcut.ts +77 -0
- package/src/components/viewer/useMouseControls.ts +9 -1
- package/src/components/viewer/usePointCloudLifecycle.ts +64 -0
- package/src/components/viewer/usePointCloudSync.ts +98 -0
- package/src/hooks/ingest/pointCloudIngest.ts +391 -0
- package/src/hooks/ingest/viewerModelIngest.ts +32 -3
- package/src/hooks/useIfcFederation.ts +72 -3
- package/src/hooks/useIfcLoader.ts +89 -13
- package/src/hooks/useKeyboardShortcuts.ts +25 -0
- package/src/hooks/useSandbox.ts +1 -1
- package/src/hooks/useSearchIndex.ts +125 -0
- package/src/index.css +66 -0
- package/src/lib/llm/system-prompt.test.ts +14 -0
- package/src/lib/llm/system-prompt.ts +102 -1
- package/src/lib/llm/types.ts +6 -0
- package/src/lib/recent-files.ts +38 -4
- package/src/lib/scripts/templates/bim-globals.d.ts +136 -114
- package/src/lib/scripts/templates/construction-schedule.ts +223 -0
- package/src/lib/scripts/templates.ts +7 -0
- package/src/lib/search/common-ifc-types.ts +36 -0
- package/src/lib/search/filter-evaluate.test.ts +537 -0
- package/src/lib/search/filter-evaluate.ts +610 -0
- package/src/lib/search/filter-rules.test.ts +119 -0
- package/src/lib/search/filter-rules.ts +198 -0
- package/src/lib/search/filter-schema.test.ts +233 -0
- package/src/lib/search/filter-schema.ts +146 -0
- package/src/lib/search/recent-searches.test.ts +116 -0
- package/src/lib/search/recent-searches.ts +93 -0
- package/src/lib/search/result-export.test.ts +101 -0
- package/src/lib/search/result-export.ts +104 -0
- package/src/lib/search/saved-filters.test.ts +118 -0
- package/src/lib/search/saved-filters.ts +154 -0
- package/src/lib/search/tier0-scan.test.ts +196 -0
- package/src/lib/search/tier0-scan.ts +237 -0
- package/src/lib/search/tier1-index.test.ts +242 -0
- package/src/lib/search/tier1-index.ts +448 -0
- package/src/sdk/adapters/export-adapter.test.ts +434 -1
- package/src/sdk/adapters/export-adapter.ts +404 -1
- package/src/sdk/adapters/export-schedule-splice.test.ts +127 -0
- package/src/sdk/adapters/export-schedule-splice.ts +87 -0
- package/src/sdk/adapters/model-compat.ts +8 -2
- package/src/sdk/adapters/schedule-adapter.ts +73 -0
- package/src/sdk/adapters/store-adapter.ts +201 -0
- package/src/sdk/adapters/visibility-adapter.ts +3 -0
- package/src/sdk/local-backend.ts +16 -8
- package/src/services/desktop-export.ts +3 -1
- package/src/services/desktop-native-metadata.ts +41 -18
- package/src/services/file-dialog.ts +8 -3
- package/src/services/tauri-modules.d.ts +25 -0
- package/src/store/basketVisibleSet.ts +3 -0
- package/src/store/globalId.ts +4 -1
- package/src/store/index.ts +79 -1
- package/src/store/slices/addElementMeshes.ts +365 -0
- package/src/store/slices/addElementSlice.ts +275 -0
- package/src/store/slices/annotationsSlice.test.ts +133 -0
- package/src/store/slices/annotationsSlice.ts +251 -0
- package/src/store/slices/dataSlice.test.ts +23 -4
- package/src/store/slices/dataSlice.ts +1 -1
- package/src/store/slices/modelSlice.test.ts +67 -9
- package/src/store/slices/modelSlice.ts +39 -7
- package/src/store/slices/mutationSlice.ts +964 -3
- package/src/store/slices/overlayCompositor.test.ts +164 -0
- package/src/store/slices/overlaySlice.test.ts +93 -0
- package/src/store/slices/overlaySlice.ts +151 -0
- package/src/store/slices/pinboardSlice.test.ts +6 -1
- package/src/store/slices/playbackSlice.ts +128 -0
- package/src/store/slices/pointCloudSlice.ts +102 -0
- package/src/store/slices/schedule-edit-helpers.test.ts +97 -0
- package/src/store/slices/schedule-edit-helpers.ts +179 -0
- package/src/store/slices/scheduleSlice.test.ts +694 -0
- package/src/store/slices/scheduleSlice.ts +1330 -0
- package/src/store/slices/searchSlice.test.ts +342 -0
- package/src/store/slices/searchSlice.ts +341 -0
- package/src/store/slices/selectionSlice.test.ts +46 -0
- package/src/store/slices/selectionSlice.ts +20 -0
- package/src/store/types.ts +7 -0
- package/src/store.ts +14 -0
- package/vite.config.ts +1 -0
- package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
- package/dist/assets/index-_bfZsDCC.css +0 -1
- package/dist/assets/sandbox-C8575tul.js +0 -5951
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, it, beforeEach } from 'node:test';
|
|
6
6
|
import assert from 'node:assert';
|
|
7
|
-
import { createDataSlice, type DataSlice } from './dataSlice.js';
|
|
7
|
+
import { createDataSlice, type DataSlice, type DataCrossSliceState } from './dataSlice.js';
|
|
8
8
|
import { DATA_DEFAULTS } from '../constants.js';
|
|
9
9
|
|
|
10
|
+
type DataTestState = DataSlice & DataCrossSliceState;
|
|
11
|
+
|
|
10
12
|
// Mock mesh data for testing
|
|
11
13
|
const createMockMesh = (expressId: number, color: [number, number, number, number] = [1, 0, 0, 1]) => ({
|
|
12
14
|
expressId,
|
|
@@ -17,9 +19,16 @@ const createMockMesh = (expressId: number, color: [number, number, number, numbe
|
|
|
17
19
|
ifcType: 'IfcWall',
|
|
18
20
|
});
|
|
19
21
|
|
|
22
|
+
type TestSetState = (
|
|
23
|
+
partial:
|
|
24
|
+
| Partial<DataTestState>
|
|
25
|
+
| ((state: DataTestState) => Partial<DataTestState>),
|
|
26
|
+
) => void;
|
|
27
|
+
type TestGetState = () => DataTestState;
|
|
28
|
+
|
|
20
29
|
describe('DataSlice', () => {
|
|
21
|
-
let state:
|
|
22
|
-
let setState:
|
|
30
|
+
let state: DataTestState;
|
|
31
|
+
let setState: TestSetState;
|
|
23
32
|
|
|
24
33
|
beforeEach(() => {
|
|
25
34
|
setState = (partial) => {
|
|
@@ -31,7 +40,17 @@ describe('DataSlice', () => {
|
|
|
31
40
|
}
|
|
32
41
|
};
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
const getState: TestGetState = () => state;
|
|
44
|
+
|
|
45
|
+
// Seed the cross-slice fields owned by ModelSlice. dataSlice's
|
|
46
|
+
// updaters look up the active model in this map, so the test mock
|
|
47
|
+
// has to provide it for the typed StateCreator to be satisfiable.
|
|
48
|
+
const slice = createDataSlice(
|
|
49
|
+
setState as Parameters<typeof createDataSlice>[0],
|
|
50
|
+
getState as Parameters<typeof createDataSlice>[1],
|
|
51
|
+
undefined as unknown as Parameters<typeof createDataSlice>[2],
|
|
52
|
+
);
|
|
53
|
+
state = { ...slice, activeModelId: null, models: new Map() };
|
|
35
54
|
});
|
|
36
55
|
|
|
37
56
|
describe('initial state', () => {
|
|
@@ -20,7 +20,7 @@ import { DATA_DEFAULTS } from '../constants.js';
|
|
|
20
20
|
* consistent. The types below describe the minimal ModelSlice surface
|
|
21
21
|
* that dataSlice accesses through the merged Zustand state.
|
|
22
22
|
*/
|
|
23
|
-
interface DataCrossSliceState {
|
|
23
|
+
export interface DataCrossSliceState {
|
|
24
24
|
activeModelId: string | null;
|
|
25
25
|
models: Map<string, FederatedModel>;
|
|
26
26
|
}
|
|
@@ -4,16 +4,34 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, it, beforeEach } from 'node:test';
|
|
6
6
|
import assert from 'node:assert';
|
|
7
|
-
import {
|
|
7
|
+
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
8
|
+
import type { GeometryResult } from '@ifc-lite/geometry';
|
|
9
|
+
import { createModelSlice, type ModelSlice, type ModelCrossSliceState } from './modelSlice.js';
|
|
8
10
|
import type { FederatedModel } from '../types.js';
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
type ModelTestState = ModelSlice & ModelCrossSliceState;
|
|
13
|
+
|
|
14
|
+
// Typed setter / getter shim that mirrors zustand's StateCreator
|
|
15
|
+
// signature without the broader middleware machinery the test doesn't
|
|
16
|
+
// need. Using StateCreator's exact types here would pull in the whole
|
|
17
|
+
// store; the local aliases below are tight enough for this test.
|
|
18
|
+
type TestSetState = (
|
|
19
|
+
partial:
|
|
20
|
+
| Partial<ModelTestState>
|
|
21
|
+
| ((state: ModelTestState) => Partial<ModelTestState>),
|
|
22
|
+
) => void;
|
|
23
|
+
type TestGetState = () => ModelTestState;
|
|
24
|
+
|
|
25
|
+
// Helper to create a mock model. `IfcDataStore` and `GeometryResult` are
|
|
26
|
+
// large interfaces that the slice never inspects on these paths — the
|
|
27
|
+
// double-cast through `unknown` is the minimum that satisfies the
|
|
28
|
+
// compiler without an `any`.
|
|
11
29
|
function createMockModel(id: string, name: string): FederatedModel {
|
|
12
30
|
return {
|
|
13
31
|
id,
|
|
14
32
|
name,
|
|
15
|
-
ifcDataStore: {} as
|
|
16
|
-
geometryResult: {} as
|
|
33
|
+
ifcDataStore: {} as unknown as IfcDataStore,
|
|
34
|
+
geometryResult: {} as unknown as GeometryResult,
|
|
17
35
|
visible: true,
|
|
18
36
|
collapsed: false,
|
|
19
37
|
schemaVersion: 'IFC4',
|
|
@@ -25,11 +43,10 @@ function createMockModel(id: string, name: string): FederatedModel {
|
|
|
25
43
|
}
|
|
26
44
|
|
|
27
45
|
describe('ModelSlice', () => {
|
|
28
|
-
let state:
|
|
29
|
-
let setState:
|
|
46
|
+
let state: ModelTestState;
|
|
47
|
+
let setState: TestSetState;
|
|
30
48
|
|
|
31
49
|
beforeEach(() => {
|
|
32
|
-
// Create a mock set function that updates state
|
|
33
50
|
setState = (partial) => {
|
|
34
51
|
if (typeof partial === 'function') {
|
|
35
52
|
const updates = partial(state);
|
|
@@ -39,8 +56,17 @@ describe('ModelSlice', () => {
|
|
|
39
56
|
}
|
|
40
57
|
};
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
const getState: TestGetState = () => state;
|
|
60
|
+
|
|
61
|
+
// The slice's StateCreator signature includes a third middleware
|
|
62
|
+
// argument (store API) that the slice's body never reads. We pass
|
|
63
|
+
// `undefined` cast to the empty middleware shape rather than `any`.
|
|
64
|
+
const slice = createModelSlice(
|
|
65
|
+
setState as Parameters<typeof createModelSlice>[0],
|
|
66
|
+
getState as Parameters<typeof createModelSlice>[1],
|
|
67
|
+
undefined as unknown as Parameters<typeof createModelSlice>[2],
|
|
68
|
+
);
|
|
69
|
+
state = { ...slice, ifcDataStore: null, geometryResult: null };
|
|
44
70
|
});
|
|
45
71
|
|
|
46
72
|
describe('initial state', () => {
|
|
@@ -270,4 +296,36 @@ describe('ModelSlice', () => {
|
|
|
270
296
|
assert.strictEqual(visible.length, 0);
|
|
271
297
|
});
|
|
272
298
|
});
|
|
299
|
+
|
|
300
|
+
describe('resolveGlobalIdFromModels — overlay-allocated ids', () => {
|
|
301
|
+
it('falls through to mutation views when the id is past maxExpressId', () => {
|
|
302
|
+
const model = createMockModel('model-1', 'First');
|
|
303
|
+
model.idOffset = 0;
|
|
304
|
+
model.maxExpressId = 10_000;
|
|
305
|
+
state.addModel(model);
|
|
306
|
+
|
|
307
|
+
// Seed a fake mutation view with a fresh overlay entity. The
|
|
308
|
+
// resolver only reads `getNewEntity` from each view, so we type
|
|
309
|
+
// the map narrowly and let it satisfy the slice's wider type via
|
|
310
|
+
// a single-property cast on the wrapping state object.
|
|
311
|
+
type StubView = { getNewEntity: (id: number) => { expressId: number } | null };
|
|
312
|
+
const stubViews: Map<string, StubView> = new Map([
|
|
313
|
+
['model-1', { getNewEntity: (id: number) => (id === 11_001 ? { expressId: id } : null) }],
|
|
314
|
+
]);
|
|
315
|
+
state = { ...state, mutationViews: stubViews } as typeof state & { mutationViews: Map<string, StubView> };
|
|
316
|
+
|
|
317
|
+
// Inside the parsed range — first pass resolves it.
|
|
318
|
+
const within = state.resolveGlobalIdFromModels(42);
|
|
319
|
+
assert.deepStrictEqual(within, { modelId: 'model-1', expressId: 42 });
|
|
320
|
+
|
|
321
|
+
// Above the parsed range but in the overlay — second pass resolves it.
|
|
322
|
+
const overlay = state.resolveGlobalIdFromModels(11_001);
|
|
323
|
+
assert.deepStrictEqual(overlay, { modelId: 'model-1', expressId: 11_001 });
|
|
324
|
+
|
|
325
|
+
// Above the parsed range and NOT in the overlay — returns null
|
|
326
|
+
// so callers can fall back to the legacy single-model path.
|
|
327
|
+
const phantom = state.resolveGlobalIdFromModels(99_999);
|
|
328
|
+
assert.strictEqual(phantom, null);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
273
331
|
});
|
|
@@ -13,8 +13,20 @@
|
|
|
13
13
|
|
|
14
14
|
import type { StateCreator } from 'zustand';
|
|
15
15
|
import type { FederatedModel } from '../types.js';
|
|
16
|
+
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
17
|
+
import type { GeometryResult } from '@ifc-lite/geometry';
|
|
16
18
|
import { federationRegistry, type GlobalIdLookup } from '@ifc-lite/renderer';
|
|
17
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Cross-slice fields the model actions write to. `ifcDataStore` and
|
|
22
|
+
* `geometryResult` are owned by `dataSlice` but `modelSlice`'s set()
|
|
23
|
+
* calls need to keep them in sync with the active model.
|
|
24
|
+
*/
|
|
25
|
+
export interface ModelCrossSliceState {
|
|
26
|
+
ifcDataStore: IfcDataStore | null;
|
|
27
|
+
geometryResult: GeometryResult | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
export interface ModelSlice {
|
|
19
31
|
// State
|
|
20
32
|
/** Map of all loaded models by ID */
|
|
@@ -72,7 +84,7 @@ export interface ModelSlice {
|
|
|
72
84
|
resolveGlobalIdFromModels: (globalId: number) => GlobalIdLookup | null;
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
export const createModelSlice: StateCreator<ModelSlice, [], [], ModelSlice> = (set, get) => ({
|
|
87
|
+
export const createModelSlice: StateCreator<ModelSlice & ModelCrossSliceState, [], [], ModelSlice> = (set, get) => ({
|
|
76
88
|
// Initial state
|
|
77
89
|
models: new Map(),
|
|
78
90
|
activeModelId: null,
|
|
@@ -245,19 +257,39 @@ export const createModelSlice: StateCreator<ModelSlice, [], [], ModelSlice> = (s
|
|
|
245
257
|
*/
|
|
246
258
|
resolveGlobalIdFromModels: (globalId: number) => {
|
|
247
259
|
const models = get().models;
|
|
260
|
+
const mutationViews = (get() as unknown as { mutationViews?: Map<string, { getNewEntity: (id: number) => unknown }> }).mutationViews;
|
|
248
261
|
|
|
249
262
|
// Sort models by offset for correct range checking
|
|
250
263
|
const sortedModels = Array.from(models.values()).sort((a, b) => a.idOffset - b.idOffset);
|
|
251
264
|
|
|
252
|
-
// Find the model that contains this globalId
|
|
253
|
-
//
|
|
265
|
+
// Find the model that contains this globalId.
|
|
266
|
+
//
|
|
267
|
+
// First pass — parse-time range. A model owns ids in
|
|
268
|
+
// `[offset, offset + maxExpressId]` from the original parse. This
|
|
269
|
+
// is the fast path covering 99% of selections.
|
|
270
|
+
//
|
|
271
|
+
// Second pass — overlay-allocated ids. Duplicates / scripted adds
|
|
272
|
+
// through StoreEditor land ABOVE the model's parse-time
|
|
273
|
+
// maxExpressId, so they fall outside the first-pass range. The
|
|
274
|
+
// federation resolver knows nothing about overlay state, so we
|
|
275
|
+
// consult each model's mutation view for the freshly-added
|
|
276
|
+
// entity. Falls back gracefully when no view is registered.
|
|
254
277
|
for (const model of sortedModels) {
|
|
255
278
|
const localId = globalId - model.idOffset;
|
|
256
279
|
if (localId >= 0 && localId <= model.maxExpressId) {
|
|
257
|
-
return {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
280
|
+
return { modelId: model.id, expressId: localId };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (mutationViews) {
|
|
285
|
+
for (const model of sortedModels) {
|
|
286
|
+
const localId = globalId - model.idOffset;
|
|
287
|
+
if (localId <= model.maxExpressId) continue; // already covered above
|
|
288
|
+
const view = mutationViews.get(model.id);
|
|
289
|
+
if (!view) continue;
|
|
290
|
+
if (view.getNewEntity(localId) !== null) {
|
|
291
|
+
return { modelId: model.id, expressId: localId };
|
|
292
|
+
}
|
|
261
293
|
}
|
|
262
294
|
}
|
|
263
295
|
|