wardens 0.5.1 → 0.6.0-rc.0

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/CHANGELOG.md CHANGED
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ### Added
10
+
11
+ - New context API carries state down the tree without plumbing through arguments.
12
+
13
+ ### Changed
14
+
15
+ - Renamed public type `ResourceContext` to `ResourceControls`.
16
+
9
17
  ## [0.5.1] - 2023-08-12
10
18
 
11
19
  ### Fixed
package/README.md CHANGED
@@ -30,7 +30,7 @@ Now define a pool that creates and manages workers:
30
30
 
31
31
  ```typescript
32
32
  async function WorkerPool(
33
- { create }: ResourceContext,
33
+ { create }: ResourceControls,
34
34
  config: { poolSize: number },
35
35
  ) {
36
36
  const promises = Array(config.poolSize).fill(Worker).map(create);
package/dist/wardens.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var E=Object.defineProperty;var C=(t,e,r)=>e in t?E(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var h=(t,e,r)=>(C(t,typeof e!="symbol"?e+"":e,r),r),g=(t,e,r)=>{if(!e.has(t))throw TypeError("Cannot "+r)};var a=(t,e,r)=>(g(t,e,"read from private field"),r?r.call(t):e.get(t)),f=(t,e,r)=>{if(e.has(t))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(t):e.set(t,r)},y=(t,e,r,o)=>(g(t,e,"write to private field"),o?o.call(t,r):e.set(t,r),r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function W(t){const e=new WeakMap;return new Proxy(t,{get(r,o){const s=Reflect.get(r,o,r);if(typeof s=="function"){if(e.has(s)===!1){const n=s.bind(r);Object.defineProperties(n,Object.getOwnPropertyDescriptors(s)),e.set(s,n)}return e.get(s)}return s},set(r,o,s){return Reflect.set(r,o,s,r)}})}const p=new WeakMap,S=new WeakSet;function O(t){return Proxy.revocable(t,{})}var l,i,u;class R{constructor(e,r){f(this,l,new WeakSet);f(this,i,void 0);f(this,u,void 0);h(this,"create",async(e,...r)=>{if(a(this,u).enforced)throw new Error("Cannot create new resources after teardown.");const o=await x(e,...r);return a(this,i).add(o),o});h(this,"destroy",async e=>{if(a(this,l).has(e))throw new Error("Resource already destroyed.");if(!a(this,i).has(e))throw new Error("You do not own this resource.");a(this,i).delete(e),a(this,l).add(e),await m(e)});y(this,i,e),y(this,u,r)}}l=new WeakMap,i=new WeakMap,u=new WeakMap;const x=async(t,...e)=>{const r={enforced:!1},o=new Set,s=new R(o,r);let n;try{n=await t(s,...e)}catch(v){const k=Array.from(o).reverse(),b=(await Promise.allSettled(k.map(d=>s.destroy(d)))).filter(d=>d.status==="rejected");throw b.length?P(b.map(d=>d.reason),{cause:v}):v}const c=n.value,{proxy:w,revoke:j}=O(c);return S.add(w),p.set(w,{curfew:r,resource:n,children:o,revoke:j}),w},m=async t=>{if(!S.has(t))throw new Error("Cannot destroy object. It is not a resource.");const e=p.get(t);if(e){p.delete(t),e.revoke();let r={status:"fulfilled",value:void 0};if(e.resource.destroy)try{await e.resource.destroy()}catch(c){r={status:"rejected",reason:c}}e.curfew.enforced=!0;const o=Array.from(e.children).reverse().map(m),s=await Promise.allSettled(o),n=[r].concat(s).filter(c=>c.status==="rejected");if(n.length)throw P(n.map(c=>c.reason))}},P=(t,e)=>t.length===1?t[0]:new B(t,e);class B extends Error{constructor(e,r){super("Some resources could not be destroyed. See the `failures` property for details.",r),this.failures=e}}exports.bindContext=W;exports.create=x;exports.destroy=m;
1
+ "use strict";var I=Object.defineProperty;var R=(r,e,t)=>e in r?I(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var f=(r,e,t)=>(R(r,typeof e!="symbol"?e+"":e,t),t),j=(r,e,t)=>{if(!e.has(r))throw TypeError("Cannot "+t)};var n=(r,e,t)=>(j(r,e,"read from private field"),t?t.call(r):e.get(r)),l=(r,e,t)=>{if(e.has(r))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(r):e.set(r,t)},h=(r,e,t,s)=>(j(r,e,"write to private field"),s?s.call(r,t):e.set(r,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function B(r){const e=new WeakMap;return new Proxy(r,{get(t,s){const o=Reflect.get(t,s,t);if(typeof o=="function"){if(e.has(o)===!1){const a=o.bind(t);Object.defineProperties(a,Object.getOwnPropertyDescriptors(o)),e.set(o,a)}return e.get(o)}return o},set(t,s,o){return Reflect.set(t,s,o,t)}})}const m=new WeakMap,P=new WeakSet;function M(r){return Proxy.revocable(r,{})}var g,w;class x{constructor(e){l(this,g,Symbol("Context ID"));l(this,w,void 0);h(this,w,e)}static getId(e){return n(e,g)}static getDefaultValue(e){var t;return n(t=e,w).call(t)}}g=new WeakMap,w=new WeakMap;const A=r=>new x(r);var y,u,p,i;class T{constructor(e,t,s){l(this,y,new WeakSet);l(this,u,void 0);l(this,p,void 0);l(this,i,void 0);f(this,"create",async(e,...t)=>{if(n(this,p).enforced)throw new Error("Cannot create new resources after teardown.");const s=Object.create(n(this,i)),o=await k(s,e,...t);return n(this,u).add(o),o});f(this,"destroy",async e=>{if(n(this,y).has(e))throw new Error("Resource already destroyed.");if(!n(this,u).has(e))throw new Error("You do not own this resource.");n(this,u).delete(e),n(this,y).add(e),await b(e)});f(this,"setContext",(e,t)=>{n(this,i)[x.getId(e)]=t});f(this,"getContext",e=>{const t=x.getId(e);return t in n(this,i)?n(this,i)[t]:x.getDefaultValue(e)});h(this,i,e),h(this,u,t),h(this,p,s)}}y=new WeakMap,u=new WeakMap,p=new WeakMap,i=new WeakMap;const k=async(r,e,...t)=>{const s={enforced:!1},o=new Set,a=new T(r,o,s);let c;try{c=await e(a,...t)}catch(v){const W=Array.from(o).reverse(),S=(await Promise.allSettled(W.map(d=>a.destroy(d)))).filter(d=>d.status==="rejected");throw S.length?D(S.map(d=>d.reason),{cause:v}):v}const E=c.value,{proxy:C,revoke:O}=M(E);return P.add(C),m.set(C,{curfew:s,resource:c,children:o,revoke:O}),C},b=async r=>{if(!P.has(r))throw new Error("Cannot destroy object. It is not a resource.");const e=m.get(r);if(e){m.delete(r),e.revoke();let t={status:"fulfilled",value:void 0};if(e.resource.destroy)try{await e.resource.destroy()}catch(c){t={status:"rejected",reason:c}}e.curfew.enforced=!0;const s=Array.from(e.children).reverse().map(b),o=await Promise.allSettled(s),a=[t].concat(o).filter(c=>c.status==="rejected");if(a.length)throw D(a.map(c=>c.reason))}},D=(r,e)=>r.length===1?r[0]:new V(r,e);class V extends Error{constructor(e,t){super("Some resources could not be destroyed. See the `failures` property for details.",t),this.failures=e}}const H=async(r,...e)=>k(Object.create(null),r,...e);exports.bindContext=B;exports.create=H;exports.createContext=A;exports.destroy=b;
package/dist/wardens.js CHANGED
@@ -1,98 +1,125 @@
1
- var P = Object.defineProperty;
2
- var j = (t, e, r) => e in t ? P(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
3
- var h = (t, e, r) => (j(t, typeof e != "symbol" ? e + "" : e, r), r), x = (t, e, r) => {
4
- if (!e.has(t))
5
- throw TypeError("Cannot " + r);
1
+ var O = Object.defineProperty;
2
+ var R = (r, e, t) => e in r ? O(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
+ var f = (r, e, t) => (R(r, typeof e != "symbol" ? e + "" : e, t), t), S = (r, e, t) => {
4
+ if (!e.has(r))
5
+ throw TypeError("Cannot " + t);
6
6
  };
7
- var a = (t, e, r) => (x(t, e, "read from private field"), r ? r.call(t) : e.get(t)), f = (t, e, r) => {
8
- if (e.has(t))
7
+ var n = (r, e, t) => (S(r, e, "read from private field"), t ? t.call(r) : e.get(r)), l = (r, e, t) => {
8
+ if (e.has(r))
9
9
  throw TypeError("Cannot add the same private member more than once");
10
- e instanceof WeakSet ? e.add(t) : e.set(t, r);
11
- }, y = (t, e, r, o) => (x(t, e, "write to private field"), o ? o.call(t, r) : e.set(t, r), r);
12
- function A(t) {
10
+ e instanceof WeakSet ? e.add(r) : e.set(r, t);
11
+ }, h = (r, e, t, s) => (S(r, e, "write to private field"), s ? s.call(r, t) : e.set(r, t), t);
12
+ function T(r) {
13
13
  const e = /* @__PURE__ */ new WeakMap();
14
- return new Proxy(t, {
15
- get(r, o) {
16
- const s = Reflect.get(r, o, r);
17
- if (typeof s == "function") {
18
- if (e.has(s) === !1) {
19
- const n = s.bind(r);
14
+ return new Proxy(r, {
15
+ get(t, s) {
16
+ const o = Reflect.get(t, s, t);
17
+ if (typeof o == "function") {
18
+ if (e.has(o) === !1) {
19
+ const a = o.bind(t);
20
20
  Object.defineProperties(
21
- n,
22
- Object.getOwnPropertyDescriptors(s)
23
- ), e.set(s, n);
21
+ a,
22
+ Object.getOwnPropertyDescriptors(o)
23
+ ), e.set(o, a);
24
24
  }
25
- return e.get(s);
25
+ return e.get(o);
26
26
  }
27
- return s;
27
+ return o;
28
28
  },
29
- set(r, o, s) {
30
- return Reflect.set(r, o, s, r);
29
+ set(t, s, o) {
30
+ return Reflect.set(t, s, o, t);
31
31
  }
32
32
  });
33
33
  }
34
- const p = /* @__PURE__ */ new WeakMap(), g = /* @__PURE__ */ new WeakSet();
35
- function W(t) {
36
- return Proxy.revocable(t, {});
34
+ const C = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakSet();
35
+ function B(r) {
36
+ return Proxy.revocable(r, {});
37
37
  }
38
- var l, i, u;
39
- class C {
40
- constructor(e, r) {
41
- f(this, l, /* @__PURE__ */ new WeakSet());
42
- f(this, i, void 0);
43
- f(this, u, void 0);
38
+ var g, w;
39
+ class x {
40
+ constructor(e) {
41
+ l(this, g, Symbol("Context ID"));
42
+ l(this, w, void 0);
43
+ h(this, w, e);
44
+ }
45
+ static getId(e) {
46
+ return n(e, g);
47
+ }
48
+ static getDefaultValue(e) {
49
+ var t;
50
+ return n(t = e, w).call(t);
51
+ }
52
+ }
53
+ g = new WeakMap(), w = new WeakMap();
54
+ const U = (r) => new x(r);
55
+ var y, u, p, i;
56
+ class A {
57
+ constructor(e, t, s) {
58
+ l(this, y, /* @__PURE__ */ new WeakSet());
59
+ l(this, u, void 0);
60
+ l(this, p, void 0);
61
+ l(this, i, void 0);
44
62
  /** Provision an owned resource and make sure it doesn't outlive us. */
45
- h(this, "create", async (e, ...r) => {
46
- if (a(this, u).enforced)
63
+ f(this, "create", async (e, ...t) => {
64
+ if (n(this, p).enforced)
47
65
  throw new Error("Cannot create new resources after teardown.");
48
- const o = await R(e, ...r);
49
- return a(this, i).add(o), o;
66
+ const s = Object.create(n(this, i)), o = await k(s, e, ...t);
67
+ return n(this, u).add(o), o;
50
68
  });
51
69
  /**
52
70
  * Tear down a resource. Happens automatically when resource owners are
53
71
  * deallocated.
54
72
  */
55
- h(this, "destroy", async (e) => {
56
- if (a(this, l).has(e))
73
+ f(this, "destroy", async (e) => {
74
+ if (n(this, y).has(e))
57
75
  throw new Error("Resource already destroyed.");
58
- if (!a(this, i).has(e))
76
+ if (!n(this, u).has(e))
59
77
  throw new Error("You do not own this resource.");
60
- a(this, i).delete(e), a(this, l).add(e), await S(e);
78
+ n(this, u).delete(e), n(this, y).add(e), await D(e);
79
+ });
80
+ /** Store a value in context. Anything down the chain can read it. */
81
+ f(this, "setContext", (e, t) => {
82
+ n(this, i)[x.getId(e)] = t;
83
+ });
84
+ /** Retrieve a value from context, or a default if it is unset. */
85
+ f(this, "getContext", (e) => {
86
+ const t = x.getId(e);
87
+ return t in n(this, i) ? n(this, i)[t] : x.getDefaultValue(e);
61
88
  });
62
- y(this, i, e), y(this, u, r);
89
+ h(this, i, e), h(this, u, t), h(this, p, s);
63
90
  }
64
91
  }
65
- l = new WeakMap(), i = new WeakMap(), u = new WeakMap();
66
- const R = async (t, ...e) => {
67
- const r = { enforced: !1 }, o = /* @__PURE__ */ new Set(), s = new C(o, r);
68
- let n;
92
+ y = new WeakMap(), u = new WeakMap(), p = new WeakMap(), i = new WeakMap();
93
+ const k = async (r, e, ...t) => {
94
+ const s = { enforced: !1 }, o = /* @__PURE__ */ new Set(), a = new A(r, o, s);
95
+ let c;
69
96
  try {
70
- n = await t(s, ...e);
71
- } catch (m) {
72
- const E = Array.from(o).reverse(), v = (await Promise.allSettled(
73
- E.map((d) => s.destroy(d))
97
+ c = await e(a, ...t);
98
+ } catch (b) {
99
+ const I = Array.from(o).reverse(), v = (await Promise.allSettled(
100
+ I.map((d) => a.destroy(d))
74
101
  )).filter(
75
102
  (d) => d.status === "rejected"
76
103
  );
77
- throw v.length ? b(
104
+ throw v.length ? E(
78
105
  v.map((d) => d.reason),
79
- { cause: m }
80
- ) : m;
106
+ { cause: b }
107
+ ) : b;
81
108
  }
82
- const c = n.value, { proxy: w, revoke: k } = W(c);
83
- return g.add(w), p.set(w, {
84
- curfew: r,
85
- resource: n,
109
+ const P = c.value, { proxy: m, revoke: W } = B(P);
110
+ return j.add(m), C.set(m, {
111
+ curfew: s,
112
+ resource: c,
86
113
  children: o,
87
- revoke: k
88
- }), w;
89
- }, S = async (t) => {
90
- if (!g.has(t))
114
+ revoke: W
115
+ }), m;
116
+ }, D = async (r) => {
117
+ if (!j.has(r))
91
118
  throw new Error("Cannot destroy object. It is not a resource.");
92
- const e = p.get(t);
119
+ const e = C.get(r);
93
120
  if (e) {
94
- p.delete(t), e.revoke();
95
- let r = {
121
+ C.delete(r), e.revoke();
122
+ let t = {
96
123
  status: "fulfilled",
97
124
  value: void 0
98
125
  };
@@ -100,26 +127,28 @@ const R = async (t, ...e) => {
100
127
  try {
101
128
  await e.resource.destroy();
102
129
  } catch (c) {
103
- r = { status: "rejected", reason: c };
130
+ t = { status: "rejected", reason: c };
104
131
  }
105
132
  e.curfew.enforced = !0;
106
- const o = Array.from(e.children).reverse().map(S), s = await Promise.allSettled(o), n = [r].concat(s).filter(
133
+ const s = Array.from(e.children).reverse().map(D), o = await Promise.allSettled(s), a = [t].concat(o).filter(
107
134
  (c) => c.status === "rejected"
108
135
  );
109
- if (n.length)
110
- throw b(n.map((c) => c.reason));
136
+ if (a.length)
137
+ throw E(a.map((c) => c.reason));
111
138
  }
112
- }, b = (t, e) => t.length === 1 ? t[0] : new B(t, e);
113
- class B extends Error {
114
- constructor(e, r) {
139
+ }, E = (r, e) => r.length === 1 ? r[0] : new M(r, e);
140
+ class M extends Error {
141
+ constructor(e, t) {
115
142
  super(
116
143
  "Some resources could not be destroyed. See the `failures` property for details.",
117
- r
144
+ t
118
145
  ), this.failures = e;
119
146
  }
120
147
  }
148
+ const Y = async (r, ...e) => k(/* @__PURE__ */ Object.create(null), r, ...e);
121
149
  export {
122
- A as bindContext,
123
- R as create,
124
- S as destroy
150
+ T as bindContext,
151
+ Y as create,
152
+ U as createContext,
153
+ D as destroy
125
154
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wardens",
3
- "version": "0.5.1",
3
+ "version": "0.6.0-rc.0",
4
4
  "description": "A framework for resource management",
5
5
  "type": "module",
6
6
  "main": "./dist/wardens.cjs",
@@ -79,11 +79,11 @@
79
79
  }
80
80
  },
81
81
  "devDependencies": {
82
- "@typescript-eslint/eslint-plugin": "6.3.0",
83
- "@typescript-eslint/parser": "6.3.0",
82
+ "@typescript-eslint/eslint-plugin": "6.4.0",
83
+ "@typescript-eslint/parser": "6.4.0",
84
84
  "eslint": "8.47.0",
85
85
  "husky": "8.0.3",
86
- "lint-staged": "13.2.3",
86
+ "lint-staged": "14.0.0",
87
87
  "prettier": "2.8.8",
88
88
  "typescript": "5.1.6",
89
89
  "vite": "4.4.9",
@@ -0,0 +1,186 @@
1
+ import ResourceControls from '../resource-controls';
2
+ import { create } from '../';
3
+ import { createContext } from '../inherited-context';
4
+
5
+ describe('ResourceControls', () => {
6
+ async function Test() {
7
+ return { value: {} };
8
+ }
9
+
10
+ it('can spawn children of its own', async () => {
11
+ const Child = async () => ({
12
+ value: { child: true },
13
+ });
14
+
15
+ const Parent = async (resource: ResourceControls) => {
16
+ return { value: await resource.create(Child) };
17
+ };
18
+
19
+ await expect(create(Parent)).resolves.toEqual({ child: true });
20
+ });
21
+
22
+ it('can deallocate child resources on demand', async () => {
23
+ const spy = vi.fn();
24
+
25
+ const Child = async () => ({
26
+ value: { child: true },
27
+ destroy: spy,
28
+ });
29
+
30
+ const Parent = async (resource: ResourceControls) => {
31
+ const child = await resource.create(Child);
32
+ await resource.destroy(child);
33
+
34
+ return {
35
+ value: { parent: true },
36
+ };
37
+ };
38
+
39
+ await expect(create(Parent)).resolves.toEqual({ parent: true });
40
+ expect(spy).toHaveBeenCalled();
41
+ });
42
+
43
+ it('fails to destroy resources owned by someone else', async () => {
44
+ const test = await create(Test);
45
+
46
+ const Sneaky = async (resource: ResourceControls) => {
47
+ await resource.destroy(test);
48
+ return { value: {} };
49
+ };
50
+
51
+ await expect(create(Sneaky)).rejects.toThrow(/do not own/i);
52
+ });
53
+
54
+ it('binds create/destroy handlers to the class instance', async () => {
55
+ async function Allocator({ create, destroy }: ResourceControls) {
56
+ const test = await create(Test);
57
+ await destroy(test);
58
+
59
+ return { value: [] };
60
+ }
61
+
62
+ await expect(create(Allocator)).resolves.not.toThrow();
63
+ });
64
+
65
+ it('indicates if a resource was already destroyed', async () => {
66
+ async function Allocator(resource: ResourceControls) {
67
+ const test = await resource.create(Test);
68
+ await resource.destroy(test);
69
+ await resource.destroy(test);
70
+
71
+ return { value: [] };
72
+ }
73
+
74
+ await expect(create(Allocator)).rejects.toThrow(/already destroyed/i);
75
+ });
76
+
77
+ it('can set and retrieve context', async () => {
78
+ const SharedValue = createContext(() => 'none');
79
+
80
+ const Test = async (resource: ResourceControls) => {
81
+ expect(resource.getContext(SharedValue)).toBe('none');
82
+
83
+ resource.setContext(SharedValue, 'saved');
84
+ expect(resource.getContext(SharedValue)).toBe('saved');
85
+
86
+ resource.setContext(SharedValue, 'updated');
87
+ expect(resource.getContext(SharedValue)).toBe('updated');
88
+
89
+ return { value: [] };
90
+ };
91
+
92
+ await expect(create(Test)).resolves.toEqual([]);
93
+ });
94
+
95
+ it('passes context to child resources', async () => {
96
+ const SharedValue = createContext<null | string>(() => null);
97
+
98
+ const Child = async (ctx: ResourceControls) => ({
99
+ value: { content: ctx.getContext(SharedValue) },
100
+ });
101
+
102
+ const Parent = async (resource: ResourceControls) => {
103
+ resource.setContext(SharedValue, 'inherited');
104
+ return { value: await resource.create(Child) };
105
+ };
106
+
107
+ await expect(create(Parent)).resolves.toEqual({ content: 'inherited' });
108
+ });
109
+
110
+ it('can override context without affecting the parent', async () => {
111
+ const Message = createContext<null | string>(() => null);
112
+ const Child = async (resource: ResourceControls) => {
113
+ // This should *NOT* affect the parent context.
114
+ resource.setContext(Message, 'child context');
115
+
116
+ return {
117
+ value: { content: resource.getContext(Message) },
118
+ };
119
+ };
120
+
121
+ const Parent = async (resource: ResourceControls) => {
122
+ resource.setContext(Message, 'parent context');
123
+ const child = await resource.create(Child);
124
+
125
+ expect(resource.getContext(Message)).toBe('parent context');
126
+
127
+ return {
128
+ value: {
129
+ content: resource.getContext(Message),
130
+ child,
131
+ },
132
+ };
133
+ };
134
+
135
+ await expect(create(Parent)).resolves.toEqual({
136
+ content: 'parent context',
137
+ child: { content: 'child context' },
138
+ });
139
+ });
140
+
141
+ it('allows two siblings to have different context values', async () => {
142
+ const Message = createContext<null | string>(() => null);
143
+ const Child = async (resource: ResourceControls, msg: string) => {
144
+ resource.setContext(Message, msg);
145
+
146
+ return {
147
+ value: { getMessage: () => resource.getContext(Message) },
148
+ };
149
+ };
150
+
151
+ const Parent = async (resource: ResourceControls) => {
152
+ resource.setContext(Message, 'parent context');
153
+
154
+ return {
155
+ value: await Promise.all([
156
+ resource.create(Child, 'child context 1'),
157
+ resource.create(Child, 'child context 2'),
158
+ ]),
159
+ };
160
+ };
161
+
162
+ const [child1, child2] = await create(Parent);
163
+ expect(child1.getMessage()).toBe('child context 1');
164
+ expect(child2.getMessage()).toBe('child context 2');
165
+ });
166
+
167
+ it('provides a live view of the current value, not just a snapshot', async () => {
168
+ const Message = createContext(() => 'default');
169
+ const Child = async (resource: ResourceControls) => ({
170
+ value: { getMessage: () => resource.getContext(Message) },
171
+ });
172
+
173
+ const Parent = async (resource: ResourceControls) => ({
174
+ value: {
175
+ setMessage: (msg: string) => resource.setContext(Message, msg),
176
+ child: await resource.create(Child),
177
+ },
178
+ });
179
+
180
+ const { setMessage, child } = await create(Parent);
181
+ expect(child.getMessage()).toBe('default');
182
+
183
+ setMessage('updated');
184
+ expect(child.getMessage()).toBe('updated');
185
+ });
186
+ });
@@ -1,13 +1,13 @@
1
- import type ResourceContext from '../resource-context';
2
- import { create, destroy } from '../allocation';
1
+ import type ResourceControls from '../resource-controls';
2
+ import { create, destroy } from '../root-lifecycle';
3
3
  import bindContext from '../bind-context';
4
4
 
5
- describe('allocation', () => {
5
+ describe('roots', () => {
6
6
  describe('create', () => {
7
7
  it('allocates the resource', async () => {
8
8
  const config = { test: 'init-args' };
9
9
  const Test = vi.fn(
10
- async (_resource: ResourceContext, config: { test: string }) => ({
10
+ async (_resource: ResourceControls, config: { test: string }) => ({
11
11
  value: config,
12
12
  }),
13
13
  );
@@ -22,7 +22,7 @@ describe('allocation', () => {
22
22
  const First = async () => ({ value: [], destroy: () => spy('1st') });
23
23
  const Second = async () => ({ value: [], destroy: () => spy('2nd') });
24
24
 
25
- const Parent = async (resource: ResourceContext) => {
25
+ const Parent = async (resource: ResourceControls) => {
26
26
  await resource.create(First);
27
27
  await resource.create(Second);
28
28
  throw new Error('Testing resource initialization errors');
@@ -42,7 +42,7 @@ describe('allocation', () => {
42
42
  },
43
43
  });
44
44
 
45
- const Parent = async (resource: ResourceContext) => {
45
+ const Parent = async (resource: ResourceControls) => {
46
46
  await resource.create(Child);
47
47
  await resource.create(Child);
48
48
  throw parentError;
@@ -89,7 +89,7 @@ describe('allocation', () => {
89
89
  it('automatically unmounts all children', async () => {
90
90
  const spy = vi.fn();
91
91
  const Child = async () => ({ value: [], destroy: spy });
92
- async function Parent(resource: ResourceContext) {
92
+ async function Parent(resource: ResourceControls) {
93
93
  await resource.create(Child);
94
94
  return { value: [] };
95
95
  }
@@ -109,7 +109,7 @@ describe('allocation', () => {
109
109
  },
110
110
  });
111
111
 
112
- const Parent = async (resource: ResourceContext) => {
112
+ const Parent = async (resource: ResourceControls) => {
113
113
  await resource.create(Child);
114
114
  return { value: [] };
115
115
  };
@@ -123,7 +123,7 @@ describe('allocation', () => {
123
123
  const First = async () => ({ value: [], destroy: () => spy('1st') });
124
124
  const Second = async () => ({ value: [], destroy: () => spy('2nd') });
125
125
 
126
- const Parent = async (resource: ResourceContext) => {
126
+ const Parent = async (resource: ResourceControls) => {
127
127
  await resource.create(First);
128
128
  await resource.create(Second);
129
129
  return { value: [] };
@@ -144,7 +144,7 @@ describe('allocation', () => {
144
144
  },
145
145
  });
146
146
 
147
- const Parent = async (resource: ResourceContext) => {
147
+ const Parent = async (resource: ResourceControls) => {
148
148
  await resource.create(Child);
149
149
  await resource.create(Child);
150
150
  return { value: [] };
@@ -158,7 +158,7 @@ describe('allocation', () => {
158
158
 
159
159
  it('ensures child resources outlive their consumers', async () => {
160
160
  const Child = async () => ({ value: [1] });
161
- const Parent = async (resource: ResourceContext) => {
161
+ const Parent = async (resource: ResourceControls) => {
162
162
  const child = await resource.create(Child);
163
163
  return {
164
164
  value: [],
@@ -176,7 +176,7 @@ describe('allocation', () => {
176
176
  it('destroys child resources even if the parent fails to close', async () => {
177
177
  const spy = vi.fn();
178
178
  const Child = async () => ({ value: [], destroy: spy });
179
- const Parent = async (resource: ResourceContext) => {
179
+ const Parent = async (resource: ResourceControls) => {
180
180
  await resource.create(Child);
181
181
  return {
182
182
  value: [],
@@ -202,7 +202,7 @@ describe('allocation', () => {
202
202
  },
203
203
  });
204
204
 
205
- const Parent = async (resource: ResourceContext) => {
205
+ const Parent = async (resource: ResourceControls) => {
206
206
  await resource.create(Child);
207
207
  return {
208
208
  value: [],
@@ -220,7 +220,7 @@ describe('allocation', () => {
220
220
 
221
221
  it('guards against creating new resources after teardown', async () => {
222
222
  const Child = async () => ({ value: [] });
223
- const Parent = async (resource: ResourceContext) => ({
223
+ const Parent = async (resource: ResourceControls) => ({
224
224
  value: bindContext(resource),
225
225
  });
226
226