UNPKG

7.25 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Slot = void 0;
4// This currentContext variable will only be used if the makeSlotClass
5// function is called, which happens only if this is the first copy of the
6// @wry/context package to be imported.
7var currentContext = null;
8// This unique internal object is used to denote the absence of a value
9// for a given Slot, and is never exposed to outside code.
10var MISSING_VALUE = {};
11var idCounter = 1;
12// Although we can't do anything about the cost of duplicated code from
13// accidentally bundling multiple copies of the @wry/context package, we can
14// avoid creating the Slot class more than once using makeSlotClass.
15var makeSlotClass = function () { return /** @class */ (function () {
16 function Slot() {
17 // If you have a Slot object, you can find out its slot.id, but you cannot
18 // guess the slot.id of a Slot you don't have access to, thanks to the
19 // randomized suffix.
20 this.id = [
21 "slot",
22 idCounter++,
23 Date.now(),
24 Math.random().toString(36).slice(2),
25 ].join(":");
26 }
27 Slot.prototype.hasValue = function () {
28 for (var context_1 = currentContext; context_1; context_1 = context_1.parent) {
29 // We use the Slot object iself as a key to its value, which means the
30 // value cannot be obtained without a reference to the Slot object.
31 if (this.id in context_1.slots) {
32 var value = context_1.slots[this.id];
33 if (value === MISSING_VALUE)
34 break;
35 if (context_1 !== currentContext) {
36 // Cache the value in currentContext.slots so the next lookup will
37 // be faster. This caching is safe because the tree of contexts and
38 // the values of the slots are logically immutable.
39 currentContext.slots[this.id] = value;
40 }
41 return true;
42 }
43 }
44 if (currentContext) {
45 // If a value was not found for this Slot, it's never going to be found
46 // no matter how many times we look it up, so we might as well cache
47 // the absence of the value, too.
48 currentContext.slots[this.id] = MISSING_VALUE;
49 }
50 return false;
51 };
52 Slot.prototype.getValue = function () {
53 if (this.hasValue()) {
54 return currentContext.slots[this.id];
55 }
56 };
57 Slot.prototype.withValue = function (value, callback,
58 // Given the prevalence of arrow functions, specifying arguments is likely
59 // to be much more common than specifying `this`, hence this ordering:
60 args, thisArg) {
61 var _a;
62 var slots = (_a = {
63 __proto__: null
64 },
65 _a[this.id] = value,
66 _a);
67 var parent = currentContext;
68 currentContext = { parent: parent, slots: slots };
69 try {
70 // Function.prototype.apply allows the arguments array argument to be
71 // omitted or undefined, so args! is fine here.
72 return callback.apply(thisArg, args);
73 }
74 finally {
75 currentContext = parent;
76 }
77 };
78 // Capture the current context and wrap a callback function so that it
79 // reestablishes the captured context when called.
80 Slot.bind = function (callback) {
81 var context = currentContext;
82 return function () {
83 var saved = currentContext;
84 try {
85 currentContext = context;
86 return callback.apply(this, arguments);
87 }
88 finally {
89 currentContext = saved;
90 }
91 };
92 };
93 // Immediately run a callback function without any captured context.
94 Slot.noContext = function (callback,
95 // Given the prevalence of arrow functions, specifying arguments is likely
96 // to be much more common than specifying `this`, hence this ordering:
97 args, thisArg) {
98 if (currentContext) {
99 var saved = currentContext;
100 try {
101 currentContext = null;
102 // Function.prototype.apply allows the arguments array argument to be
103 // omitted or undefined, so args! is fine here.
104 return callback.apply(thisArg, args);
105 }
106 finally {
107 currentContext = saved;
108 }
109 }
110 else {
111 return callback.apply(thisArg, args);
112 }
113 };
114 return Slot;
115}()); };
116function maybe(fn) {
117 try {
118 return fn();
119 }
120 catch (ignored) { }
121}
122// We store a single global implementation of the Slot class as a permanent
123// non-enumerable property of the globalThis object. This obfuscation does
124// nothing to prevent access to the Slot class, but at least it ensures the
125// implementation (i.e. currentContext) cannot be tampered with, and all copies
126// of the @wry/context package (hopefully just one) will share the same Slot
127// implementation. Since the first copy of the @wry/context package to be
128// imported wins, this technique imposes a steep cost for any future breaking
129// changes to the Slot class.
130var globalKey = "@wry/context:Slot";
131var host =
132// Prefer globalThis when available.
133// https://github.com/benjamn/wryware/issues/347
134maybe(function () { return globalThis; }) ||
135 // Fall back to global, which works in Node.js and may be converted by some
136 // bundlers to the appropriate identifier (window, self, ...) depending on the
137 // bundling target. https://github.com/endojs/endo/issues/576#issuecomment-1178515224
138 maybe(function () { return global; }) ||
139 // Otherwise, use a dummy host that's local to this module. We used to fall
140 // back to using the Array constructor as a namespace, but that was flagged in
141 // https://github.com/benjamn/wryware/issues/347, and can be avoided.
142 Object.create(null);
143// Whichever globalHost we're using, make TypeScript happy about the additional
144// globalKey property.
145var globalHost = host;
146exports.Slot = globalHost[globalKey] ||
147 // Earlier versions of this package stored the globalKey property on the Array
148 // constructor, so we check there as well, to prevent Slot class duplication.
149 Array[globalKey] ||
150 (function (Slot) {
151 try {
152 Object.defineProperty(globalHost, globalKey, {
153 value: Slot,
154 enumerable: false,
155 writable: false,
156 // When it was possible for globalHost to be the Array constructor (a
157 // legacy Slot dedup strategy), it was important for the property to be
158 // configurable:true so it could be deleted. That does not seem to be as
159 // important when globalHost is the global object, but I don't want to
160 // cause similar problems again, and configurable:true seems safest.
161 // https://github.com/endojs/endo/issues/576#issuecomment-1178274008
162 configurable: true
163 });
164 }
165 finally {
166 return Slot;
167 }
168 })(makeSlotClass());
169//# sourceMappingURL=slot.js.map
\No newline at end of file