effectweb 0.3.0 → 0.3.1

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,27 @@
1
+ /** Internal completion accounting, including reentrant acquisition before a fiber is available. */
2
+ export class Settlement {
3
+ pending = 0;
4
+ waiters = [];
5
+ begin() {
6
+ this.pending++;
7
+ let finished = false;
8
+ return () => {
9
+ if (finished)
10
+ return;
11
+ finished = true;
12
+ if (--this.pending)
13
+ return;
14
+ for (const done of this.waiters)
15
+ done();
16
+ this.waiters.length = 0;
17
+ };
18
+ }
19
+ wait() {
20
+ return new Promise((done) => {
21
+ if (this.pending)
22
+ this.waiters.push(done);
23
+ else
24
+ done();
25
+ });
26
+ }
27
+ }
package/dist/sharing.js CHANGED
@@ -60,28 +60,31 @@ function share(previous, next, fields) {
60
60
  oldKeys.some((key) => !Object.hasOwn(Object.getOwnPropertyDescriptor(previous, key), 'value')))
61
61
  return next;
62
62
  let equal = keys.length === oldKeys.length && (!array || value.length === old.length);
63
- let unchangedNext = true;
64
- const shared = array ? [] : {};
65
- if (array)
66
- shared.length = value.length;
63
+ let changed;
67
64
  for (const key of keys) {
68
65
  const custom = fields && Object.hasOwn(fields, key) ? fields[key] : undefined;
69
66
  const item = custom && Object.hasOwn(old, key)
70
67
  ? custom(old[key], value[key])
71
68
  : share(Object.hasOwn(old, key) ? old[key] : undefined, value[key], undefined);
72
- // Define an own data property, including __proto__, without invoking setters.
73
- Object.defineProperty(shared, key, {
74
- value: item,
75
- writable: true,
76
- enumerable: true,
77
- configurable: true,
78
- });
79
69
  if (!Object.hasOwn(old, key) || !Object.is(item, old[key]))
80
70
  equal = false;
81
71
  if (!Object.is(item, value[key]))
82
- unchangedNext = false;
72
+ (changed ??= new Map()).set(key, item);
73
+ }
74
+ let result = equal ? previous : next;
75
+ if (!equal && changed) {
76
+ const shared = array ? [] : {};
77
+ if (array)
78
+ shared.length = value.length;
79
+ for (const key of keys)
80
+ Object.defineProperty(shared, key, {
81
+ value: changed.has(key) ? changed.get(key) : value[key],
82
+ writable: true,
83
+ enumerable: true,
84
+ configurable: true,
85
+ });
86
+ result = shared;
83
87
  }
84
- const result = equal ? previous : unchangedNext ? next : shared;
85
88
  if (fields)
86
89
  return result;
87
90
  if (!pairs) {
@@ -0,0 +1 @@
1
+ export declare const compiledSlots: WeakSet<object>;
@@ -0,0 +1,2 @@
1
+ // Shared only by compiler runtime helpers; user functions cannot acquire this proof.
2
+ export const compiledSlots = new WeakSet();
package/dist/snapshot.js CHANGED
@@ -2,29 +2,37 @@ import { isAsyncResult } from 'effect/unstable/reactivity/AsyncResult';
2
2
  const checked = new WeakSet();
3
3
  /** Protect published plain data in every build. Opaque objects keep their own lifecycle. */
4
4
  export function protectSnapshot(value) {
5
- if (!value || typeof value !== 'object' || checked.has(value))
6
- return value;
7
- // AsyncResult is an immutable Effect wrapper, but its successful UI data is plain data.
8
- if (isAsyncResult(value)) {
9
- checked.add(value);
10
- if (value._tag === 'Success')
11
- protectSnapshot(value.value);
12
- else if (value._tag === 'Failure' && value.previousSuccess._tag === 'Some')
13
- protectSnapshot(value.previousSuccess.value);
14
- return value;
15
- }
16
- const prototype = Object.getPrototypeOf(value);
17
- if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null)
18
- return value;
19
- // Mark before walking to allow cyclic data and reuse already checked shared branches.
20
- checked.add(value);
21
- const descriptors = Object.getOwnPropertyDescriptors(value);
22
- for (const key of Reflect.ownKeys(descriptors)) {
23
- const descriptor = Reflect.get(descriptors, key);
24
- // Never execute getters or traverse closures, class instances, Effects or DOM objects.
25
- if ('value' in descriptor)
26
- protectSnapshot(descriptor.value);
27
- }
28
- Object.freeze(value);
5
+ const seen = new Set();
6
+ const plain = [];
7
+ const validate = (item) => {
8
+ if (!item || typeof item !== 'object' || checked.has(item) || seen.has(item))
9
+ return;
10
+ seen.add(item);
11
+ // Effect wrappers remain opaque, but their successful UI data is plain data.
12
+ if (isAsyncResult(item)) {
13
+ if (item._tag === 'Success')
14
+ validate(item.value);
15
+ else if (item._tag === 'Failure' && item.previousSuccess._tag === 'Some')
16
+ validate(item.previousSuccess.value);
17
+ return;
18
+ }
19
+ const prototype = Object.getPrototypeOf(item);
20
+ if (!Array.isArray(item) && prototype !== Object.prototype && prototype !== null)
21
+ return;
22
+ for (const key of Reflect.ownKeys(item)) {
23
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
24
+ if (!Object.hasOwn(descriptor, 'value'))
25
+ throw new Error(`Snapshot data cannot contain an accessor (${String(key)}). Materialize its value before publication; keep services in opaque instances.`);
26
+ validate(descriptor.value);
27
+ }
28
+ plain.push(item);
29
+ };
30
+ // Validate the complete graph before caching any proof. A rejected cyclic graph
31
+ // must not make a later publication skip validation through a previously seen child.
32
+ validate(value);
33
+ for (const item of plain)
34
+ Object.freeze(item);
35
+ for (const item of seen)
36
+ checked.add(item);
29
37
  return value;
30
38
  }
package/dist/tasks.js CHANGED
@@ -137,6 +137,7 @@ function taskBuilder(definition, runtime) {
137
137
  receive,
138
138
  controls,
139
139
  view: (view) => programView({
140
+ ...(definition.identity ? { identity: definition.identity } : {}),
140
141
  create,
141
142
  receive: (source, props) => receive(source, props),
142
143
  view,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effectweb",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Immutable Effect models and compiled JSX with direct DOM rendering",
5
5
  "homepage": "https://github.com/DerpyCrabs/EffectWeb",
6
6
  "bugs": {
@@ -127,6 +127,14 @@
127
127
  "./http": {
128
128
  "types": "./dist/http.d.ts",
129
129
  "import": "./dist/http.js"
130
+ },
131
+ "./infinite-query": {
132
+ "types": "./dist/infinite-query.d.ts",
133
+ "import": "./dist/infinite-query.js"
134
+ },
135
+ "./keyed-tasks": {
136
+ "types": "./dist/keyed-tasks.d.ts",
137
+ "import": "./dist/keyed-tasks.js"
130
138
  }
131
139
  },
132
140
  "publishConfig": {