@nxtedition/lib 14.0.11 → 14.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "14.0.11",
3
+ "version": "14.0.13",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "files": [
@@ -7,10 +7,14 @@ const JSON5 = require('json5')
7
7
  const objectHash = require('object-hash')
8
8
  const weakCache = require('../../weakCache')
9
9
 
10
- module.exports = (options) => {
10
+ module.exports = ({ ds, proxify }) => {
11
+ const compiler = {
12
+ current: null,
13
+ }
14
+
11
15
  const compilers = {
12
- nxt: getNxtpressionsCompiler(options),
13
- js: getJavascriptCompiler(options),
16
+ nxt: getNxtpressionsCompiler({ ds }),
17
+ js: getJavascriptCompiler({ ds, proxify, compiler }),
14
18
  }
15
19
 
16
20
  function inner(str) {
@@ -70,158 +74,99 @@ module.exports = (options) => {
70
74
  }
71
75
  }
72
76
 
73
- const _compileArrayTemplate = weakCache(
74
- (template, hash) => {
75
- if (!fp.isArray(template)) {
76
- throw new Error('invalid argument')
77
- }
78
-
79
- if (!hash) {
80
- return null
77
+ function compileArrayTemplate(template) {
78
+ let resolvers
79
+ let indices
80
+
81
+ for (let i = 0; i < template.length; i++) {
82
+ const resolver = compileTemplate(template[i])
83
+ if (resolver) {
84
+ resolvers ??= []
85
+ resolvers.push(resolver)
86
+ indices ??= []
87
+ indices.push(i)
81
88
  }
89
+ }
82
90
 
83
- let resolvers
84
- let indices
85
-
86
- for (let i = 0; i < template.length; i++) {
87
- const resolver = compileTemplate(template[i])
88
- if (resolver) {
89
- resolvers ??= []
90
- resolvers.push(resolver)
91
- indices ??= []
92
- indices.push(i)
93
- }
94
- }
95
-
96
- return resolvers
97
- ? (arr, args$) => {
98
- const len = resolvers.length
99
- const values = new Array(len)
100
- for (let n = 0; n < len; n++) {
101
- values[n] = resolvers[n](arr[indices[n]], args$)
102
- }
103
-
104
- return rxjs.combineLatest(values).pipe(
105
- rx.map((values) => {
106
- const ret = [...arr]
107
- for (let n = 0; n < values.length; n++) {
108
- ret[indices[n]] = values[n]
109
- }
110
- return ret
111
- })
112
- )
91
+ return resolvers
92
+ ? (arr, args$) => {
93
+ const len = resolvers.length
94
+ const values = new Array(len)
95
+ for (let n = 0; n < len; n++) {
96
+ values[n] = resolvers[n](arr[indices[n]], args$)
113
97
  }
114
- : null
115
- },
116
- (arr, hash) => hash ?? hashTemplate(arr)
117
- )
118
98
 
119
- const compileArrayTemplate = (template) => {
120
- if (!fp.isArray(template)) {
121
- throw new Error('invalid argument')
122
- }
123
- const hash = hashTemplate(template)
124
- return hash ? _compileArrayTemplate(template, hash) : null
99
+ return rxjs.combineLatest(values).pipe(
100
+ rx.map((values) => {
101
+ const ret = [...arr]
102
+ for (let n = 0; n < values.length; n++) {
103
+ ret[indices[n]] = values[n]
104
+ }
105
+ return ret
106
+ })
107
+ )
108
+ }
109
+ : null
125
110
  }
126
111
 
127
- const _compileObjectTemplate = weakCache(
128
- (template, hash) => {
129
- if (!fp.isPlainObject(template)) {
130
- throw new Error('invalid argument')
131
- }
132
-
133
- if (!hash) {
134
- return null
135
- }
136
-
137
- let resolvers
138
- let indices
112
+ function compileObjectTemplate(template) {
113
+ let resolvers
114
+ let indices
139
115
 
140
- const keys = Object.keys(template)
116
+ const keys = Object.keys(template)
141
117
 
142
- for (let i = 0; i < keys.length; i++) {
143
- const resolver = compileTemplate(template[keys[i]])
144
- if (resolver) {
145
- resolvers ??= []
146
- resolvers.push(resolver)
147
- indices ??= []
148
- indices.push(keys[i])
149
- }
118
+ for (let i = 0; i < keys.length; i++) {
119
+ const resolver = compileTemplate(template[keys[i]])
120
+ if (resolver) {
121
+ resolvers ??= []
122
+ resolvers.push(resolver)
123
+ indices ??= []
124
+ indices.push(keys[i])
150
125
  }
126
+ }
151
127
 
152
- return resolvers
153
- ? (obj, args$) => {
154
- const len = resolvers.length
155
- const values = new Array(len)
156
- for (let n = 0; n < len; n++) {
157
- values[n] = resolvers[n](obj[indices[n]], args$)
158
- }
159
-
160
- return rxjs.combineLatest(values).pipe(
161
- rx.map((values) => {
162
- const ret = { ...obj }
163
- for (let n = 0; n < values.length; n++) {
164
- ret[indices[n]] = values[n]
165
- }
166
- return ret
167
- })
168
- )
128
+ return resolvers
129
+ ? (obj, args$) => {
130
+ const len = resolvers.length
131
+ const values = new Array(len)
132
+ for (let n = 0; n < len; n++) {
133
+ values[n] = resolvers[n](obj[indices[n]], args$)
169
134
  }
170
- : null
171
- },
172
- (obj, hash) => hash ?? hashTemplate(obj)
173
- )
174
135
 
175
- const compileObjectTemplate = (template) => {
176
- if (!fp.isPlainObject(template)) {
177
- throw new Error('invalid argument')
178
- }
179
-
180
- const hash = hashTemplate(template)
181
- return hash ? _compileObjectTemplate(template, hash) : null
136
+ return rxjs.combineLatest(values).pipe(
137
+ rx.map((values) => {
138
+ const ret = { ...obj }
139
+ for (let n = 0; n < values.length; n++) {
140
+ ret[indices[n]] = values[n]
141
+ }
142
+ return ret
143
+ })
144
+ )
145
+ }
146
+ : null
182
147
  }
183
148
 
184
- const _compileStringTemplate = weakCache(
185
- (template, hash) => {
186
- if (!fp.isString(template)) {
187
- throw new Error('invalid argument')
188
- }
189
-
190
- if (!hash) {
191
- return null
192
- }
193
-
194
- const match = inner(template)
195
- if (!match) {
196
- return null
197
- }
198
-
199
- const { pre, type, body, post } = match
200
-
201
- const compileExpression = compilers[type]
202
- if (!compileExpression) {
203
- throw new Error('unknown expression type: ' + type)
204
- }
149
+ function compileStringTemplate(template) {
150
+ const match = inner(template)
151
+ if (!match) {
152
+ return null
153
+ }
205
154
 
206
- const expr = compileExpression(body)
155
+ const { pre, type, body, post } = match
207
156
 
208
- if (!pre && !post) {
209
- return (str, args$) => expr(args$)
210
- }
157
+ const compileExpression = compilers[type]
158
+ if (!compileExpression) {
159
+ throw new Error('unknown expression type: ' + type)
160
+ }
211
161
 
212
- return (str, args$) =>
213
- expr(args$).pipe(rx.map((body) => `${pre}${stringify(body, type !== 'js')}${post}`))
214
- },
215
- (str, hash) => hash ?? hashTemplate(str)
216
- )
162
+ const expr = compileExpression(body)
217
163
 
218
- const compileStringTemplate = (template) => {
219
- if (!fp.isString(template)) {
220
- throw new Error('invalid argument')
164
+ if (!pre && !post) {
165
+ return (str, args$) => expr(args$)
221
166
  }
222
167
 
223
- const hash = hashTemplate(template)
224
- return hash ? _compileStringTemplate(template, hash) : null
168
+ return (str, args$) =>
169
+ expr(args$).pipe(rx.map((body) => `${pre}${stringify(body, type !== 'js')}${post}`))
225
170
  }
226
171
 
227
172
  function stringify(value, escape) {
@@ -239,16 +184,24 @@ module.exports = (options) => {
239
184
  return typeof val === 'string' && val.indexOf('{{') !== -1
240
185
  }
241
186
 
187
+ const compileTemplateCache = weakCache(
188
+ (template) => {
189
+ if (fp.isPlainObject(template)) {
190
+ return compileObjectTemplate(template)
191
+ } else if (fp.isArray(template)) {
192
+ return compileArrayTemplate(template)
193
+ } else if (isTemplate(template)) {
194
+ return compileStringTemplate(template)
195
+ } else {
196
+ return null
197
+ }
198
+ },
199
+ (template, hash) => hash
200
+ )
201
+
242
202
  function compileTemplate(template) {
243
- if (fp.isPlainObject(template)) {
244
- return compileObjectTemplate(template)
245
- } else if (fp.isArray(template)) {
246
- return compileArrayTemplate(template)
247
- } else if (isTemplate(template)) {
248
- return compileStringTemplate(template)
249
- } else {
250
- return null
251
- }
203
+ const hash = hashTemplate(template)
204
+ return hash ? compileTemplateCache(template, hash) : null
252
205
  }
253
206
 
254
207
  async function resolveTemplate(template, args$) {
@@ -263,10 +216,10 @@ module.exports = (options) => {
263
216
  }
264
217
  }
265
218
 
266
- return {
219
+ return Object.assign(compiler, {
267
220
  resolveTemplate,
268
221
  onResolveTemplate,
269
222
  compileTemplate,
270
223
  isTemplate,
271
- }
224
+ })
272
225
  }
@@ -14,13 +14,13 @@ class TimerEntry {
14
14
  constructor(key, refresh, delay) {
15
15
  this.key = key
16
16
  this.counter = null
17
+
17
18
  this.timer = setTimeout(refresh, delay)
18
19
  }
19
20
 
20
21
  dispose() {
21
22
  clearTimeout(this.timer)
22
23
 
23
- this.refresh = null
24
24
  this.timer = null
25
25
  }
26
26
  }
@@ -29,12 +29,12 @@ class FetchEntry {
29
29
  constructor(key, refresh, { resource, options }) {
30
30
  this.key = key
31
31
  this.counter = null
32
+
32
33
  this.refresh = refresh
33
34
  this.ac = new AbortController()
34
35
  this.signal = this.ac.signal
35
36
  this.body = null
36
37
  this.status = null
37
- this.options = options
38
38
  this.error = null
39
39
 
40
40
  // TODO (fix): options.signal
@@ -42,9 +42,9 @@ class FetchEntry {
42
42
  // TODO (fix): expire...
43
43
 
44
44
  undici
45
- .fetch(resource, { ...this.options, signal: this.signal })
45
+ .fetch(resource, { ...options, signal: this.signal })
46
46
  .then(async (res) => {
47
- if (!this.signal.aborted) {
47
+ if (this.refresh) {
48
48
  // TODO (fix): max size...
49
49
  this.body = Buffer.from(await res.arrayBuffer())
50
50
  this.status = res.status
@@ -53,7 +53,7 @@ class FetchEntry {
53
53
  }
54
54
  })
55
55
  .catch((err) => {
56
- if (!this.signal.aborted) {
56
+ if (this.refresh) {
57
57
  this.error = err
58
58
  this.refresh()
59
59
  }
@@ -61,9 +61,10 @@ class FetchEntry {
61
61
  }
62
62
 
63
63
  dispose() {
64
- this.ac.abort()
65
-
66
64
  this.refresh = null
65
+
66
+ this.ac.abort()
67
+ this.ac = null
67
68
  }
68
69
  }
69
70
 
@@ -71,6 +72,7 @@ class RecordEntry {
71
72
  constructor(key, refresh, ds) {
72
73
  this.key = key
73
74
  this.counter = null
75
+
74
76
  this.refresh = refresh
75
77
  this.record = ds.record.getRecord(key)
76
78
 
@@ -116,6 +118,36 @@ class ObservableEntry {
116
118
  dispose() {
117
119
  this.subscription.unsubscribe()
118
120
  this.subscription = null
121
+ this.refresh = null
122
+ }
123
+ }
124
+
125
+ class PromiseEntry {
126
+ constructor(key, refresh, promise) {
127
+ this.key = key
128
+ this.counter = null
129
+ this.value = kEmpty
130
+ this.error = null
131
+ this.refresh = refresh
132
+
133
+ promise.then(
134
+ (value) => {
135
+ if (this.refresh) {
136
+ this.value = value
137
+ this.refresh()
138
+ }
139
+ },
140
+ (err) => {
141
+ if (this.refresh) {
142
+ this.error = err
143
+ this.refresh()
144
+ }
145
+ }
146
+ )
147
+ }
148
+
149
+ dispose() {
150
+ this.refresh = null
119
151
  }
120
152
  }
121
153
 
@@ -141,13 +173,15 @@ const globals = {
141
173
  nxt: null,
142
174
  }
143
175
 
144
- function proxyify(value, expression) {
176
+ function proxify(value, expression, handler) {
145
177
  if (!value) {
146
178
  return value
147
179
  } else if (rxjs.isObservable(value)) {
148
- return proxyify(expression.observe(value), expression)
180
+ return proxify(expression.observe(value), expression)
181
+ } else if (typeof value?.then === 'function') {
182
+ return proxify(expression.wait(value), expression, handler)
149
183
  } else if (typeof value === 'object') {
150
- return new Proxy(value, expression._handler)
184
+ return new Proxy(value, handler)
151
185
  } else {
152
186
  return value
153
187
  }
@@ -155,7 +189,14 @@ function proxyify(value, expression) {
155
189
 
156
190
  const MAP_POOL = []
157
191
 
158
- module.exports = ({ ds, ...options }) => {
192
+ function makeWrapper(expression) {
193
+ const handler = {
194
+ get: (target, prop) => proxify(target[prop], this),
195
+ }
196
+ return (value) => proxify(value, expression, handler)
197
+ }
198
+
199
+ module.exports = ({ ds, proxify, compiler }) => {
159
200
  class Expression {
160
201
  constructor(context, script, expression, args, observer) {
161
202
  this._context = context
@@ -172,19 +213,13 @@ module.exports = ({ ds, ...options }) => {
172
213
  this._disposing = false
173
214
  this._destroyed = false
174
215
  this._subscription = null
175
- this._args = null
176
- this._ready = false
177
- this._handler = options.proxyify
178
- ? {
179
- get: (target, prop) => proxyify(target[prop], this),
180
- }
181
- : null
216
+ this._args = kEmpty
217
+ this._wrap = null
182
218
 
183
219
  if (rxjs.isObservable(args)) {
184
220
  this._subscription = args.subscribe({
185
221
  next: (args) => {
186
- this._args = this._handler ? proxyify(args, this) : args
187
- this._ready = true
222
+ this._args = proxify ? this.wrap(args) : args
188
223
  this._refresh()
189
224
  },
190
225
  error: (err) => {
@@ -196,12 +231,16 @@ module.exports = ({ ds, ...options }) => {
196
231
  },
197
232
  })
198
233
  } else {
199
- this._args = this._handler ? proxyify(args, this) : args
200
- this._ready = true
234
+ this._args = proxify ? this.wrap(args) : args
201
235
  this._refreshNT(this)
202
236
  }
203
237
  }
204
238
 
239
+ wrap(value) {
240
+ this._wrap ??= makeWrapper(this)
241
+ return this._wrap(value)
242
+ }
243
+
205
244
  suspend() {
206
245
  throw kSuspend
207
246
  }
@@ -214,6 +253,10 @@ module.exports = ({ ds, ...options }) => {
214
253
  return this._getObservable(observable, throws)
215
254
  }
216
255
 
256
+ wait(promise, throws) {
257
+ return this._getWait(promise, throws)
258
+ }
259
+
217
260
  ds(id, state, throws) {
218
261
  return this._getRecord(id, state, throws)
219
262
  }
@@ -267,7 +310,7 @@ module.exports = ({ ds, ...options }) => {
267
310
  _refreshNT(self) {
268
311
  self._refreshing = false
269
312
 
270
- if (self._destroyed || self._disposing || !self._ready) {
313
+ if (self._destroyed || self._disposing || self._args === kEmpty) {
271
314
  return
272
315
  }
273
316
 
@@ -277,6 +320,7 @@ module.exports = ({ ds, ...options }) => {
277
320
  self._context.$ = self._args
278
321
  self._context.nxt = self
279
322
  try {
323
+ compiler.current = this
280
324
  const value = self._script.runInContext(self._context)
281
325
  if (value !== self._value) {
282
326
  self._value = value
@@ -294,6 +338,8 @@ module.exports = ({ ds, ...options }) => {
294
338
  })
295
339
  )
296
340
  } finally {
341
+ compiler.current = null
342
+
297
343
  self._context.$ = null
298
344
  self._context.nxt = null
299
345
 
@@ -321,7 +367,7 @@ module.exports = ({ ds, ...options }) => {
321
367
  }
322
368
 
323
369
  _refresh = () => {
324
- if (this._refreshing || this._destroyed || this._disposing || !this._ready) {
370
+ if (this._refreshing || this._destroyed || this._disposing || this._args === kEmpty) {
325
371
  return
326
372
  }
327
373
 
@@ -381,6 +427,28 @@ module.exports = ({ ds, ...options }) => {
381
427
  return entry.value
382
428
  }
383
429
 
430
+ _getWait(promise, throws) {
431
+ if (typeof promise?.then !== 'function') {
432
+ throw new Error(`invalid argument: Promise (${promise})`)
433
+ }
434
+
435
+ const entry = this._getEntry(promise, PromiseEntry, promise)
436
+
437
+ if (entry.error) {
438
+ throw entry.error
439
+ }
440
+
441
+ if (entry.value === kEmpty) {
442
+ if (throws ?? true) {
443
+ throw kSuspend
444
+ } else {
445
+ return null
446
+ }
447
+ }
448
+
449
+ return entry.value
450
+ }
451
+
384
452
  _getRecord(key, state, throws) {
385
453
  if (!key || typeof key !== 'string') {
386
454
  throw new Error(`invalid argument: key (${key})`)