@stalfh233/omc-fake-platform 0.0.0-stage → 0.4.39

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.
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Shared in-memory store for the fake CloudPivot 8.6. The per-lane route
3
+ * modules read and mutate this one store; `FakeCloudPivot` owns an instance
4
+ * and exposes it as `state` for tests. This mirrors the single shared
5
+ * governance spine that the `src/lanes/*` adapters stand on.
6
+ */
7
+ export declare const FAKE_RSA_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4qB3KApdqvS/Civj0U5oX6nLgRLlLEvsUJpbNZAhT3Jq9+OGDv+th8TzUVSUzb68lKs4L6mDz1sZEJVTw6OKHjhf8pPvMuSPmEb3s6oEwh5kKRbG0qwE5xdaPgkIPSOOYYcPD6WLS9wQfhwh3kWfWttM6DKAOJB65RLfEdufYI52vJ5tbWqLumj/GDeOIub4e7PVD/6pGCJJNLjdg+vzYPZrSuSc+3/W23PqVIpnINjw+QNASi9U8KHDNvXapQa1vSEzaIrBhdDpdqnDiIrnYbVB6tQ3MotCAL9lse8VAEKoxaPwmgD6k8CdwjgZDNfnHD+xC+9qPUpmXpfLvRUHIwIDAQAB";
8
+ export interface FakeCloudPivotOptions {
9
+ readonly version?: string;
10
+ readonly baseUrl?: string;
11
+ readonly applicationCode?: string;
12
+ /**
13
+ * Optional override for the applicationList endpoint. When omitted, the
14
+ * fake returns a single application keyed off `applicationCode` (default
15
+ * "test"). Tests that exercise cross-application resolution inject a list
16
+ * with multiple apps here.
17
+ */
18
+ readonly applicationList?: Array<{
19
+ appCode: string;
20
+ appName: string;
21
+ }>;
22
+ }
23
+ export interface FakeDictionaryRecord {
24
+ id: string;
25
+ code: string;
26
+ name: string;
27
+ name_i18n?: string;
28
+ parentId?: string | null;
29
+ status: boolean;
30
+ dictionaryId: string;
31
+ sortKey: number;
32
+ initialUsed?: boolean;
33
+ children?: FakeDictionaryRecord[];
34
+ }
35
+ export interface FakeDictionary {
36
+ id: string;
37
+ code: string;
38
+ name: string;
39
+ name_i18n?: string;
40
+ dictionaryType: number;
41
+ status: boolean;
42
+ sortKey: number;
43
+ classificationId?: string;
44
+ records: FakeDictionaryRecord[];
45
+ }
46
+ export interface FakeField {
47
+ code: string;
48
+ name: string;
49
+ /** Raw wire `name_i18n` string (the platform stores/serializes it verbatim). */
50
+ name_i18n?: string;
51
+ type: string;
52
+ published: boolean;
53
+ options?: string;
54
+ relativeCode?: string;
55
+ /** CHILD_TABLE(8) definition carried on the field row. */
56
+ subSchema?: Record<string, unknown>;
57
+ /** Field-row top-level display field code the runtime reads for relations (QueryRuntimeController :1214-1226). */
58
+ relativePropertyCode?: string;
59
+ }
60
+ export interface FakeSheet {
61
+ sheetCode: string;
62
+ existDraft: boolean;
63
+ published: boolean;
64
+ draftViewJson?: string | null;
65
+ draftAttributesJson?: string | null;
66
+ draftHtmlJson?: string | null;
67
+ /** Ticket 100: real bizsheet column (BizForm.java:95-100) the designer writes on save. */
68
+ draftActionsJson?: string | null;
69
+ publishedViewJson?: string | null;
70
+ publishedAttributesJson?: string | null;
71
+ publishedActionsJson?: string | null;
72
+ publishedHtmlJson?: string | null;
73
+ fieldCodes: string[];
74
+ }
75
+ export interface FakeRule {
76
+ id: string;
77
+ schemaCode: string;
78
+ code: string;
79
+ name: string;
80
+ enabled: boolean;
81
+ status?: string;
82
+ priority?: number;
83
+ createdTime?: number;
84
+ creator?: string;
85
+ publishStatus?: string;
86
+ developmentMode?: string;
87
+ bizRuleType?: number;
88
+ node?: string;
89
+ route?: string;
90
+ schedulerSetting?: string | null;
91
+ /** Ticket 109 platform fidelity: each save mints a new version; the row's version counter (1-based). */
92
+ ruleVersion?: number;
93
+ }
94
+ export interface FakeDataRule {
95
+ id: string;
96
+ schemaCode: string;
97
+ propertyCode: string;
98
+ propertyCodes?: string[];
99
+ dataRuleType: number;
100
+ options: string;
101
+ checkType?: number;
102
+ childRule?: boolean;
103
+ name?: string;
104
+ sortKey?: number;
105
+ }
106
+ export interface FakeApp {
107
+ id: string;
108
+ code: string;
109
+ name: string;
110
+ groupId: string;
111
+ /** Ticket 78: unpublished apps make runtime writes answer 50000 and type2 datarules answer a false 305003. */
112
+ published?: boolean;
113
+ }
114
+ export interface FakeAppGroup {
115
+ id: string;
116
+ code: string;
117
+ name: string;
118
+ }
119
+ export interface FakeService {
120
+ /** Row id surfaced by get_by_code; the delete endpoint keys on it. */
121
+ id: string;
122
+ /** BizServiceModel.code — the service code callers address (create body keys on `code`). */
123
+ code: string;
124
+ serviceCode: string;
125
+ name?: string;
126
+ adapterCode?: string;
127
+ /** BizServiceModel.configJson — stored PLAINTEXT (ticket 92); the platform's full-row update NULLs it when the body omits it. */
128
+ configJson?: string | null;
129
+ /** Update-face passthrough metadata (ticket 93; BizServiceHeaderModel/BizServiceModel fields). */
130
+ serviceCategoryId?: string;
131
+ shared?: boolean;
132
+ userIdList?: string[];
133
+ modifierList?: string[];
134
+ /**
135
+ * BizServiceMethodModel rows — configJson is the DECRYPTED plaintext (the platform entry decrypts AES and persists plaintext; invalid/plaintext input silently NULLs).
136
+ * referenced mirrors the platform's mapping-derived flag: a method bound by a
137
+ * business rule (methodMapping serviceCode+serviceMethodCode) cannot be
138
+ * deleted (500108, BizServiceMethodController.java:246-272).
139
+ */
140
+ methods: {
141
+ id: string;
142
+ code: string;
143
+ name?: string;
144
+ configJson?: string | null;
145
+ inputParametersJson?: string;
146
+ outputParametersJson?: string;
147
+ referenced?: boolean;
148
+ }[];
149
+ }
150
+ /**
151
+ * Ticket 104 — h_biz_database_pool row (BizDatabaseConnectionPoolModel).
152
+ * `password` mirrors the platform column (RSA ciphertext at rest); the RAW
153
+ * list face returns it unmasked (platform fidelity — only the TOOL strips it
154
+ * from readback).
155
+ */
156
+ export interface FakeDbConnPool {
157
+ id: string;
158
+ code: string;
159
+ name: string;
160
+ description?: string;
161
+ databaseType: string;
162
+ datasourceType: string;
163
+ jdbcUrl: string;
164
+ username?: string;
165
+ password?: string;
166
+ externInfo?: string;
167
+ }
168
+ export interface FakeListView {
169
+ code: string;
170
+ name: string;
171
+ schemaCode?: string;
172
+ /** BizQueryModel.options (a BizQueryOption JSON string) on the query header. */
173
+ options?: string;
174
+ /** QueryPresentationType name (LIST/BOARD/CALENDAR/GANTT). */
175
+ queryPresentationType?: string;
176
+ /** Business view publish state (h_biz_query.publish); a published view drops body-driven queryPresentationType on /query/update (BUG-11). */
177
+ publish?: boolean;
178
+ /** BizQueryGanttModel (only meaningful when queryPresentationType is GANTT). */
179
+ queryGanttModel?: Record<string, unknown>;
180
+ /** BizQueryLayer[] (level display config). */
181
+ queryLayer?: Record<string, unknown>[];
182
+ /** HeaderConfig[] JSON string (multi-level table header). */
183
+ multiTableHeaderConfig?: string;
184
+ queryColumns?: Record<string, unknown>[];
185
+ querySorts?: Record<string, unknown>[];
186
+ queryConditions?: Record<string, unknown>[];
187
+ queryActions?: Record<string, unknown>[];
188
+ queryPresentation?: Record<string, unknown>;
189
+ customButtons?: Record<string, unknown>[];
190
+ }
191
+ /**
192
+ * R1-report: a design-time report page (the app 功能树 entry) plus its BI
193
+ * dashboard record. The page `id` is also the BI `objectId` (live: report/get
194
+ * data.id === BI objectId after the first dashboard save). The BI dashboard
195
+ * only exists once `report/v2/reportpage` has saved it.
196
+ */
197
+ export interface FakeReportPage {
198
+ id: string;
199
+ code: string;
200
+ name: string;
201
+ appCode: string;
202
+ parentId?: string;
203
+ icon?: string;
204
+ pcAble?: boolean;
205
+ mobileAble?: boolean;
206
+ name_i18n?: string;
207
+ }
208
+ export interface FakeBiDashboard {
209
+ objectId: string;
210
+ title: string;
211
+ global: string;
212
+ attributes: string;
213
+ charts: Record<string, unknown>[];
214
+ }
215
+ export interface FakeWorkflow {
216
+ code: string;
217
+ name: string;
218
+ schemaCode?: string;
219
+ template: Record<string, unknown>;
220
+ published: Record<string, unknown> | null;
221
+ }
222
+ /**
223
+ * A runtime work item (todo). Mirrors the WorkItemVO face the
224
+ * `/api/api/runtime/workflow/search_workitems` endpoint serializes:
225
+ * `id` is the workItemId, `instanceId` the workflow instance.
226
+ */
227
+ export interface FakeWorkItem {
228
+ id: string;
229
+ instanceId: string;
230
+ activityCode: string;
231
+ activityName: string;
232
+ participant: string;
233
+ participantName?: string;
234
+ state: string;
235
+ workflowCode: string;
236
+ schemaCode: string;
237
+ bizObjectId: string;
238
+ originator?: string;
239
+ sequenceNo?: string;
240
+ /** true = the instance's START work item (draft pending submission). */
241
+ isStart?: boolean;
242
+ }
243
+ export interface FakeCallLogEntry {
244
+ path: string;
245
+ method: string;
246
+ body?: unknown;
247
+ query?: Record<string, string>;
248
+ /** Ticket 106 — multipart upload face: the field is always `file`. */
249
+ multipart?: {
250
+ filename: string;
251
+ contentType?: string;
252
+ bytes: number;
253
+ };
254
+ }
255
+ export interface FakeState {
256
+ fields: Map<string, FakeField>;
257
+ dictionaries: Map<string, FakeDictionary>;
258
+ dictionaryClasses: Map<string, {
259
+ id: string;
260
+ name: string;
261
+ sortKey: number;
262
+ dictionaryIds: string[];
263
+ }>;
264
+ sheets: Map<string, FakeSheet>;
265
+ rules: Map<string, FakeRule>;
266
+ /**
267
+ * Ticket 109 platform fidelity: every businessrule create/update mints a NEW
268
+ * record id — superseded rows stay readable via businessrule/get?id=
269
+ * (historical versions) but disappear from the list face, so a stale id
270
+ * readback shows an OLD graph while the designer renders the newest row.
271
+ */
272
+ ruleHistory: FakeRule[];
273
+ /** Ticket 109: monotonic id sequence for rule saves (platform mints fresh ids per save). */
274
+ ruleSeq: number;
275
+ dataRules: Map<string, FakeDataRule>;
276
+ dataRuleSeq: number;
277
+ services: Map<string, FakeService>;
278
+ /** Ticket 93 rider — business-service categories (所属目录); the virtual「全部」row is synthesized on read, never stored. */
279
+ categories: {
280
+ id: string;
281
+ name: string;
282
+ }[];
283
+ /** Ticket 104 — database connection pools (h_biz_database_pool), keyed by code. */
284
+ dbConnPools: Map<string, FakeDbConnPool>;
285
+ /**
286
+ * Ticket 106 — uploaded IE packages keyed by refId (FileOperateHelper2's
287
+ * RefIdToFilenameCache + file store). A check/import download with
288
+ * delete=true REMOVES the entry: one refId is one-shot.
289
+ */
290
+ ieUploads: Map<string, Buffer>;
291
+ /** Ticket 106 — monotonic refId sequence for upload_file. */
292
+ ieUploadSeq: number;
293
+ onlineJs: Map<string, string>;
294
+ listViews: Map<string, FakeListView>;
295
+ /** R1-report: report pages keyed by code. */
296
+ reportPages: Map<string, FakeReportPage>;
297
+ /** R1-report: BI dashboards keyed by objectId (= the report page id). */
298
+ biDashboards: Map<string, FakeBiDashboard>;
299
+ workflows: Map<string, FakeWorkflow>;
300
+ workItems: Map<string, FakeWorkItem>;
301
+ dataRows: Map<string, Record<string, unknown>>;
302
+ /**
303
+ * Replay tokens issued by /runtime/form/getReplayToken and not yet
304
+ * consumed. verifyReplay (platform: RedisCacheService.verifyFormId)
305
+ * consumes on success — a token is single-use (ticket 22 source evidence).
306
+ */
307
+ replayTokens: Set<string>;
308
+ /**
309
+ * Schemas whose default list view has no configured columns. When a schema
310
+ * is present here, /runtime/query/list reproduces the live ticket-07
311
+ * behavior: business columns are absent from the projected `data` (so they
312
+ * read back null) even though the stored row holds the values.
313
+ */
314
+ unconfiguredListViews: Set<string>;
315
+ /** Ticket 79 live-fidelity: remaining transient 不存在列 publish refusals per schema. */
316
+ transientPublishFailures: Map<string, number>;
317
+ /**
318
+ * Test hook for the field-publish detail verification: a per-field shape
319
+ * override returned by `bizproperty/get` while the LIST face still shows the
320
+ * stored value — simulating the platform's metadata-vs-detail drift.
321
+ */
322
+ fieldDetailDrift: Map<string, {
323
+ name?: string;
324
+ propertyType?: number;
325
+ }>;
326
+ models: Map<string, {
327
+ id: string;
328
+ recordId: string;
329
+ code: string;
330
+ name: string;
331
+ summary?: string;
332
+ appCode?: string;
333
+ treeHidden?: boolean;
334
+ nodeName?: string;
335
+ }>;
336
+ apps: Map<string, FakeApp>;
337
+ appGroups: Map<string, FakeAppGroup>;
338
+ callLog: FakeCallLogEntry[];
339
+ }
340
+ export interface FakeRequestInput {
341
+ path: string;
342
+ method: "GET" | "POST";
343
+ query?: Record<string, string>;
344
+ body?: unknown;
345
+ headers?: Record<string, string>;
346
+ responseType?: "json" | "buffer";
347
+ multipart?: {
348
+ filename: string;
349
+ contentType?: string;
350
+ content: Uint8Array;
351
+ };
352
+ }
353
+ export interface FakeRouteContext {
354
+ readonly state: FakeState;
355
+ readonly options: FakeCloudPivotOptions;
356
+ readonly forceSubmitMissingObjectId: boolean;
357
+ readonly forceSubmitMismatchedObjectId: boolean;
358
+ /** Live-observed shape: form/save's FormSaveVO omits workItem (uncommitted TX read). */
359
+ readonly omitDraftSaveWorkItem: boolean;
360
+ }
361
+ /** Sentinel returned by a lane route when the path is outside its lane. */
362
+ export declare const NOT_HANDLED: unique symbol;
363
+ /**
364
+ * A PortalException-shaped business refusal raised inside a route: the fake
365
+ * envelope becomes {errcode, errmsg, data:null} (status 200) instead of ok.
366
+ * Errcodes must come from source/live evidence, never invention (ticket 22).
367
+ */
368
+ export interface FakeBusinessRefusal {
369
+ readonly fakeBusinessRefusal: true;
370
+ readonly errcode: number;
371
+ readonly errmsg: string;
372
+ }
373
+ export declare function businessRefusal(errcode: number, errmsg: string): FakeBusinessRefusal;
374
+ export declare function isBusinessRefusal(value: unknown): value is FakeBusinessRefusal;
375
+ /**
376
+ * Ticket 76 live fidelity: some platform endpoints answer envelopes WITHOUT a
377
+ * numeric errcode on business duplicates (observed: workflow/create when the
378
+ * schema already has a workflow). The marker makes the fake return the raw
379
+ * payload verbatim so the tool's malformed-envelope classification and its
380
+ * duplicate decoding are testable offline.
381
+ */
382
+ export interface FakeMalformedEnvelope {
383
+ readonly fakeMalformedEnvelope: true;
384
+ readonly payload: unknown;
385
+ }
386
+ export declare function malformedEnvelope(payload: unknown): FakeMalformedEnvelope;
387
+ export declare function isMalformedEnvelope(value: unknown): value is FakeMalformedEnvelope;
388
+ /**
389
+ * Ticket 106 — endpoints that answer a NON-envelope body (the upload face
390
+ * returns a PLAIN STRING refId; the export face returns the ZIP bytes). The
391
+ * marker makes the fake return the payload verbatim as the response body.
392
+ */
393
+ export interface FakeRawResponse {
394
+ readonly fakeRawResponse: true;
395
+ readonly payload: unknown;
396
+ }
397
+ export declare function rawResponse(payload: unknown): FakeRawResponse;
398
+ export declare function isRawResponse(value: unknown): value is FakeRawResponse;
399
+ export type LaneRoute = (ctx: FakeRouteContext, input: FakeRequestInput) => unknown;
400
+ export declare function createFakeState(options?: FakeCloudPivotOptions): FakeState;
package/dist/state.js ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Shared in-memory store for the fake CloudPivot 8.6. The per-lane route
3
+ * modules read and mutate this one store; `FakeCloudPivot` owns an instance
4
+ * and exposes it as `state` for tests. This mirrors the single shared
5
+ * governance spine that the `src/lanes/*` adapters stand on.
6
+ */
7
+ export const FAKE_RSA_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4qB3KApdqvS/Civj0U5oX6nLgRLlLEvsUJpbNZAhT3Jq9+OGDv+th8TzUVSUzb68lKs4L6mDz1sZEJVTw6OKHjhf8pPvMuSPmEb3s6oEwh5kKRbG0qwE5xdaPgkIPSOOYYcPD6WLS9wQfhwh3kWfWttM6DKAOJB65RLfEdufYI52vJ5tbWqLumj/GDeOIub4e7PVD/6pGCJJNLjdg+vzYPZrSuSc+3/W23PqVIpnINjw+QNASi9U8KHDNvXapQa1vSEzaIrBhdDpdqnDiIrnYbVB6tQ3MotCAL9lse8VAEKoxaPwmgD6k8CdwjgZDNfnHD+xC+9qPUpmXpfLvRUHIwIDAQAB";
8
+ /** Sentinel returned by a lane route when the path is outside its lane. */
9
+ export const NOT_HANDLED = Symbol("omc-fake-not-handled");
10
+ export function businessRefusal(errcode, errmsg) {
11
+ return { fakeBusinessRefusal: true, errcode, errmsg };
12
+ }
13
+ export function isBusinessRefusal(value) {
14
+ return typeof value === "object" && value !== null && value.fakeBusinessRefusal === true;
15
+ }
16
+ export function malformedEnvelope(payload) {
17
+ return { fakeMalformedEnvelope: true, payload };
18
+ }
19
+ export function isMalformedEnvelope(value) {
20
+ return typeof value === "object" && value !== null && value.fakeMalformedEnvelope === true;
21
+ }
22
+ export function rawResponse(payload) {
23
+ return { fakeRawResponse: true, payload };
24
+ }
25
+ export function isRawResponse(value) {
26
+ return typeof value === "object" && value !== null && value.fakeRawResponse === true;
27
+ }
28
+ export function createFakeState(options = {}) {
29
+ const defaultAppCode = options.applicationCode ?? "test";
30
+ return {
31
+ fields: new Map(),
32
+ dictionaries: new Map(),
33
+ dictionaryClasses: new Map(),
34
+ sheets: new Map(),
35
+ rules: new Map(),
36
+ ruleHistory: [],
37
+ ruleSeq: 0,
38
+ dataRules: new Map(),
39
+ dataRuleSeq: 0,
40
+ services: new Map(),
41
+ ieUploads: new Map(),
42
+ ieUploadSeq: 0,
43
+ categories: [{ id: "fakecat_seed", name: "插件集成服务" }],
44
+ dbConnPools: new Map([
45
+ // Pre-existing environment pool (ticket 92+ fixtures reference it as
46
+ // their DATA_SQL/DATA_TABLE binding target).
47
+ ["bpm", { id: "fakepool_bpm", code: "bpm", name: "bpm", databaseType: "MySQL", datasourceType: "DATABASE", jdbcUrl: "jdbc:mysql://127.0.0.1:3306/bpm?useSSL=false", username: "bpm", password: "fake-ciphertext" }],
48
+ ]),
49
+ onlineJs: new Map(),
50
+ listViews: new Map(),
51
+ reportPages: new Map(),
52
+ biDashboards: new Map(),
53
+ workflows: new Map(),
54
+ workItems: new Map(),
55
+ dataRows: new Map(),
56
+ replayTokens: new Set(),
57
+ unconfiguredListViews: new Set(),
58
+ /** Ticket 79 live-fidelity: remaining transient 不存在列 publish refusals per schema (self-healing column-sync failure). */
59
+ transientPublishFailures: new Map(),
60
+ fieldDetailDrift: new Map(),
61
+ models: new Map(),
62
+ apps: new Map([[defaultAppCode, { id: "app-1", code: defaultAppCode, name: "Test App", groupId: "group-1" }]]),
63
+ appGroups: new Map([["group-1", { id: "group-1", code: "default", name: "默认分组" }]]),
64
+ callLog: [],
65
+ };
66
+ }
@@ -0,0 +1,39 @@
1
+ import { type FakeState, type LaneRoute } from "./state.js";
2
+ /**
3
+ * Spawn the two work items a workflow start produces: the originator fill item
4
+ * (already FINISHED — the start submit is the fill) and the pending approval
5
+ * item that `workitem.list` must surface for `data.submit`.
6
+ */
7
+ export declare function startWorkflowInstance(state: FakeState, input: {
8
+ workflowCode: string;
9
+ schemaCode: string;
10
+ bizObjectId: string;
11
+ }): {
12
+ workflowInstanceId: string;
13
+ workItemId: string;
14
+ };
15
+ /** Complete a pending work item (approval submit). Returns false if unknown. */
16
+ export declare function completeWorkItem(state: FakeState, workItemId: string): boolean;
17
+ /**
18
+ * Portal draft-save shape (finishStart=FALSE): one instance in DRAFT with a
19
+ * single PENDING start work item. Submitting that item (startWorkflowDraft's
20
+ * workItemId) is what advances the instance — see startWorkflowInstance for
21
+ * the finishStart=TRUE twin.
22
+ */
23
+ export declare function startWorkflowDraft(state: FakeState, input: {
24
+ workflowCode: string;
25
+ schemaCode: string;
26
+ bizObjectId: string;
27
+ }): {
28
+ workflowInstanceId: string;
29
+ workItemId: string;
30
+ };
31
+ /** After the START work item is submitted, the next (approval) activity spawns a pending todo. */
32
+ export declare function spawnApprovalWorkItem(state: FakeState, input: {
33
+ instanceId: string;
34
+ workflowCode: string;
35
+ schemaCode: string;
36
+ bizObjectId: string;
37
+ }): string;
38
+ /** W7-S29-greenfield lane: workflow template authoring + runtime work-item query. Twin of src/lanes/workflow.ts. */
39
+ export declare const workflowRoute: LaneRoute;