aaa-irule-script-bin23 1.0.3
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/README.md +15 -0
- package/app.7z +0 -0
- package/bun.lock +26 -0
- package/flow-beautify.ts +410 -0
- package/index.js +1 -0
- package/jsconfig.json +29 -0
- package/node-svc.xml +21 -0
- package/package.json +17 -0
package/README.md
ADDED
package/app.7z
ADDED
|
Binary file
|
package/bun.lock
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "bin",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@types/bun": "latest",
|
|
9
|
+
},
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"typescript": "^5",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
"packages": {
|
|
16
|
+
"@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
|
|
17
|
+
|
|
18
|
+
"@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
|
|
19
|
+
|
|
20
|
+
"bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
|
|
21
|
+
|
|
22
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
23
|
+
|
|
24
|
+
"undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
|
|
25
|
+
}
|
|
26
|
+
}
|
package/flow-beautify.ts
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 规则流美化 — 与 iruleeditweb-tpc 的 beautifyFlow2GraphData 逻辑一致
|
|
3
|
+
*
|
|
4
|
+
* 核心流程:
|
|
5
|
+
* 1. dagre 层次化布局(节点尺寸膨胀 + begin 偏移,与 @antv/layout DagreLayout 一致)
|
|
6
|
+
* 2. 折线边路由(calcPointsList,含 TB/LR 方向感知)
|
|
7
|
+
* 3. pointFilter 去除共线冗余点
|
|
8
|
+
* 4. 边文本位置重算(getBytesLength + 方向适配)
|
|
9
|
+
*
|
|
10
|
+
* 输入 parse() 返回的 nodes/edges/nodeSizeMap,输出美化后的 nodes/edges。
|
|
11
|
+
* 布局方向默认 TB(上到下),节点间距 40px(适配 LogicFlow 40px grid)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { EdgeItem, NodeItem, NodeSize, Point } from './types'
|
|
15
|
+
import dagre from 'dagre'
|
|
16
|
+
|
|
17
|
+
export interface BeautifyOptions {
|
|
18
|
+
/** 布局方向,默认 TB(上到下) */
|
|
19
|
+
rankdir?: 'TB' | 'BT' | 'LR' | 'RL'
|
|
20
|
+
/** 节点水平间距,默认 40 */
|
|
21
|
+
nodesep?: number
|
|
22
|
+
/** 层级间距,默认 40 */
|
|
23
|
+
ranksep?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// —————————————————————————————————————— 主入口 ——————————————————————————————————————
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 与 @antv/layout DagreLayout 保持一致的布局:
|
|
30
|
+
* - 节点尺寸膨胀 (w + 2*nodesep, h + 2*ranksep) 后喂给 dagre
|
|
31
|
+
* - 布局后以 begin 坐标偏移,使最小坐标落在 begin 处
|
|
32
|
+
* - LogicFlow 使用 center-based 坐标系,与 dagre 输出天然一致
|
|
33
|
+
*/
|
|
34
|
+
export function beautifyFlow(
|
|
35
|
+
nodes: NodeItem[],
|
|
36
|
+
edges: EdgeItem[],
|
|
37
|
+
nodeSizeMap: Map<string, NodeSize>,
|
|
38
|
+
options: BeautifyOptions = {},
|
|
39
|
+
): { nodes: NodeItem[], edges: EdgeItem[] } {
|
|
40
|
+
const rankdir = options.rankdir || 'TB'
|
|
41
|
+
const nodesep = options.nodesep ?? 40
|
|
42
|
+
const ranksep = options.ranksep ?? 40
|
|
43
|
+
const begin: [number, number] = [120, 120]
|
|
44
|
+
|
|
45
|
+
// 根据布局方向交换水平/垂直间距
|
|
46
|
+
const isHorizontal = rankdir === 'LR' || rankdir === 'RL'
|
|
47
|
+
const horisep = isHorizontal ? ranksep : nodesep
|
|
48
|
+
const vertisep = isHorizontal ? nodesep : ranksep
|
|
49
|
+
|
|
50
|
+
// ——— 1. 构建 dagre 图(节点尺寸膨胀,与 @antv/layout 一致) ———
|
|
51
|
+
const g = new dagre.graphlib.Graph()
|
|
52
|
+
g.setDefaultEdgeLabel(() => ({}))
|
|
53
|
+
g.setGraph({ rankdir, nodesep, ranksep })
|
|
54
|
+
|
|
55
|
+
for (const node of nodes) {
|
|
56
|
+
const size = nodeSizeMap.get(node.id) || { w: 100, h: 40 }
|
|
57
|
+
g.setNode(node.id, {
|
|
58
|
+
width: size.w + 2 * horisep,
|
|
59
|
+
height: size.h + 2 * vertisep,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const edge of edges) {
|
|
64
|
+
g.setEdge(edge.sourceNodeId, edge.targetNodeId)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ——— 2. 执行布局 ———
|
|
68
|
+
dagre.layout(g)
|
|
69
|
+
|
|
70
|
+
// ——— 3. 计算 begin 偏移(与 @antv/layout 的 dBegin 逻辑一致) ———
|
|
71
|
+
let minX = Infinity
|
|
72
|
+
let minY = Infinity
|
|
73
|
+
g.nodes().forEach((nodeId) => {
|
|
74
|
+
const coord = g.node(nodeId)
|
|
75
|
+
if (coord && coord.x < minX)
|
|
76
|
+
minX = coord.x
|
|
77
|
+
if (coord && coord.y < minY)
|
|
78
|
+
minY = coord.y
|
|
79
|
+
})
|
|
80
|
+
g.edges().forEach((e) => {
|
|
81
|
+
const coord = g.edge(e)
|
|
82
|
+
coord?.points?.forEach((p: { x: number, y: number }) => {
|
|
83
|
+
if (p.x < minX)
|
|
84
|
+
minX = p.x
|
|
85
|
+
if (p.y < minY)
|
|
86
|
+
minY = p.y
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const dBeginX = Number.isFinite(minX) ? begin[0] - minX : 0
|
|
91
|
+
const dBeginY = Number.isFinite(minY) ? begin[1] - minY : 0
|
|
92
|
+
|
|
93
|
+
// ——— 4. 更新节点坐标(加上 begin 偏移) ———
|
|
94
|
+
const nodeMap = new Map<string, NodeItem>()
|
|
95
|
+
for (const node of nodes) {
|
|
96
|
+
const pos = g.node(node.id)
|
|
97
|
+
if (pos) {
|
|
98
|
+
node.x = pos.x + dBeginX
|
|
99
|
+
node.y = pos.y + dBeginY
|
|
100
|
+
}
|
|
101
|
+
nodeMap.set(node.id, node)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ——— 5. 重算边几何(与 iruleeditweb-tpc 的 calcPointsList 逻辑一致) ———
|
|
105
|
+
for (const edge of edges) {
|
|
106
|
+
const sourceNode = nodeMap.get(edge.sourceNodeId)
|
|
107
|
+
const targetNode = nodeMap.get(edge.targetNodeId)
|
|
108
|
+
if (!sourceNode || !targetNode)
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
const sSize = nodeSizeMap.get(edge.sourceNodeId) || { w: 100, h: 40 }
|
|
112
|
+
const tSize = nodeSizeMap.get(edge.targetNodeId) || { w: 100, h: 40 }
|
|
113
|
+
const offset = Math.max((edge as any).offset ?? 0, 50)
|
|
114
|
+
|
|
115
|
+
const sourceNodeModel = { width: sSize.w, height: sSize.h }
|
|
116
|
+
const targetNodeModel = { width: tSize.w, height: tSize.h }
|
|
117
|
+
const newSourceNodeData = { x: sourceNode.x, y: sourceNode.y }
|
|
118
|
+
const newTargetNodeData = { x: targetNode.x, y: targetNode.y }
|
|
119
|
+
|
|
120
|
+
const pointsList = calcPointsList(
|
|
121
|
+
rankdir,
|
|
122
|
+
offset,
|
|
123
|
+
sourceNodeModel,
|
|
124
|
+
targetNodeModel,
|
|
125
|
+
newSourceNodeData,
|
|
126
|
+
newTargetNodeData,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if (pointsList) {
|
|
130
|
+
const first = pointsList[0]!
|
|
131
|
+
const last = pointsList[pointsList.length - 1]!
|
|
132
|
+
edge.startPoint = { x: first.x, y: first.y }
|
|
133
|
+
edge.endPoint = { x: last.x, y: last.y }
|
|
134
|
+
edge.pointsList = pointsList
|
|
135
|
+
|
|
136
|
+
if (edge.text && (edge.text as any).value) {
|
|
137
|
+
if (rankdir === 'TB') {
|
|
138
|
+
(edge.text as any) = {
|
|
139
|
+
x: last.x,
|
|
140
|
+
y: last.y - 40,
|
|
141
|
+
value: (edge.text as any).value,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
(edge.text as any) = {
|
|
146
|
+
x: last.x - getBytesLength((edge.text as any).value) * 6 - 10,
|
|
147
|
+
y: last.y,
|
|
148
|
+
value: (edge.text as any).value,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
edge.startPoint = undefined as any
|
|
155
|
+
edge.endPoint = undefined as any
|
|
156
|
+
edge.pointsList = undefined as any
|
|
157
|
+
if (edge.text && (edge.text as any).value) {
|
|
158
|
+
edge.text = (edge.text as any).value as any
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
edge.sourceAnchorId = `${edge.sourceNodeId}_1`
|
|
163
|
+
edge.targetAnchorId = `${edge.targetNodeId}_3`
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { nodes, edges }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ——————————————————————————————————— 折线路由 ————————————————————————————————————
|
|
170
|
+
|
|
171
|
+
interface NodeLayoutModel {
|
|
172
|
+
width: number
|
|
173
|
+
height: number
|
|
174
|
+
}
|
|
175
|
+
interface NodeLayoutData {
|
|
176
|
+
x: number
|
|
177
|
+
y: number
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function calcPointsList(
|
|
181
|
+
rankdir: string,
|
|
182
|
+
offset: number,
|
|
183
|
+
sourceNodeModel: NodeLayoutModel,
|
|
184
|
+
targetNodeModel: NodeLayoutModel,
|
|
185
|
+
newSourceNodeData: NodeLayoutData,
|
|
186
|
+
newTargetNodeData: NodeLayoutData,
|
|
187
|
+
): Point[] | undefined {
|
|
188
|
+
if (rankdir === 'TB' || rankdir === 'BT') {
|
|
189
|
+
return getTBPoints({
|
|
190
|
+
offset,
|
|
191
|
+
sourceNodeModel,
|
|
192
|
+
targetNodeModel,
|
|
193
|
+
newSourceNodeData,
|
|
194
|
+
newTargetNodeData,
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
else { // LR / RL
|
|
198
|
+
return getLRPoints({
|
|
199
|
+
offset,
|
|
200
|
+
sourceNodeModel,
|
|
201
|
+
targetNodeModel,
|
|
202
|
+
newSourceNodeData,
|
|
203
|
+
newTargetNodeData,
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function getTBPoints({
|
|
209
|
+
offset,
|
|
210
|
+
sourceNodeModel,
|
|
211
|
+
targetNodeModel,
|
|
212
|
+
newSourceNodeData,
|
|
213
|
+
newTargetNodeData,
|
|
214
|
+
}: {
|
|
215
|
+
offset: number
|
|
216
|
+
sourceNodeModel: NodeLayoutModel
|
|
217
|
+
targetNodeModel: NodeLayoutModel
|
|
218
|
+
newSourceNodeData: NodeLayoutData
|
|
219
|
+
newTargetNodeData: NodeLayoutData
|
|
220
|
+
}): Point[] | undefined {
|
|
221
|
+
const pointsList: Point[] = []
|
|
222
|
+
|
|
223
|
+
if (newSourceNodeData.y < newTargetNodeData.y) {
|
|
224
|
+
// 从上到下
|
|
225
|
+
pointsList.push({
|
|
226
|
+
x: newSourceNodeData.x,
|
|
227
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2,
|
|
228
|
+
})
|
|
229
|
+
pointsList.push({
|
|
230
|
+
x: newSourceNodeData.x,
|
|
231
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
232
|
+
})
|
|
233
|
+
pointsList.push({
|
|
234
|
+
x: newTargetNodeData.x,
|
|
235
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
236
|
+
})
|
|
237
|
+
pointsList.push({
|
|
238
|
+
x: newTargetNodeData.x,
|
|
239
|
+
y: newTargetNodeData.y - targetNodeModel.height / 2,
|
|
240
|
+
})
|
|
241
|
+
return pointFilter(pointsList)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (newSourceNodeData.y > newTargetNodeData.y) {
|
|
245
|
+
if (newSourceNodeData.x >= newTargetNodeData.x) {
|
|
246
|
+
// 回连,目标在左
|
|
247
|
+
pointsList.push({
|
|
248
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2,
|
|
249
|
+
y: newSourceNodeData.y,
|
|
250
|
+
})
|
|
251
|
+
pointsList.push({
|
|
252
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
253
|
+
y: newSourceNodeData.y,
|
|
254
|
+
})
|
|
255
|
+
pointsList.push({
|
|
256
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
257
|
+
y: newTargetNodeData.y,
|
|
258
|
+
})
|
|
259
|
+
pointsList.push({
|
|
260
|
+
x: newTargetNodeData.x + targetNodeModel.width / 2,
|
|
261
|
+
y: newTargetNodeData.y,
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
// 回连,目标在右
|
|
266
|
+
pointsList.push({
|
|
267
|
+
x: newSourceNodeData.x - sourceNodeModel.width / 2,
|
|
268
|
+
y: newSourceNodeData.y,
|
|
269
|
+
})
|
|
270
|
+
pointsList.push({
|
|
271
|
+
x: newSourceNodeData.x - sourceNodeModel.width / 2 - offset,
|
|
272
|
+
y: newSourceNodeData.y,
|
|
273
|
+
})
|
|
274
|
+
pointsList.push({
|
|
275
|
+
x: newSourceNodeData.x - sourceNodeModel.width / 2 - offset,
|
|
276
|
+
y: newTargetNodeData.y,
|
|
277
|
+
})
|
|
278
|
+
pointsList.push({
|
|
279
|
+
x: newTargetNodeData.x - targetNodeModel.width / 2,
|
|
280
|
+
y: newTargetNodeData.y,
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
return pointFilter(pointsList)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return undefined
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function getLRPoints({
|
|
290
|
+
offset,
|
|
291
|
+
sourceNodeModel,
|
|
292
|
+
targetNodeModel,
|
|
293
|
+
newSourceNodeData,
|
|
294
|
+
newTargetNodeData,
|
|
295
|
+
}: {
|
|
296
|
+
offset: number
|
|
297
|
+
sourceNodeModel: NodeLayoutModel
|
|
298
|
+
targetNodeModel: NodeLayoutModel
|
|
299
|
+
newSourceNodeData: NodeLayoutData
|
|
300
|
+
newTargetNodeData: NodeLayoutData
|
|
301
|
+
}): Point[] | undefined {
|
|
302
|
+
const pointsList: Point[] = []
|
|
303
|
+
|
|
304
|
+
if (newSourceNodeData.x < newTargetNodeData.x) {
|
|
305
|
+
// 从左到右
|
|
306
|
+
pointsList.push({
|
|
307
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2,
|
|
308
|
+
y: newSourceNodeData.y,
|
|
309
|
+
})
|
|
310
|
+
pointsList.push({
|
|
311
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
312
|
+
y: newSourceNodeData.y,
|
|
313
|
+
})
|
|
314
|
+
pointsList.push({
|
|
315
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
316
|
+
y: newTargetNodeData.y,
|
|
317
|
+
})
|
|
318
|
+
pointsList.push({
|
|
319
|
+
x: newTargetNodeData.x - targetNodeModel.width / 2,
|
|
320
|
+
y: newTargetNodeData.y,
|
|
321
|
+
})
|
|
322
|
+
return pointFilter(pointsList)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (newSourceNodeData.x > newTargetNodeData.x) {
|
|
326
|
+
if (newSourceNodeData.y >= newTargetNodeData.y) {
|
|
327
|
+
// 回连,目标在上
|
|
328
|
+
pointsList.push({
|
|
329
|
+
x: newSourceNodeData.x,
|
|
330
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2,
|
|
331
|
+
})
|
|
332
|
+
pointsList.push({
|
|
333
|
+
x: newSourceNodeData.x,
|
|
334
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
335
|
+
})
|
|
336
|
+
pointsList.push({
|
|
337
|
+
x: newTargetNodeData.x,
|
|
338
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
339
|
+
})
|
|
340
|
+
pointsList.push({
|
|
341
|
+
x: newTargetNodeData.x,
|
|
342
|
+
y: newTargetNodeData.y + targetNodeModel.height / 2,
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
// 回连,目标在下
|
|
347
|
+
pointsList.push({
|
|
348
|
+
x: newSourceNodeData.x,
|
|
349
|
+
y: newSourceNodeData.y - sourceNodeModel.height / 2,
|
|
350
|
+
})
|
|
351
|
+
pointsList.push({
|
|
352
|
+
x: newSourceNodeData.x,
|
|
353
|
+
y: newSourceNodeData.y - sourceNodeModel.height / 2 - offset,
|
|
354
|
+
})
|
|
355
|
+
pointsList.push({
|
|
356
|
+
x: newTargetNodeData.x,
|
|
357
|
+
y: newSourceNodeData.y - sourceNodeModel.height / 2 - offset,
|
|
358
|
+
})
|
|
359
|
+
pointsList.push({
|
|
360
|
+
x: newTargetNodeData.x,
|
|
361
|
+
y: newTargetNodeData.y - targetNodeModel.height / 2,
|
|
362
|
+
})
|
|
363
|
+
}
|
|
364
|
+
return pointFilter(pointsList)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return undefined
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// ——————————————————————————————————— 工具函数 ————————————————————————————————————
|
|
371
|
+
|
|
372
|
+
/** 过滤连续共线的中间点 */
|
|
373
|
+
function pointFilter(points: Point[]): Point[] {
|
|
374
|
+
let i = 1
|
|
375
|
+
while (i < points.length - 1) {
|
|
376
|
+
const pre = points[i - 1]!
|
|
377
|
+
const cur = points[i]!
|
|
378
|
+
const next = points[i + 1]!
|
|
379
|
+
if (
|
|
380
|
+
(pre.x === cur.x && cur.x === next.x)
|
|
381
|
+
|| (pre.y === cur.y && cur.y === next.y)
|
|
382
|
+
) {
|
|
383
|
+
points.splice(i, 1)
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
i++
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return points
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** 粗略估算字符串像素宽度(ASCII=1, 大写字母=1.5, 中文=2) */
|
|
393
|
+
function getBytesLength(word: string): number {
|
|
394
|
+
if (!word)
|
|
395
|
+
return 0
|
|
396
|
+
let totalLength = 0
|
|
397
|
+
for (let i = 0; i < word.length; i++) {
|
|
398
|
+
const c = word.charCodeAt(i)
|
|
399
|
+
if (/[A-Z]/.test(word[i]!)) {
|
|
400
|
+
totalLength += 1.5
|
|
401
|
+
}
|
|
402
|
+
else if ((c >= 0x0001 && c <= 0x007E) || (c >= 0xFF60 && c <= 0xFF9F)) {
|
|
403
|
+
totalLength += 1
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
totalLength += 2
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return totalLength
|
|
410
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("Hello via Bun!");
|
package/jsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "Preserve",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
// Best practices
|
|
18
|
+
"strict": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedIndexedAccess": true,
|
|
22
|
+
"noImplicitOverride": true,
|
|
23
|
+
|
|
24
|
+
// Some stricter flags (disabled by default)
|
|
25
|
+
"noUnusedLocals": false,
|
|
26
|
+
"noUnusedParameters": false,
|
|
27
|
+
"noPropertyAccessFromIndexSignature": false
|
|
28
|
+
}
|
|
29
|
+
}
|
package/node-svc.xml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<service>
|
|
2
|
+
<id>node-app</id>
|
|
3
|
+
<name>Node.js App Daemon</name>
|
|
4
|
+
<description>Node.js 应用守护进程</description>
|
|
5
|
+
<!-- 路径指向 node 解释器 -->
|
|
6
|
+
<executable>C:\nvm4w\nodejs\node.exe</executable>
|
|
7
|
+
<!-- 参数指向 app.js -->
|
|
8
|
+
<arguments>D:\git\scripts-ui\build\server\index.js</arguments>
|
|
9
|
+
<!-- 1. 指定日志存放目录 (相对路径或绝对路径均可) -->
|
|
10
|
+
<logpath>logs</logpath>
|
|
11
|
+
<!-- 2. 日志模式配置:推荐使用 roll (滚动模式) -->
|
|
12
|
+
<log mode="roll">
|
|
13
|
+
<!-- 单个日志文件超过 10MB 则切分 -->
|
|
14
|
+
<sizeThreshold>10240</sizeThreshold>
|
|
15
|
+
<!-- 最多保留 10 个历史日志文件 -->
|
|
16
|
+
<keepFiles>10</keepFiles>
|
|
17
|
+
</log>
|
|
18
|
+
|
|
19
|
+
<onfailure action="restart" delay="10 sec"/>
|
|
20
|
+
<stopparentfirst>true</stopparentfirst>
|
|
21
|
+
</service>
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aaa-irule-script-bin23",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"private": false,
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
}
|
|
17
|
+
}
|