@nxtedition/lib 22.0.7 → 22.0.9

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/ass.js CHANGED
@@ -1,7 +1,14 @@
1
1
  import fp from 'lodash/fp.js'
2
2
 
3
- export function encodeASS(events, { styles = {}, width = 1920, height = 1080 } = {}) {
4
- return [encASSHeader({ width, height }), encASSStyles(styles), encASSEvents(events)].join('\n')
3
+ const BASE_WIDTH = 1920
4
+ const BASE_HEIGHT = 1080
5
+
6
+ export function encodeASS(events, { styles = {}, width = BASE_WIDTH, height = BASE_HEIGHT } = {}) {
7
+ return [
8
+ encASSHeader({ width, height }),
9
+ encASSStyles({ styles, width, height }),
10
+ encASSEvents(events),
11
+ ].join('\n')
5
12
  }
6
13
 
7
14
  function formatDialogues(events) {
@@ -80,10 +87,24 @@ const formatStyles = fp.pipe(
80
87
  }, ''),
81
88
  )
82
89
 
83
- function encASSStyles(styles) {
90
+ function encASSStyles({ styles, width, height }) {
91
+ const scaledStyles = {}
92
+
93
+ for (const [id, style] of Object.entries(styles)) {
94
+ scaledStyles[id] = { ...style }
95
+ for (const key of ['fontsize', 'marginV', 'marginL', 'marginR']) {
96
+ const scale = key === 'fontsize' ? height / BASE_HEIGHT : width / BASE_WIDTH
97
+
98
+ if (typeof style[key] === 'string') {
99
+ const scaled = Number(style[key]) * scale
100
+ scaledStyles[id][key] = String(Math.round(scaled * 10) / 10)
101
+ }
102
+ }
103
+ }
104
+
84
105
  return `[V4+ Styles]
85
106
  Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
86
- ${formatStyles(styles)}`
107
+ ${formatStyles(scaledStyles)}`
87
108
  }
88
109
 
89
110
  function encASSHeader({ width, height }) {
@@ -94,8 +115,8 @@ function encASSHeader({ width, height }) {
94
115
  // 3: same as 0, but lower line gets wider.
95
116
  return `[Script Info]
96
117
  ScriptType: v4.00+
97
- PlayResX: ${width || 1920}
98
- PlayResY: ${height || 1080}
118
+ PlayResX: ${width || BASE_WIDTH}
119
+ PlayResY: ${height || BASE_HEIGHT}
99
120
  WrapStyle: 1
100
121
  `
101
122
  }
package/http.js CHANGED
@@ -134,7 +134,7 @@ export async function requestMiddleware(ctx, next) {
134
134
 
135
135
  pendingSet.add(ctx)
136
136
  try {
137
- if (req.method === 'GET' || req.method === 'HEAD') {
137
+ if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
138
138
  req.resume() // Dump the body if there is one.
139
139
  }
140
140
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "22.0.7",
3
+ "version": "22.0.9",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
@@ -58,6 +58,7 @@
58
58
  "@elastic/elasticsearch": "^8.16.1",
59
59
  "@elastic/transport": "^8.9.1",
60
60
  "@nxtedition/nxt-undici": "^4.2.26",
61
+ "@swc/wasm-web": "^1.10.1",
61
62
  "content-type": "^1.0.5",
62
63
  "date-fns": "^3.6.0",
63
64
  "fast-querystring": "^1.1.1",
@@ -94,11 +95,11 @@
94
95
  "eslint-plugin-import": "^2.31.0",
95
96
  "eslint-plugin-n": "^17.13.2",
96
97
  "eslint-plugin-node": "^11.1.0",
97
- "eslint-plugin-promise": "^7.1.0",
98
+ "eslint-plugin-promise": "^7.2.1",
98
99
  "husky": "^9.1.7",
99
- "lint-staged": "^15.2.10",
100
+ "lint-staged": "^15.2.11",
100
101
  "pinst": "^3.0.0",
101
- "prettier": "^3.3.3",
102
+ "prettier": "^3.4.2",
102
103
  "rxjs": "^7.5.6",
103
104
  "send": "^0.18.0",
104
105
  "tap": "^21.0.1",
package/serializers.js CHANGED
@@ -37,6 +37,8 @@ function getTiming(obj) {
37
37
 
38
38
  export default {
39
39
  err: (err) => errSerializer(err),
40
+ error: (err) => errSerializer(err),
41
+ errors: (err) => errSerializer(err),
40
42
  socket: (socket) =>
41
43
  socket && {
42
44
  id: socket.id || undefined,
@@ -98,8 +100,8 @@ export default {
98
100
  bytesRead: ures.bytesRead,
99
101
  bytesReadPerSecond:
100
102
  ures.bytesReadPerSecond ??
101
- (ures.timing?.complete > 0 && ures.bytesRead
102
- ? (ures.bytesRead * 1e3) / ures.timing.complete
103
+ (ures.timing && ures.timing.data > 0 && ures.timing.end > 0
104
+ ? (ures.bytesRead * 1e3) / (ures.timing.end - ures.timing.data)
103
105
  : undefined),
104
106
  body: typeof ures.body === 'string' ? ures.body : undefined,
105
107
  headers: getHeaders(ures),
@@ -131,8 +133,8 @@ export default {
131
133
  bytesWritten: ureq.bytesWritten,
132
134
  bytesReadPerSecond:
133
135
  ureq.bytesWrittenPerSecond ??
134
- (ureq.timing?.complete > 0 && ureq.bytesWritten
135
- ? (ureq.bytesWritten * 1e3) / ureq.timing.complete
136
+ (ureq.timing && ureq.timing.data > 0 && ureq.timing.end > 0
137
+ ? (ureq.bytesWritten * 1e3) / (ureq.timing.end - ureq.timing.data)
136
138
  : undefined),
137
139
  headers: getHeaders(ureq),
138
140
  query: ureq.query,
@@ -1,16 +1,15 @@
1
1
  import assert from 'node:assert'
2
2
  import { makeWeakCache } from '../../weakCache.js'
3
3
  import * as rxjs from 'rxjs'
4
- import vm from 'node:vm'
5
4
  import objectHash from 'object-hash'
6
5
  import * as datefns from 'date-fns'
7
6
  import JSON5 from 'json5'
8
7
  import { request } from '@nxtedition/nxt-undici'
9
8
  import undici from 'undici'
10
9
  import fp from 'lodash/fp.js'
11
- import _ from 'lodash'
12
10
  import moment from 'moment-timezone'
13
11
  import Timecode from 'smpte-timecode'
12
+ import transform from './transform.js'
14
13
 
15
14
  const SEP = String.fromCharCode(30) // ASCII 1E
16
15
 
@@ -190,28 +189,6 @@ class PromiseEntry {
190
189
  }
191
190
  }
192
191
 
193
- function pipe(value, ...fns) {
194
- for (const fn of fns) {
195
- value = fn(value)
196
- if (value == null) {
197
- return value
198
- }
199
- }
200
- return value
201
- }
202
-
203
- const globals = {
204
- fp,
205
- _,
206
- moment,
207
- Timecode,
208
- datefns,
209
- JSON5,
210
- pipe,
211
- $: null,
212
- nxt: null,
213
- }
214
-
215
192
  function proxify(value, expression, handler, suspend = true) {
216
193
  assert(expression)
217
194
  assert(handler)
@@ -238,8 +215,7 @@ function makeWrapper(expression) {
238
215
 
239
216
  export default function ({ ds, proxify, compiler }) {
240
217
  class Expression {
241
- constructor(context, script, expression, args, observer) {
242
- this._context = context
218
+ constructor(script, expression, args, observer) {
243
219
  this._expression = expression
244
220
  this._observer = observer
245
221
  this._script = script
@@ -371,16 +347,48 @@ export default function ({ ds, proxify, compiler }) {
371
347
 
372
348
  this._counter = (this._counter + 1) & maxInt
373
349
 
374
- // TODO (fix): freeze?
375
- this._context.$ = this._args
376
- this._context.nxt = this
377
-
378
350
  const previous = compiler.current
379
351
  compiler.current = this
380
352
 
381
353
  try {
382
354
  assert(this._suspended === false)
383
- const value = this._script.runInContext(this._context)
355
+
356
+ const savedThis = {
357
+ fp: globalThis.fp,
358
+ moment: globalThis.moment,
359
+ Timecode: globalThis.Timecode,
360
+ datefns: globalThis.datefns,
361
+ JSON5: globalThis.JSON5,
362
+ $: globalThis.$,
363
+ nxt: globalThis.nxt,
364
+ }
365
+
366
+ let value
367
+ try {
368
+ globalThis.fp = fp
369
+ globalThis.moment = moment
370
+ globalThis.Timecode = Timecode
371
+ globalThis.datefns = datefns
372
+ globalThis.JSON5 = JSON5
373
+ globalThis.$ = this._args
374
+ globalThis.nxt = this
375
+
376
+ // TODO (fix): This should be sandboxed somehow...
377
+ // - Use a webworker?
378
+ // - Use vm for Node only?
379
+ // - Use https://github.com/tc39/proposal-shadowrealm?
380
+ value = this._script()
381
+ } finally {
382
+ globalThis.fp = savedThis.fp
383
+ globalThis._ = savedThis._
384
+ globalThis.moment = savedThis.moment
385
+ globalThis.Timecode = savedThis.Timecode
386
+ globalThis.datefns = savedThis.datefns
387
+ globalThis.JSON5 = savedThis.JSON5
388
+ globalThis.$ = savedThis.$
389
+ globalThis.nxt = savedThis.nxt
390
+ }
391
+
384
392
  this._errored = false
385
393
  if (this._suspended) {
386
394
  return
@@ -410,9 +418,6 @@ export default function ({ ds, proxify, compiler }) {
410
418
  } finally {
411
419
  compiler.current = previous
412
420
 
413
- this._context.$ = null
414
- this._context.nxt = null
415
-
416
421
  this._suspended = false
417
422
  this._disposing = true
418
423
 
@@ -598,18 +603,25 @@ export default function ({ ds, proxify, compiler }) {
598
603
  return makeWeakCache((expression) => {
599
604
  let script
600
605
  try {
601
- script = new vm.Script(`
602
- "use strict";
603
- {
604
- const _ = pipe;
606
+ script = new Function(
607
+ transform(`
608
+ const _ = (value, ...fns) => {
609
+ for (const fn of fns) {
610
+ value = fn(value)
611
+ if (value == null) {
612
+ return value
613
+ }
614
+ }
615
+ return value
616
+ };
605
617
  _.asset = (type, state, suspend) => (name) => nxt._asset(name, type, state, suspend);
606
618
  _.ds = (postfix, state, suspend) => (name) => nxt._ds(name, postfix, state, suspend);
607
619
  _.get = (postfix, path, state, suspend) => (name) => nxt._get(name, postfix, path, state, suspend);
608
620
  _.timer = (dueTime) => (dueValue) => nxt.timer(dueTime, dueValue);
609
621
  _.fetch = (options, suspend) => (resource) => nxt.fetch(resource, options, suspend);
610
622
  ${expression}
611
- }
612
- `)
623
+ `),
624
+ )
613
625
  } catch (err) {
614
626
  return () =>
615
627
  rxjs.throwError(
@@ -617,12 +629,10 @@ export default function ({ ds, proxify, compiler }) {
617
629
  )
618
630
  }
619
631
 
620
- const context = vm.createContext({ ...globals })
621
-
622
632
  return (args) =>
623
633
  new rxjs.Observable((o) => {
624
634
  try {
625
- const exp = new Expression(context, script, expression, args, o)
635
+ const exp = new Expression(script, expression, args, o)
626
636
  return () => {
627
637
  exp._destroy()
628
638
  }
@@ -0,0 +1,30 @@
1
+ import { createRequire } from 'node:module'
2
+ import { readFileSync } from 'node:fs'
3
+ import { parseSync, printSync, initSync } from '@swc/wasm-web'
4
+
5
+ const require = createRequire(import.meta.url)
6
+ const wasmPath = require.resolve('@swc/wasm-web').replace('wasm.js', 'wasm_bg.wasm')
7
+ const wasmBuffer = readFileSync(wasmPath)
8
+
9
+ initSync({ module: wasmBuffer })
10
+
11
+ export default function (code) {
12
+ const ast = parseSync(code, { syntax: 'ecmascript', script: true })
13
+
14
+ const { body } = ast
15
+
16
+ if (body.length > 0) {
17
+ const lastStatement = body[body.length - 1]
18
+
19
+ if (lastStatement.type !== 'ReturnStatement') {
20
+ body[body.length - 1] = {
21
+ type: 'ReturnStatement',
22
+ argument:
23
+ lastStatement.type === 'ExpressionStatement' ? lastStatement.expression : lastStatement,
24
+ span: lastStatement.span,
25
+ }
26
+ }
27
+ }
28
+
29
+ return printSync(ast).code
30
+ }
@@ -0,0 +1,16 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import transform from './transform.js'
4
+
5
+ test('transform', function () {
6
+ assert.strictEqual(transform('const x = 2; x;'), 'const x = 2;\nreturn x;\n')
7
+ assert.strictEqual(transform('[1,2,3];'), 'return [\n 1,\n 2,\n 3\n];\n')
8
+ assert.strictEqual(transform('[1,2,3]'), 'return [\n 1,\n 2,\n 3\n];\n')
9
+ assert.strictEqual(transform('x\n? \n2 \n: \n3'), `return x ? 2 : 3;\n`)
10
+ // assert.strictEqual(transform('return x\n? \n2 \n: \n3'), `return x ? 2 : 3;\n`)
11
+ // assert.strictEqual(transform('return\n x\n? \n2 \n: \n3'), `return x ? 2 : 3;\n`)
12
+ assert.strictEqual(
13
+ transform('$.width > $.height ? 30 : 20'),
14
+ `return $.width > $.height ? 30 : 20;\n`,
15
+ )
16
+ })