@nordcraft/runtime 1.0.68 → 1.0.70

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.
@@ -5,6 +5,7 @@ export declare class Signal<T> {
5
5
  destroy?: () => void;
6
6
  }>;
7
7
  subscriptions: Array<() => void>;
8
+ destroying: boolean;
8
9
  constructor(value: T);
9
10
  get(): T;
10
11
  set(value: T): void;
@@ -3,6 +3,7 @@ export class Signal {
3
3
  value;
4
4
  subscribers;
5
5
  subscriptions;
6
+ destroying = false;
6
7
  constructor(value) {
7
8
  this.value = value;
8
9
  this.subscribers = new Set();
@@ -19,7 +20,9 @@ export class Signal {
19
20
  }
20
21
  if (fastDeepEqual(value, this.value) === false) {
21
22
  this.value = value;
22
- this.subscribers.forEach(({ notify }) => notify(this.value));
23
+ for (const subscriber of this.subscribers) {
24
+ subscriber.notify(this.value);
25
+ }
23
26
  }
24
27
  }
25
28
  update(f) {
@@ -34,16 +37,25 @@ export class Signal {
34
37
  };
35
38
  }
36
39
  destroy() {
37
- this.subscribers.forEach(({ destroy }) => {
38
- destroy?.();
39
- });
40
+ // Prevent re-entrancy
41
+ if (this.destroying) {
42
+ return;
43
+ }
44
+ this.destroying = true;
45
+ for (const subscriber of this.subscribers) {
46
+ subscriber.destroy?.();
47
+ }
40
48
  this.subscribers.clear();
41
- this.subscriptions?.forEach((f) => f());
49
+ for (const subscription of this.subscriptions) {
50
+ subscription();
51
+ }
52
+ this.subscriptions.splice(0, this.subscriptions.length);
53
+ this.destroying = false;
42
54
  }
43
55
  cleanSubscribers() {
44
- this.subscribers.forEach(({ destroy }) => {
45
- destroy?.();
46
- });
56
+ for (const subscriber of this.subscribers) {
57
+ subscriber.destroy?.();
58
+ }
47
59
  this.subscribers.clear();
48
60
  }
49
61
  map(f) {
@@ -1 +1 @@
1
- {"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -4,8 +4,8 @@
4
4
  "type": "module",
5
5
  "homepage": "https://github.com/nordcraftengine/nordcraft",
6
6
  "dependencies": {
7
- "@nordcraft/core": "1.0.68",
8
- "@nordcraft/std-lib": "1.0.68",
7
+ "@nordcraft/core": "1.0.70",
8
+ "@nordcraft/std-lib": "1.0.70",
9
9
  "fast-deep-equal": "3.1.3",
10
10
  "path-to-regexp": "6.3.0"
11
11
  },
@@ -21,5 +21,5 @@
21
21
  "files": ["dist", "src"],
22
22
  "main": "dist/page.main.js",
23
23
  "types": "dist/page.main.d.ts",
24
- "version": "1.0.68"
24
+ "version": "1.0.70"
25
25
  }
@@ -957,6 +957,7 @@ export function createAPI({
957
957
  | Signal<{
958
958
  request: ReturnType<typeof constructRequest>
959
959
  api: ReturnType<typeof getApiForComparison>
960
+ headers: Array<[string, string]>
960
961
  // if the evaluated value of autoFetch changes from false -> true, we need to refetch the api
961
962
  autoFetch: boolean
962
963
  // currently, the proxy setting is always controlled by a "value formula", but in case we later
@@ -966,19 +967,22 @@ export function createAPI({
966
967
  | undefined
967
968
 
968
969
  // eslint-disable-next-line prefer-const
969
- payloadSignal = ctx.dataSignal.map((_) => {
970
- const payloadContext = getFormulaContext(api, initialComponentData)
970
+ payloadSignal = ctx.dataSignal.map((data) => {
971
+ const payloadContext = getFormulaContext(api, data)
972
+ const request = constructRequest(api, data)
971
973
  return {
972
- request: constructRequest(api, initialComponentData),
974
+ request,
973
975
  api: getApiForComparison(api),
976
+ // Serialize the Headers object to be able to compare changes
977
+ headers: Array.from(request.requestSettings.headers.entries()),
974
978
  autoFetch: api.autoFetch
975
979
  ? applyFormula(api.autoFetch, payloadContext)
976
980
  : false,
977
981
  proxy: applyFormula(api.server?.proxy?.enabled.formula, payloadContext),
978
982
  }
979
983
  })
980
- payloadSignal.subscribe(async (_) => {
981
- const { url, requestSettings } = constructRequest(api, initialComponentData)
984
+ payloadSignal.subscribe(async (apiData) => {
985
+ const { url, requestSettings } = apiData.request
982
986
  // Ensure we only use caching if the page is currently loading
983
987
  const cacheMatch =
984
988
  // We lookup the API from cache as long as autofetch is defined (and not statically falsy)
@@ -1138,14 +1142,17 @@ export function createAPI({
1138
1142
  const autoFetch =
1139
1143
  api.autoFetch && applyFormula(api.autoFetch, updateContext)
1140
1144
  if (autoFetch) {
1145
+ const request = constructRequest(newApi, componentData)
1141
1146
  payloadSignal?.set({
1142
- request: constructRequest(newApi, componentData),
1147
+ request,
1143
1148
  api: getApiForComparison(newApi),
1144
1149
  autoFetch,
1145
1150
  proxy: applyFormula(
1146
1151
  newApi.server?.proxy?.enabled.formula,
1147
1152
  updateContext,
1148
1153
  ),
1154
+ // Serialize the Headers object to be able to compare changes
1155
+ headers: Array.from(request.requestSettings.headers.entries()),
1149
1156
  })
1150
1157
  }
1151
1158
  },
@@ -11,8 +11,10 @@ import {
11
11
  import { appendUnit } from '@nordcraft/core/dist/styling/customProperty'
12
12
  import { getNodeSelector } from '@nordcraft/core/dist/utils/getNodeSelector'
13
13
  import { isDefined, toBoolean } from '@nordcraft/core/dist/utils/util'
14
+ import type { ComponentData } from '@nordcraft/core/src/component/component.types'
14
15
  import { handleAction } from '../events/handleAction'
15
16
  import type { Signal } from '../signal/signal'
17
+ import type { ComponentContext } from '../types'
16
18
  import { getDragData } from '../utils/getDragData'
17
19
  import { getElementTagName } from '../utils/getElementTagName'
18
20
  import { setAttribute } from '../utils/setAttribute'
@@ -210,39 +212,20 @@ export function createElement({
210
212
  )
211
213
  })
212
214
 
213
- // TODO: This is a bit heavy on the runtime. Can we optimize this with a static function instead of creating a closure for each event?
215
+ const eventHandlers: [string, (e: Event) => boolean][] = []
214
216
  Object.values(node.events).forEach((event) => {
215
- const handler = (e: Event) => {
216
- event?.actions.forEach((action) => {
217
- if (e instanceof DragEvent) {
218
- ;(e as any).data = getDragData(e)
219
- }
220
- if (e instanceof ClipboardEvent) {
221
- try {
222
- ;(e as any).data = Array.from(e.clipboardData?.items ?? []).reduce<
223
- Record<string, any>
224
- >((dragData, item) => {
225
- try {
226
- dragData[item.type] = JSON.parse(
227
- e.clipboardData?.getData(item.type) as any,
228
- )
229
- } catch {
230
- dragData[item.type] = e.clipboardData?.getData(item.type)
231
- }
232
- return dragData
233
- }, {})
234
- } catch (e) {
235
- // eslint-disable-next-line no-console
236
- console.error('Could not get paste data', e)
237
- }
238
- }
239
- void handleAction(action, { ...dataSignal.get(), Event: e }, ctx, e)
240
- })
241
- return false
242
- }
243
- if (event) {
244
- elem.addEventListener(event.trigger, handler)
217
+ if (!event) {
218
+ return
245
219
  }
220
+
221
+ eventHandlers.push([
222
+ event.trigger,
223
+ getEventHandler({ event, dataSignal, ctx }),
224
+ ])
225
+ })
226
+
227
+ eventHandlers.forEach(([eventName, handler]) => {
228
+ elem.addEventListener(eventName, handler)
246
229
  })
247
230
 
248
231
  // for script, style & SVG<text> tags we only render text child.
@@ -290,19 +273,67 @@ export function createElement({
290
273
  })
291
274
  })
292
275
  } else {
276
+ const childNodes: (Element | Text)[] = []
293
277
  node.children.forEach((child, i) => {
294
- const childNodes = createNode({
295
- parentElement: elem,
296
- id: child,
297
- path: path + '.' + i,
298
- dataSignal,
299
- ctx,
300
- namespace,
301
- instance,
302
- })
303
- childNodes.forEach((childNode) => elem.appendChild(childNode))
278
+ childNodes.push(
279
+ ...createNode({
280
+ parentElement: elem,
281
+ id: child,
282
+ path: path + '.' + i,
283
+ dataSignal,
284
+ ctx,
285
+ namespace,
286
+ instance,
287
+ }),
288
+ )
304
289
  })
290
+ elem.append(...childNodes)
305
291
  }
292
+ dataSignal.subscribe(() => {}, {
293
+ destroy: () => {
294
+ // TODO: Clean up event listeners, but after destruction of child signals (Maybe we need a "afterDestroy" hook on signals?)
295
+ elem.parentNode?.removeChild(elem)
296
+ },
297
+ })
306
298
 
307
299
  return elem
308
300
  }
301
+
302
+ const getEventHandler =
303
+ ({
304
+ event,
305
+ dataSignal,
306
+ ctx,
307
+ }: {
308
+ event: ElementNodeModel['events'][string]
309
+ dataSignal: Signal<ComponentData>
310
+ ctx: ComponentContext
311
+ }) =>
312
+ (e: Event) => {
313
+ event?.actions.forEach((action) => {
314
+ if (e instanceof DragEvent) {
315
+ ;(e as any).data = getDragData(e)
316
+ }
317
+ if (e instanceof ClipboardEvent) {
318
+ try {
319
+ ;(e as any).data = Array.from(e.clipboardData?.items ?? []).reduce<
320
+ Record<string, any>
321
+ >((dragData, item) => {
322
+ try {
323
+ dragData[item.type] = JSON.parse(
324
+ e.clipboardData?.getData(item.type) as any,
325
+ )
326
+ } catch {
327
+ dragData[item.type] = e.clipboardData?.getData(item.type)
328
+ }
329
+ return dragData
330
+ }, {})
331
+ } catch (e) {
332
+ // eslint-disable-next-line no-console
333
+ console.error('Could not get paste data', e)
334
+ }
335
+ }
336
+ void handleAction(action, { ...dataSignal.get(), Event: e }, ctx, e)
337
+ })
338
+ return false
339
+ }
@@ -7,6 +7,7 @@ export class Signal<T> {
7
7
  destroy?: () => void
8
8
  }>
9
9
  subscriptions: Array<() => void>
10
+ destroying = false
10
11
 
11
12
  constructor(value: T) {
12
13
  this.value = value
@@ -25,7 +26,9 @@ export class Signal<T> {
25
26
 
26
27
  if (fastDeepEqual(value, this.value) === false) {
27
28
  this.value = value
28
- this.subscribers.forEach(({ notify }) => notify(this.value))
29
+ for (const subscriber of this.subscribers) {
30
+ subscriber.notify(this.value)
31
+ }
29
32
  }
30
33
  }
31
34
 
@@ -41,16 +44,26 @@ export class Signal<T> {
41
44
  }
42
45
  }
43
46
  destroy() {
44
- this.subscribers.forEach(({ destroy }) => {
45
- destroy?.()
46
- })
47
+ // Prevent re-entrancy
48
+ if (this.destroying) {
49
+ return
50
+ }
51
+
52
+ this.destroying = true
53
+ for (const subscriber of this.subscribers) {
54
+ subscriber.destroy?.()
55
+ }
47
56
  this.subscribers.clear()
48
- this.subscriptions?.forEach((f) => f())
57
+ for (const subscription of this.subscriptions) {
58
+ subscription()
59
+ }
60
+ this.subscriptions.splice(0, this.subscriptions.length)
61
+ this.destroying = false
49
62
  }
50
63
  cleanSubscribers() {
51
- this.subscribers.forEach(({ destroy }) => {
52
- destroy?.()
53
- })
64
+ for (const subscriber of this.subscribers) {
65
+ subscriber.destroy?.()
66
+ }
54
67
  this.subscribers.clear()
55
68
  }
56
69
  map<T2>(f: (value: T) => T2): Signal<T2> {