@stalfh233/omc-fake-platform 0.4.75 → 0.4.77

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/field.js CHANGED
@@ -6,6 +6,12 @@ export const fieldRoute = (ctx, input) => {
6
6
  const body = (input.body ?? {});
7
7
  switch (input.path) {
8
8
  case "/api/api/app/bizproperty/list": {
9
+ // Live 2026-10-23 (issue §1.4): on some versions a CHILD-table schema code
10
+ // answers 601003 业务模型不存在 on this face while bizdatarule/list answers
11
+ // rows for it. That is a version-specific asymmetry, so the fake exposes it
12
+ // as an explicit lever (state.fieldListUnavailable) instead of guessing it.
13
+ if (ctx.state.fieldListUnavailable?.has(String(q.schemaCode ?? "")))
14
+ return businessRefusal(601003, "业务模型不存在");
9
15
  const rows = [...ctx.state.fields.entries()]
10
16
  .filter(([key]) => key.startsWith(`${q.schemaCode}#`))
11
17
  .map(([, field]) => ({
package/dist/model.js CHANGED
@@ -20,10 +20,47 @@ export const modelRoute = (ctx, input) => {
20
20
  // discover_environment with applicationCode returned no models. Consumers
21
21
  // must flattenTreeRows (as resolveModelTreeNode already does); the fake
22
22
  // must nest so a regression cannot pass.
23
- const children = [...ctx.state.models.values()]
24
- .filter((model) => !model.treeHidden && (appCode === undefined || (model.appCode ?? "test") === appCode))
25
- .map((model) => ({ id: model.id, type: "BizModel", code: model.code, name: model.nodeName ?? model.name, parentId: "folder-1" }));
26
- return [{ id: "folder-1", type: "FOLDER", code: "general", name: "General", children }];
23
+ const folders = [...ctx.state.folders.values()].filter((folder) => appCode === undefined || folder.appCode === appCode);
24
+ // Every real application has at least one folder (the designer's default
25
+ // group). An app seeded without one still needs a resolvable folder or
26
+ // model.create could never run there — synthesize the default.
27
+ if (appCode !== undefined && !folders.some((folder) => folder.appCode === appCode))
28
+ folders.push({ id: `folder-${appCode}`, appCode, name: "General", code: "general" });
29
+ const models = [...ctx.state.models.values()].filter((model) => !model.treeHidden && (appCode === undefined || (model.appCode ?? "test") === appCode));
30
+ const ROOT = "folder-1";
31
+ const nodeOf = (folder) => ({
32
+ id: folder.id,
33
+ type: "Folder",
34
+ code: folder.code ?? null,
35
+ name: folder.name,
36
+ parentId: folder.parentId ?? ROOT,
37
+ children: [],
38
+ });
39
+ const nodes = new Map();
40
+ for (const folder of folders)
41
+ nodes.set(folder.id, nodeOf(folder));
42
+ const rootChildren = [];
43
+ for (const folder of folders) {
44
+ const node = nodes.get(folder.id);
45
+ const parent = folder.parentId ? nodes.get(folder.parentId) : undefined;
46
+ if (parent)
47
+ parent.children.push(node);
48
+ else
49
+ rootChildren.push(node);
50
+ }
51
+ for (const model of models) {
52
+ const node = { id: model.id, type: "BizModel", code: model.code, name: model.nodeName ?? model.name, parentId: model.parentId ?? ROOT };
53
+ const parent = model.parentId ? nodes.get(model.parentId) : undefined;
54
+ if (parent)
55
+ parent.children.push(node);
56
+ else
57
+ rootChildren.push(node);
58
+ }
59
+ // Live 1.1.25 fidelity (2026-10-23, folder.tree readback): the tree
60
+ // returns the TOP-LEVEL folders directly — no synthetic app-package
61
+ // container row. The fake must match, or a depth-0 assertion would pass
62
+ // against a shape the platform never returns.
63
+ return rootChildren;
27
64
  }
28
65
  case "/api/api/app/bizmodels/list_schema":
29
66
  return [...ctx.state.models.values()].map((model) => ({ id: model.recordId, schemaCode: model.code, name: model.name }));
@@ -36,6 +73,7 @@ export const modelRoute = (ctx, input) => {
36
73
  code,
37
74
  name: String(body.name ?? code),
38
75
  appCode: String(body.applicationCode ?? body.appCode ?? "test"),
76
+ ...(body.folderId !== undefined && body.folderId !== null && String(body.folderId) !== "" ? { parentId: String(body.folderId) } : {}),
39
77
  });
40
78
  // Live fidelity (ticket 42): bizmodels/create seeds NO form — the model
41
79
  // has no h_biz_sheet row until the form is created (bizsheet/create, or
@@ -105,6 +143,61 @@ export const modelRoute = (ctx, input) => {
105
143
  const model = ctx.state.models.get(q.schemaCode);
106
144
  return model ? { id: model.recordId, name: model.name, modelType: "LIST" } : null;
107
145
  }
146
+ case "/api/api/app/folders/create": {
147
+ const appCode = String(body.appCode ?? "");
148
+ if (!appCode)
149
+ return businessRefusal(10005, "应用编码不能为空");
150
+ const name = String(body.name ?? "");
151
+ if (!name)
152
+ return businessRefusal(10005, "名称不能为空");
153
+ const parentId = body.parentId === undefined || body.parentId === null || String(body.parentId) === "" ? "folder-1" : String(body.parentId);
154
+ if (parentId !== "folder-1" && !ctx.state.folders.has(parentId))
155
+ return businessRefusal(10005, `目录不存在: ${parentId}`);
156
+ const id = `folder-${ctx.state.folders.size + 2}`;
157
+ ctx.state.folders.set(id, {
158
+ id,
159
+ appCode,
160
+ name,
161
+ ...(typeof body.name_i18n === "string" ? { name_i18n: body.name_i18n } : {}),
162
+ ...(typeof body.code === "string" ? { code: body.code } : {}),
163
+ ...(parentId !== "folder-1" ? { parentId } : {}),
164
+ });
165
+ return null;
166
+ }
167
+ case "/api/api/app/folders/get": {
168
+ const folder = ctx.state.folders.get(String(q.folderId ?? ""));
169
+ if (!folder)
170
+ return businessRefusal(10005, "目录不存在");
171
+ return { ...folder, type: "Folder" };
172
+ }
173
+ case "/api/api/app/folders/update": {
174
+ const folder = ctx.state.folders.get(String(body.id ?? ""));
175
+ if (!folder)
176
+ return businessRefusal(10005, "目录不存在");
177
+ const parentId = body.parentId === undefined || body.parentId === null ? "" : String(body.parentId);
178
+ if (parentId !== "" && parentId === folder.id)
179
+ return businessRefusal(10005, "上级不能选择自己");
180
+ if (parentId !== "" && parentId !== "folder-1" && !ctx.state.folders.has(parentId))
181
+ return businessRefusal(10005, `目录不存在: ${parentId}`);
182
+ ctx.state.folders.set(folder.id, {
183
+ ...folder,
184
+ ...(body.name !== undefined ? { name: String(body.name) } : {}),
185
+ ...(typeof body.name_i18n === "string" ? { name_i18n: body.name_i18n } : {}),
186
+ ...(parentId !== "" ? { parentId: parentId === "folder-1" ? undefined : parentId } : {}),
187
+ });
188
+ return null;
189
+ }
190
+ case "/api/api/app/folders/delete": {
191
+ const folderId = String(q.folderId ?? "");
192
+ if (!ctx.state.folders.has(folderId))
193
+ return businessRefusal(10005, "目录不存在");
194
+ const hasChildFolder = [...ctx.state.folders.values()].some((folder) => folder.parentId === folderId);
195
+ const hasChildModel = [...ctx.state.models.values()].some((model) => model.parentId === folderId);
196
+ if (hasChildFolder || hasChildModel)
197
+ return businessRefusal(10005, "目录不为空,无法删除");
198
+ ctx.state.folders.delete(folderId);
199
+ return null;
200
+ }
108
201
  case "/api/api/app/bizmodels/update": {
109
202
  // Ticket 83 (source-pinned BizSchemaController.update :266-305): the
110
203
  // endpoint patches the AppFunction MENU NODE — bizSchemaCode, id, name,
@@ -126,7 +219,10 @@ export const modelRoute = (ctx, input) => {
126
219
  return businessRefusal(10005, "父id不可为空");
127
220
  if (nodeId !== model.id)
128
221
  return businessRefusal(10005, `id不是模型 ${schemaCode} 的应用树节点`);
222
+ if (parentId !== (model.parentId ?? "folder-1") && !ctx.state.folders.has(parentId) && parentId !== "folder-1")
223
+ return businessRefusal(10005, `目录不存在: ${parentId}`);
129
224
  model.nodeName = name;
225
+ model.parentId = parentId;
130
226
  return null;
131
227
  }
132
228
  default:
package/dist/state.d.ts CHANGED
@@ -277,6 +277,15 @@ export interface FakeCallLogEntry {
277
277
  bytes: number;
278
278
  };
279
279
  }
280
+ /** App-tree folder node (h_app_function type=Folder) — issue 2026-10-23. */
281
+ export interface FakeFolder {
282
+ id: string;
283
+ appCode: string;
284
+ name: string;
285
+ name_i18n?: string;
286
+ code?: string;
287
+ parentId?: string;
288
+ }
280
289
  export interface FakeState {
281
290
  fields: Map<string, FakeField>;
282
291
  dictionaries: Map<string, FakeDictionary>;
@@ -289,6 +298,9 @@ export interface FakeState {
289
298
  sheets: Map<string, FakeSheet>;
290
299
  rules: Map<string, FakeRule>;
291
300
  buttons: Map<string, FakeButton>;
301
+ folders: Map<string, FakeFolder>;
302
+ /** Schema codes for which bizproperty/list answers 601003 (see field.ts). */
303
+ fieldListUnavailable: Set<string>;
292
304
  /**
293
305
  * Ticket 109 platform fidelity: every businessrule create/update mints a NEW
294
306
  * record id — superseded rows stay readable via businessrule/get?id=
@@ -404,6 +416,7 @@ export interface FakeState {
404
416
  appCode?: string;
405
417
  treeHidden?: boolean;
406
418
  nodeName?: string;
419
+ parentId?: string;
407
420
  }>;
408
421
  apps: Map<string, FakeApp>;
409
422
  appGroups: Map<string, FakeAppGroup>;
package/dist/state.js CHANGED
@@ -34,6 +34,11 @@ export function createFakeState(options = {}) {
34
34
  sheets: new Map(),
35
35
  rules: new Map(),
36
36
  buttons: new Map(),
37
+ // model.create resolves the first Folder in the tree when no folderId is
38
+ // given; this row is that default. Its parent is the app package (implicit
39
+ // root), so it is a top-level tree node.
40
+ folders: new Map([["folder-1", { id: "folder-1", appCode: "test", name: "General", code: "general" }]]),
41
+ fieldListUnavailable: new Set(),
37
42
  ruleHistory: [],
38
43
  ruleSeq: 0,
39
44
  dataRules: new Map(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stalfh233/omc-fake-platform",
3
- "version": "0.4.75",
3
+ "version": "0.4.77",
4
4
  "description": "OMC 的内存假 CloudPivot 平台(wire 接缝的第二个适配器)+ HTTP 假服务器——@stalfh233/omc-cli 的测试与 bench 基准基座",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "build": "tsc --project tsconfig.build.json"
19
19
  },
20
20
  "dependencies": {
21
- "omc-core": "0.4.75"
21
+ "omc-core": "0.4.77"
22
22
  },
23
23
  "engines": {
24
24
  "node": ">=22.13"