@stainless-api/playgrounds 0.0.1-beta.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 +15 -0
- package/README.md +23 -0
- package/eslint.config.js +2 -0
- package/package.json +69 -0
- package/src/Logs.tsx +216 -0
- package/src/Panel.tsx +21 -0
- package/src/PlaygroundPanelWrapper.tsx +5 -0
- package/src/build-py-types.ts +152 -0
- package/src/build-ts-types.ts +70 -0
- package/src/build.ts +97 -0
- package/src/codemirror/comlink.ts +698 -0
- package/src/codemirror/curl/curlconverter.vendor.js +7959 -0
- package/src/codemirror/curl.ts +108 -0
- package/src/codemirror/deps.ts +12 -0
- package/src/codemirror/fix-lsp-markdown.ts +50 -0
- package/src/codemirror/lsp.ts +87 -0
- package/src/codemirror/python/anser.ts +398 -0
- package/src/codemirror/python/pyodide.ts +180 -0
- package/src/codemirror/python.ts +160 -0
- package/src/codemirror/react.tsx +615 -0
- package/src/codemirror/sanitize-html.ts +12 -0
- package/src/codemirror/shiki.ts +65 -0
- package/src/codemirror/typescript/cdn-typescript.d.ts +1 -0
- package/src/codemirror/typescript/cdn-typescript.js +1 -0
- package/src/codemirror/typescript/console.ts +590 -0
- package/src/codemirror/typescript/get-signature.ts +94 -0
- package/src/codemirror/typescript/prettier-plugin-external-typescript.vendor.js +4968 -0
- package/src/codemirror/typescript/runner.ts +396 -0
- package/src/codemirror/typescript/special-info.ts +171 -0
- package/src/codemirror/typescript/worker.ts +292 -0
- package/src/codemirror/typescript.tsx +198 -0
- package/src/create.tsx +44 -0
- package/src/icon.tsx +21 -0
- package/src/index.ts +6 -0
- package/src/logs-context.ts +5 -0
- package/src/playground.css +359 -0
- package/src/sandbox-worker/in-frame.js +179 -0
- package/src/sandbox-worker/index.ts +202 -0
- package/src/use-storage.ts +54 -0
- package/src/util.ts +29 -0
- package/src/virtual-module.d.ts +45 -0
- package/src/vite-env.d.ts +1 -0
- package/test/get-signature.test.ts +73 -0
- package/test/use-storage.test.ts +60 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* This worker runs user code and handles environment setup, processes console logs,
|
|
4
|
+
* and implements the structured object logging api so users can expand and collapse
|
|
5
|
+
* logged objects.
|
|
6
|
+
*/
|
|
7
|
+
import { expose } from '../comlink.ts';
|
|
8
|
+
import { createConsole } from './console.ts';
|
|
9
|
+
import process from 'unenv/runtime/node/process/index';
|
|
10
|
+
import { Entries, isProxy, specialInfo } from './special-info.ts';
|
|
11
|
+
import type { Logger, Part } from '../../Logs.tsx';
|
|
12
|
+
import { getSignature } from './get-signature.ts';
|
|
13
|
+
|
|
14
|
+
// add the process polyfill to the global
|
|
15
|
+
globalThis.process = process;
|
|
16
|
+
// unenv uses a weird proxy thing for process.env, just use an object instead.
|
|
17
|
+
globalThis.process.env = {};
|
|
18
|
+
|
|
19
|
+
let hideSpecial = false;
|
|
20
|
+
|
|
21
|
+
// WebIDL types like Request/Response/URL/etc. use getters and setters instead of value
|
|
22
|
+
// properties, but we don't want to trigger user side effects when logging.
|
|
23
|
+
// To fix this, keep a set of getters that are safe to call.
|
|
24
|
+
const safeGetters = new Set(
|
|
25
|
+
Reflect.ownKeys(globalThis)
|
|
26
|
+
.map((e) => {
|
|
27
|
+
try {
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
return (globalThis as any)[e].prototype;
|
|
30
|
+
} catch {
|
|
31
|
+
// ignore errors
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.filter((e) => e && e !== Function.prototype && e !== Object.prototype)
|
|
35
|
+
.flatMap((e) => Object.values(Object.getOwnPropertyDescriptors(e)))
|
|
36
|
+
.flatMap((e) => e?.get ?? []),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function* prototypeChain(
|
|
40
|
+
obj: object | ((..._: never[]) => unknown),
|
|
41
|
+
): Generator<object | ((..._: never[]) => unknown)> {
|
|
42
|
+
while (obj) {
|
|
43
|
+
yield obj;
|
|
44
|
+
obj = Object.getPrototypeOf(obj);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type Properties = {
|
|
49
|
+
key: unknown;
|
|
50
|
+
configurable: boolean;
|
|
51
|
+
enumerable: boolean;
|
|
52
|
+
value: unknown;
|
|
53
|
+
get: unknown;
|
|
54
|
+
set: unknown;
|
|
55
|
+
}[];
|
|
56
|
+
|
|
57
|
+
async function properties(value: unknown): Promise<{
|
|
58
|
+
properties: Properties;
|
|
59
|
+
specials: { name: string; value: unknown }[];
|
|
60
|
+
}> {
|
|
61
|
+
const specials = hideSpecial
|
|
62
|
+
? []
|
|
63
|
+
: (await specialInfo(value)).map(({ name, value }) => ({
|
|
64
|
+
name,
|
|
65
|
+
value: value,
|
|
66
|
+
}));
|
|
67
|
+
const isObject = (typeof value === 'object' || typeof value === 'function') && value !== null;
|
|
68
|
+
if (isObject && isProxy(value)) {
|
|
69
|
+
return {
|
|
70
|
+
properties: [],
|
|
71
|
+
specials,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
properties: isObject
|
|
76
|
+
? [
|
|
77
|
+
...Reflect.ownKeys(value)
|
|
78
|
+
.map((key): Properties[number] | undefined => {
|
|
79
|
+
let desc: PropertyDescriptor | undefined;
|
|
80
|
+
try {
|
|
81
|
+
desc = Object.getOwnPropertyDescriptor(value, key);
|
|
82
|
+
} catch {
|
|
83
|
+
try {
|
|
84
|
+
desc = {
|
|
85
|
+
configurable: true,
|
|
86
|
+
enumerable: true,
|
|
87
|
+
value: Reflect.get(value, key, value),
|
|
88
|
+
};
|
|
89
|
+
} catch {
|
|
90
|
+
// ignore error
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (desc) {
|
|
94
|
+
return {
|
|
95
|
+
key: key,
|
|
96
|
+
configurable: !!desc.configurable,
|
|
97
|
+
enumerable: !!desc.enumerable,
|
|
98
|
+
value: desc.value,
|
|
99
|
+
get: desc.get,
|
|
100
|
+
set: desc.set,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
.filter((e): e is Properties[number] => !!e),
|
|
105
|
+
...[...prototypeChain(value)].flatMap((e) =>
|
|
106
|
+
Object.entries(Object.getOwnPropertyDescriptors(e)).flatMap(([k, v]) => {
|
|
107
|
+
try {
|
|
108
|
+
if (!safeGetters.has(v.get!)) return [];
|
|
109
|
+
const result = (value as unknown as Record<PropertyKey, unknown>)[k];
|
|
110
|
+
if (result instanceof Promise) {
|
|
111
|
+
result.catch(() => {});
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
key: k,
|
|
115
|
+
configurable: !!v.configurable,
|
|
116
|
+
enumerable: !!v.enumerable,
|
|
117
|
+
value: result,
|
|
118
|
+
get: undefined,
|
|
119
|
+
set: undefined,
|
|
120
|
+
};
|
|
121
|
+
} catch {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}),
|
|
125
|
+
),
|
|
126
|
+
]
|
|
127
|
+
: [],
|
|
128
|
+
specials,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function inspect(value: unknown, color: boolean): Promise<Part> {
|
|
132
|
+
const literal = color ? 'color:var(--stl-color-green-foreground)' : '';
|
|
133
|
+
const object = color ? 'color:var(--stl-color-blue-foreground)' : '';
|
|
134
|
+
const dim = color ? 'color:var(--stl-color-foreground-reduced)' : '';
|
|
135
|
+
if (Entries.isEntries(value)) {
|
|
136
|
+
return {
|
|
137
|
+
value: [
|
|
138
|
+
{
|
|
139
|
+
value: `Entries`,
|
|
140
|
+
css: object,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
value: ' { … }',
|
|
144
|
+
css: dim,
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
css: '',
|
|
148
|
+
expandHandle: Handles.create(value),
|
|
149
|
+
id: id(value),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (value === null || value === undefined) {
|
|
153
|
+
return {
|
|
154
|
+
value: value + '',
|
|
155
|
+
css: dim,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (Object.is(value, -0)) return { value: '-0', css: literal };
|
|
159
|
+
if (typeof value === 'string') {
|
|
160
|
+
return { value: JSON.stringify(value), css: literal };
|
|
161
|
+
}
|
|
162
|
+
if (typeof value === 'bigint') {
|
|
163
|
+
return { value: value + 'n', css: literal };
|
|
164
|
+
}
|
|
165
|
+
switch (typeof value) {
|
|
166
|
+
case 'number':
|
|
167
|
+
case 'boolean':
|
|
168
|
+
return { value: value + '', css: literal };
|
|
169
|
+
case 'symbol':
|
|
170
|
+
return { value: `Symbol(${value.description})`, css: literal };
|
|
171
|
+
case 'function':
|
|
172
|
+
return {
|
|
173
|
+
value: getSignature(functionToString(value)),
|
|
174
|
+
css: object,
|
|
175
|
+
expandHandle: Handles.create(value),
|
|
176
|
+
id: id(value),
|
|
177
|
+
lowPriority: true,
|
|
178
|
+
};
|
|
179
|
+
default: {
|
|
180
|
+
const className = isProxy(value) ? 'Proxy' : objectToString(value).slice('[Object '.length, -1);
|
|
181
|
+
const err =
|
|
182
|
+
(void 0,
|
|
183
|
+
// @ts-expect-error not in our lib yet
|
|
184
|
+
Error.isError
|
|
185
|
+
? // @ts-expect-error not in our lib yet
|
|
186
|
+
Error.isError(value)
|
|
187
|
+
: Object.prototype.toString.call(value) === '[object Error]');
|
|
188
|
+
return {
|
|
189
|
+
value: err
|
|
190
|
+
? [
|
|
191
|
+
{
|
|
192
|
+
value: `${className}`,
|
|
193
|
+
css: object,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
value: ': ',
|
|
197
|
+
css: dim,
|
|
198
|
+
},
|
|
199
|
+
await inspect((value as Error).message, color),
|
|
200
|
+
]
|
|
201
|
+
: [
|
|
202
|
+
{
|
|
203
|
+
value: `${className}`,
|
|
204
|
+
css: object,
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
value: Array.isArray(value) ? ' [ … ]' : ' { … }',
|
|
208
|
+
css: dim,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
css: '',
|
|
212
|
+
expandHandle: Handles.create(value),
|
|
213
|
+
id: id(value),
|
|
214
|
+
lowPriority: err,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
Object.defineProperty(globalThis, 'console', {
|
|
220
|
+
configurable: true,
|
|
221
|
+
writable: true,
|
|
222
|
+
value: createConsole(async (type, params?) => {
|
|
223
|
+
if (type === 'clear') {
|
|
224
|
+
logger!({ type });
|
|
225
|
+
} else {
|
|
226
|
+
logger!({
|
|
227
|
+
type,
|
|
228
|
+
parts: await Promise.all(
|
|
229
|
+
params.map(async (e) =>
|
|
230
|
+
typeof e === 'string'
|
|
231
|
+
? { css: '', value: e }
|
|
232
|
+
: e.type === 'string'
|
|
233
|
+
? {
|
|
234
|
+
css: e.css,
|
|
235
|
+
value: e.value + '',
|
|
236
|
+
}
|
|
237
|
+
: inspect(e.value, true),
|
|
238
|
+
),
|
|
239
|
+
),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}),
|
|
243
|
+
});
|
|
244
|
+
export type Handle = string & { readonly _handle: unique symbol };
|
|
245
|
+
const ids = new WeakMap<WeakKey, string>();
|
|
246
|
+
const idsMap = new Map<unknown, string>();
|
|
247
|
+
const id = (obj: unknown) => {
|
|
248
|
+
const cached = typeof obj === 'object' && obj !== null ? ids.get(obj) : idsMap.get(obj);
|
|
249
|
+
if (cached) return cached;
|
|
250
|
+
const id = crypto.randomUUID();
|
|
251
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
252
|
+
ids.set(obj, id);
|
|
253
|
+
} else {
|
|
254
|
+
idsMap.set(obj, id);
|
|
255
|
+
}
|
|
256
|
+
return id;
|
|
257
|
+
};
|
|
258
|
+
class Handles {
|
|
259
|
+
static #handles: Record<string, unknown> = {};
|
|
260
|
+
static #freed = Symbol();
|
|
261
|
+
static create(value: unknown): Handle {
|
|
262
|
+
const handle = crypto.randomUUID() as Handle;
|
|
263
|
+
this.#handles[handle] = value;
|
|
264
|
+
return handle;
|
|
265
|
+
}
|
|
266
|
+
static deref(handle: Handle): unknown {
|
|
267
|
+
if (!Object.hasOwn(this.#handles, handle)) {
|
|
268
|
+
throw new TypeError('handle does not exist');
|
|
269
|
+
}
|
|
270
|
+
const value = this.#handles[handle];
|
|
271
|
+
if (value === this.#freed) throw new TypeError('handle was freed');
|
|
272
|
+
return value;
|
|
273
|
+
}
|
|
274
|
+
static free(handle: Handle) {
|
|
275
|
+
this.deref(handle); // ensure it exists
|
|
276
|
+
this.#handles[handle] = this.#freed;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const functionToString = Function.prototype.call.bind(Function.prototype.toString);
|
|
280
|
+
const objectToString = Function.prototype.call.bind(Object.prototype.toString);
|
|
281
|
+
addEventListener('unhandledrejection', async (e) => {
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
logger({ type: 'error', parts: ['Uncaught (in promise) ', await inspect(e.reason, true)] });
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
let logger: Logger;
|
|
287
|
+
const api = {
|
|
288
|
+
setLogger(logger_: Logger) {
|
|
289
|
+
logger = logger_;
|
|
290
|
+
},
|
|
291
|
+
async expandLogHandle(handle: string): Promise<Part[]> {
|
|
292
|
+
let value;
|
|
293
|
+
try {
|
|
294
|
+
value = Handles.deref(handle as Handle);
|
|
295
|
+
} catch {
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
if (Entries.isEntries(value)) {
|
|
299
|
+
return Promise.all(
|
|
300
|
+
value.value.map(async (e, i) => ({
|
|
301
|
+
css: '',
|
|
302
|
+
value: await Promise.all([
|
|
303
|
+
{
|
|
304
|
+
value: i + '',
|
|
305
|
+
css: 'color:var(--stl-color-foreground)',
|
|
306
|
+
},
|
|
307
|
+
{ value: `: `, css: 'color:var(--stl-color-foreground-reduced)' },
|
|
308
|
+
...(value.pairs && Array.isArray(e)
|
|
309
|
+
? [
|
|
310
|
+
inspect(e[0], true),
|
|
311
|
+
{
|
|
312
|
+
value: ` → `,
|
|
313
|
+
css: 'color:var(--stl-color-foreground-reduced)',
|
|
314
|
+
},
|
|
315
|
+
inspect(e[1], true),
|
|
316
|
+
]
|
|
317
|
+
: [inspect(e, true)]),
|
|
318
|
+
]),
|
|
319
|
+
})),
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const props = await properties(value);
|
|
323
|
+
const formatted: Promise<Part>[] = [
|
|
324
|
+
...props.properties.map(async ({ get, set, key, value }): Promise<Part> => {
|
|
325
|
+
return {
|
|
326
|
+
value: await Promise.all([
|
|
327
|
+
typeof key === 'string' &&
|
|
328
|
+
(parseInt(key, 10) + '' === key ||
|
|
329
|
+
/(?=[$_\p{ID_Start}\\])[$_\u200C\u200D\p{ID_Continue}]+/u.test(key))
|
|
330
|
+
? {
|
|
331
|
+
value: key + '',
|
|
332
|
+
css: 'color:var(--stl-color-foreground)',
|
|
333
|
+
}
|
|
334
|
+
: {
|
|
335
|
+
...(await inspect(key, false)),
|
|
336
|
+
css: 'color:var(--stl-color-foreground)',
|
|
337
|
+
},
|
|
338
|
+
{ value: `: `, css: 'color:var(--stl-color-foreground-reduced)' },
|
|
339
|
+
typeof get === 'function'
|
|
340
|
+
? typeof set === 'function'
|
|
341
|
+
? {
|
|
342
|
+
value: '[Getter/Setter]',
|
|
343
|
+
css: 'color:var(--stl-color-foreground-reduced)',
|
|
344
|
+
}
|
|
345
|
+
: {
|
|
346
|
+
value: '[Getter]',
|
|
347
|
+
css: 'color:var(--stl-color-foreground-reduced)',
|
|
348
|
+
}
|
|
349
|
+
: typeof set === 'function'
|
|
350
|
+
? {
|
|
351
|
+
value: '[Setter]',
|
|
352
|
+
css: 'color:var(--stl-color-foreground-reduced)',
|
|
353
|
+
}
|
|
354
|
+
: inspect(value, true),
|
|
355
|
+
]),
|
|
356
|
+
css: '',
|
|
357
|
+
};
|
|
358
|
+
}),
|
|
359
|
+
...props.specials.map(async (e): Promise<Part> => {
|
|
360
|
+
return {
|
|
361
|
+
value: await Promise.all([
|
|
362
|
+
{
|
|
363
|
+
value: `<${e.name}>: `,
|
|
364
|
+
css: 'color:var(--stl-color-foreground-reduced)',
|
|
365
|
+
},
|
|
366
|
+
{ ...(await inspect(e.value, true)), lowPriority: true },
|
|
367
|
+
]),
|
|
368
|
+
css: '',
|
|
369
|
+
};
|
|
370
|
+
}),
|
|
371
|
+
];
|
|
372
|
+
return await Promise.all(formatted);
|
|
373
|
+
},
|
|
374
|
+
freeLogHandle(handle: string) {
|
|
375
|
+
try {
|
|
376
|
+
Handles.free(handle as Handle);
|
|
377
|
+
} catch {
|
|
378
|
+
// ignore error
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
setEnv(vars: Record<string, string>) {
|
|
382
|
+
Object.assign(process.env, vars);
|
|
383
|
+
},
|
|
384
|
+
async run(code: string, hideSpecial_ = false): Promise<void> {
|
|
385
|
+
try {
|
|
386
|
+
hideSpecial = hideSpecial_;
|
|
387
|
+
await import(
|
|
388
|
+
/* @vite-ignore */ 'data:text/javascript;charset=utf-8,' + encodeURIComponent(code) + '#' + Date.now()
|
|
389
|
+
);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
logger({ type: 'error', parts: ['Uncaught (in promise) ', await inspect(error, true)] });
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
export type RunnerAPI = typeof api;
|
|
396
|
+
expose(api);
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Exposes synthetic properties that are useful when logging objects.
|
|
4
|
+
* Supports
|
|
5
|
+
* - Boxed primitive values
|
|
6
|
+
* - Promise state
|
|
7
|
+
* - Map/Set/MapLike/SetLike entries
|
|
8
|
+
* - Proxy targets and handlers
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const specials: ((o: unknown, infos: { name: string; value: unknown }[]) => undefined)[] = [];
|
|
12
|
+
for (const e of [Boolean, Date, Number, String, BigInt, Symbol]) {
|
|
13
|
+
const fn = Function.prototype.call.bind(e.prototype.valueOf);
|
|
14
|
+
specials.push((v, infos) => {
|
|
15
|
+
try {
|
|
16
|
+
infos.push({ name: 'primitive value', value: fn(v) });
|
|
17
|
+
} catch {
|
|
18
|
+
// ignore error (brand check failed, try next primitive)
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const promisePrototypeThen = Function.prototype.call.bind(Promise.prototype.then);
|
|
23
|
+
const promiseResolve = Promise.resolve.bind(Promise);
|
|
24
|
+
specials.push((v, infos) => {
|
|
25
|
+
try {
|
|
26
|
+
let state: 'fulfilled' | 'rejected' | 'pending';
|
|
27
|
+
let value: unknown;
|
|
28
|
+
let reason: unknown;
|
|
29
|
+
promisePrototypeThen(
|
|
30
|
+
v,
|
|
31
|
+
(val: unknown) => {
|
|
32
|
+
if (state) return;
|
|
33
|
+
value = val;
|
|
34
|
+
state = 'fulfilled';
|
|
35
|
+
},
|
|
36
|
+
(err: unknown) => {
|
|
37
|
+
if (state) return;
|
|
38
|
+
reason = err;
|
|
39
|
+
state = 'rejected';
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
promisePrototypeThen(promiseResolve(), () => {
|
|
43
|
+
state ??= 'pending';
|
|
44
|
+
infos.push({ name: 'state', value: state });
|
|
45
|
+
if (state === 'rejected') {
|
|
46
|
+
infos.push({ name: 'reason', value: reason });
|
|
47
|
+
}
|
|
48
|
+
if (state === 'fulfilled') {
|
|
49
|
+
infos.push({ name: 'value', value: value });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
} catch {
|
|
53
|
+
// ignore error (not a promise)
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
export class Entries {
|
|
57
|
+
#brand: undefined;
|
|
58
|
+
pairs: boolean;
|
|
59
|
+
value: unknown[];
|
|
60
|
+
constructor(pairs: boolean, value: unknown[]) {
|
|
61
|
+
this.pairs = pairs;
|
|
62
|
+
this.value = value;
|
|
63
|
+
this.#brand = undefined;
|
|
64
|
+
}
|
|
65
|
+
static isEntries(obj: unknown): obj is Entries {
|
|
66
|
+
return typeof obj === 'object' && obj !== null && #brand in obj;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const name of Object.getOwnPropertyNames(globalThis)) {
|
|
70
|
+
let entries, proto, pairs;
|
|
71
|
+
try {
|
|
72
|
+
proto = Object.getOwnPropertyDescriptor(globalThis, name)!.value?.prototype;
|
|
73
|
+
entries = proto?.forEach && (proto?.keys === proto?.values ? proto?.values : proto?.entries);
|
|
74
|
+
pairs = proto?.keys !== proto?.values;
|
|
75
|
+
} catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (
|
|
79
|
+
typeof entries === 'function' &&
|
|
80
|
+
entries !== Array.prototype.entries &&
|
|
81
|
+
entries !== Uint8Array.prototype.entries
|
|
82
|
+
) {
|
|
83
|
+
try {
|
|
84
|
+
const result = entries();
|
|
85
|
+
if ('then' in result) {
|
|
86
|
+
(result as Promise<unknown>).then(
|
|
87
|
+
() => {},
|
|
88
|
+
() => {},
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
const fn = Function.prototype.call.bind(entries);
|
|
93
|
+
specials.push((v, infos) => {
|
|
94
|
+
try {
|
|
95
|
+
infos.push({
|
|
96
|
+
name: 'entries',
|
|
97
|
+
value: new Entries(pairs, [...fn(v)]),
|
|
98
|
+
});
|
|
99
|
+
} catch {
|
|
100
|
+
// ignore error (brand check failed)
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const proxyMap = new WeakMap<object, { target: object; handler: ProxyHandler<object> }>();
|
|
107
|
+
const realProxy = Proxy;
|
|
108
|
+
const realRevocable = Proxy.revocable;
|
|
109
|
+
realProxy.revocable = new realProxy(realRevocable, {
|
|
110
|
+
apply(_, { 0: target, 1: handler }) {
|
|
111
|
+
const result = realRevocable(target, handler);
|
|
112
|
+
proxyMap.set(result.proxy, { target, handler });
|
|
113
|
+
return result;
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
globalThis.Proxy = new realProxy(realProxy, {
|
|
117
|
+
construct(_, { 0: target, 1: handler }) {
|
|
118
|
+
const proxy = new realProxy(target, handler);
|
|
119
|
+
proxyMap.set(proxy, { target, handler });
|
|
120
|
+
return proxy;
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
specials.push((o, infos) => {
|
|
124
|
+
const proxyInfo = proxyMap.get(o as object);
|
|
125
|
+
if (proxyInfo) {
|
|
126
|
+
infos.push({ name: 'target', value: proxyInfo.target });
|
|
127
|
+
infos.push({ name: 'handler', value: proxyInfo.handler });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
specials.push((o, infos) => {
|
|
131
|
+
Promise.resolve().then(() => {
|
|
132
|
+
if (!isProxy(o) && o !== null && o !== undefined) {
|
|
133
|
+
infos.push({ name: 'prototype', value: Object.getPrototypeOf(o) });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
export const isProxy: (o: unknown) => boolean = proxyMap.has.bind(proxyMap) as (o: unknown) => boolean;
|
|
138
|
+
Object.freeze(specials);
|
|
139
|
+
export const specialInfo = (
|
|
140
|
+
v: unknown,
|
|
141
|
+
): Promise<
|
|
142
|
+
{
|
|
143
|
+
name: string;
|
|
144
|
+
value: unknown;
|
|
145
|
+
}[]
|
|
146
|
+
> => {
|
|
147
|
+
const infos: {
|
|
148
|
+
name: string;
|
|
149
|
+
value: unknown;
|
|
150
|
+
}[] = [];
|
|
151
|
+
for (let i = 0; i < specials.length; i++) {
|
|
152
|
+
specials[i]!(v, infos);
|
|
153
|
+
}
|
|
154
|
+
return Promise.resolve().then(() => infos);
|
|
155
|
+
};
|
|
156
|
+
/*
|
|
157
|
+
console.log(await specialInfo(Promise.resolve(1)));
|
|
158
|
+
console.log(await specialInfo(Promise.reject(1)));
|
|
159
|
+
console.log(await specialInfo(new Promise(() => {})));
|
|
160
|
+
console.log(await specialInfo(new Map([[1, 2], [3, 4]])));
|
|
161
|
+
console.log(await specialInfo(new Set([1, 2, 3, 4])));
|
|
162
|
+
console.log(await specialInfo(new FormData()));
|
|
163
|
+
console.log(await specialInfo(new Headers({ a: "b" })));
|
|
164
|
+
console.log(await specialInfo(new URLSearchParams("a=1")));
|
|
165
|
+
console.log(await specialInfo(new Boolean()));
|
|
166
|
+
console.log(await specialInfo(new Number()));
|
|
167
|
+
console.log(await specialInfo(new String()));
|
|
168
|
+
console.log(await specialInfo(Object(1n)));
|
|
169
|
+
console.log(await specialInfo(Object(Symbol.iterator)));
|
|
170
|
+
console.log(await specialInfo(new Proxy({}, {})));
|
|
171
|
+
*/
|