@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +23 -0
  3. package/eslint.config.js +2 -0
  4. package/package.json +69 -0
  5. package/src/Logs.tsx +216 -0
  6. package/src/Panel.tsx +21 -0
  7. package/src/PlaygroundPanelWrapper.tsx +5 -0
  8. package/src/build-py-types.ts +152 -0
  9. package/src/build-ts-types.ts +70 -0
  10. package/src/build.ts +97 -0
  11. package/src/codemirror/comlink.ts +698 -0
  12. package/src/codemirror/curl/curlconverter.vendor.js +7959 -0
  13. package/src/codemirror/curl.ts +108 -0
  14. package/src/codemirror/deps.ts +12 -0
  15. package/src/codemirror/fix-lsp-markdown.ts +50 -0
  16. package/src/codemirror/lsp.ts +87 -0
  17. package/src/codemirror/python/anser.ts +398 -0
  18. package/src/codemirror/python/pyodide.ts +180 -0
  19. package/src/codemirror/python.ts +160 -0
  20. package/src/codemirror/react.tsx +615 -0
  21. package/src/codemirror/sanitize-html.ts +12 -0
  22. package/src/codemirror/shiki.ts +65 -0
  23. package/src/codemirror/typescript/cdn-typescript.d.ts +1 -0
  24. package/src/codemirror/typescript/cdn-typescript.js +1 -0
  25. package/src/codemirror/typescript/console.ts +590 -0
  26. package/src/codemirror/typescript/get-signature.ts +94 -0
  27. package/src/codemirror/typescript/prettier-plugin-external-typescript.vendor.js +4968 -0
  28. package/src/codemirror/typescript/runner.ts +396 -0
  29. package/src/codemirror/typescript/special-info.ts +171 -0
  30. package/src/codemirror/typescript/worker.ts +292 -0
  31. package/src/codemirror/typescript.tsx +198 -0
  32. package/src/create.tsx +44 -0
  33. package/src/icon.tsx +21 -0
  34. package/src/index.ts +6 -0
  35. package/src/logs-context.ts +5 -0
  36. package/src/playground.css +359 -0
  37. package/src/sandbox-worker/in-frame.js +179 -0
  38. package/src/sandbox-worker/index.ts +202 -0
  39. package/src/use-storage.ts +54 -0
  40. package/src/util.ts +29 -0
  41. package/src/virtual-module.d.ts +45 -0
  42. package/src/vite-env.d.ts +1 -0
  43. package/test/get-signature.test.ts +73 -0
  44. package/test/use-storage.test.ts +60 -0
  45. package/tsconfig.json +11 -0
@@ -0,0 +1,590 @@
1
+ import { JSLogType } from '../../Logs';
2
+
3
+ export type Printer = (...args: [JSLogType, (string | Part)[]] | ['clear']) => void;
4
+ type Logger = (...args: [JSLogType, unknown[]]) => void;
5
+
6
+ const stripMe = /^(?:.*Error.*\n)?.+\n/;
7
+ type Part = {
8
+ css: string | undefined;
9
+ value: unknown;
10
+ type: 'string' | 'object' | 'table';
11
+ };
12
+
13
+ function format(args: unknown[]): (string | Part)[] {
14
+ const first = args[0];
15
+ let a = 0;
16
+ const parts: (Part | string)[] = [];
17
+
18
+ if (typeof first == 'string' && args.length > 1) {
19
+ a++;
20
+ // Index of the first not-yet-appended character. Use this so we only
21
+ // have to append to `string` when a substitution occurs / at the end.
22
+ let appendedChars = 0;
23
+ let css: string | undefined;
24
+ for (let i = 0; i < first.length - 1; i++) {
25
+ const prevCss = css;
26
+ if (first[i] == '%') {
27
+ const char = first[++i];
28
+ if (a < args.length) {
29
+ let formattedArg: Part | null = null;
30
+ switch (char) {
31
+ case 's': {
32
+ // Format as a string.
33
+ formattedArg = {
34
+ css,
35
+ type: 'string',
36
+ value: String(args[a++]),
37
+ };
38
+ break;
39
+ }
40
+ case 'd':
41
+ case 'i': {
42
+ // Format as an integer.
43
+ const value = args[a++];
44
+ if (typeof value === 'symbol') {
45
+ formattedArg = {
46
+ css,
47
+ type: 'object',
48
+ value: NaN,
49
+ };
50
+ } else {
51
+ formattedArg = {
52
+ css,
53
+ type: 'object',
54
+ value: Number.parseInt(value as string, 10),
55
+ };
56
+ }
57
+ break;
58
+ }
59
+ case 'f': {
60
+ // Format as a floating point value.
61
+ const value = args[a++];
62
+ if (typeof value === 'symbol') {
63
+ formattedArg = {
64
+ css,
65
+ type: 'object',
66
+ value: NaN,
67
+ };
68
+ } else {
69
+ formattedArg = {
70
+ css,
71
+ type: 'object',
72
+ value: Number.parseFloat(value as string),
73
+ };
74
+ }
75
+ break;
76
+ }
77
+ case 'O':
78
+ case 'o': {
79
+ // Format as an object.
80
+ formattedArg = {
81
+ css,
82
+ type: 'object',
83
+ value: args[a++],
84
+ };
85
+ break;
86
+ }
87
+ case 'c': {
88
+ css = String(args[a++]);
89
+ formattedArg = {
90
+ css: '',
91
+ type: 'string',
92
+ value: '',
93
+ };
94
+ break;
95
+ }
96
+ }
97
+
98
+ if (formattedArg !== null) {
99
+ if (appendedChars !== i - 1) {
100
+ parts.push(
101
+ {
102
+ css: prevCss,
103
+ value: first.slice(appendedChars, i - 1),
104
+ type: 'string',
105
+ },
106
+ formattedArg,
107
+ );
108
+ } else {
109
+ parts.push(formattedArg);
110
+ }
111
+ appendedChars = i + 1;
112
+ }
113
+ }
114
+ if (char == '%') {
115
+ parts.push({
116
+ css: prevCss,
117
+ value: first.slice(appendedChars, i - 1) + '%',
118
+ type: 'string',
119
+ });
120
+ appendedChars = i + 1;
121
+ }
122
+ }
123
+ }
124
+ parts.push({
125
+ css,
126
+ value: first.slice(appendedChars),
127
+ type: 'string',
128
+ });
129
+ css = undefined;
130
+ }
131
+
132
+ for (; a < args.length; a++) {
133
+ if (a > 0) {
134
+ parts.push(' ');
135
+ }
136
+ if (typeof args[a] == 'string') {
137
+ parts.push(args[a] as string);
138
+ } else {
139
+ // Use default maximum depth for null or undefined arguments.
140
+ parts.push({
141
+ css: undefined,
142
+ type: 'object',
143
+ value: args[a],
144
+ });
145
+ }
146
+ }
147
+
148
+ return parts;
149
+ }
150
+
151
+ /**
152
+ * The Console interface provides methods for logging information to the console,
153
+ * as well as other utility methods for debugging and inspecting code.
154
+ * Methods include logging, debugging, and timing functionality.
155
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/console
156
+ */
157
+ export interface Console {
158
+ /**
159
+ * Tests that an expression is true. If not, logs an error message
160
+ * @param condition The expression to test for truthiness
161
+ * @param data Additional arguments to be printed if the assertion fails
162
+ * @example
163
+ * ```ts
164
+ * console.assert(1 === 1, "This won't show");
165
+ * console.assert(1 === 2, "This will show an error");
166
+ * ```
167
+ */
168
+ assert(condition?: boolean, ...data: unknown[]): void;
169
+
170
+ /**
171
+ * Clears the console if the environment allows it
172
+ * @example
173
+ * ```ts
174
+ * console.clear();
175
+ * ```
176
+ */
177
+ clear(): void;
178
+
179
+ /**
180
+ * Maintains an internal counter for a given label, incrementing it each time the method is called
181
+ * @param label The label to count. Defaults to 'default'
182
+ * @example
183
+ * ```ts
184
+ * console.count('myCounter');
185
+ * console.count('myCounter'); // Will show: myCounter: 2
186
+ * ```
187
+ */
188
+ count(label?: string): void;
189
+
190
+ /**
191
+ * Resets the counter for a given label
192
+ * @param label The label to reset. Defaults to 'default'
193
+ * @example
194
+ * ```ts
195
+ * console.count('myCounter');
196
+ * console.countReset('myCounter'); // Resets to 0
197
+ * ```
198
+ */
199
+ countReset(label?: string): void;
200
+
201
+ /**
202
+ * Outputs a debugging message to the console
203
+ * @param data Values to be printed to the console
204
+ * @example
205
+ * ```ts
206
+ * console.debug('Debug message', { detail: 'some data' });
207
+ * ```
208
+ */
209
+ debug(...data: unknown[]): void;
210
+
211
+ /**
212
+ * Displays a list of the properties of a specified object
213
+ * @param item Object to display
214
+ * @param options Formatting options
215
+ * @example
216
+ * ```ts
217
+ * console.dir({ name: 'object', value: 42 }, { depth: 1 });
218
+ * ```
219
+ */
220
+ dir(item?: unknown, options?: unknown): void;
221
+
222
+ /**
223
+ * @ignore
224
+ */
225
+ dirxml(...data: unknown[]): void;
226
+
227
+ /**
228
+ * Outputs an error message to the console.
229
+ * This method routes the output to stderr,
230
+ * unlike other console methods that route to stdout.
231
+ * @param data Values to be printed to the console
232
+ * @example
233
+ * ```ts
234
+ * console.error('Error occurred:', new Error('Something went wrong'));
235
+ * ```
236
+ */
237
+ error(...data: unknown[]): void;
238
+
239
+ /**
240
+ * Creates a new inline group in the console, indenting subsequent console messages
241
+ * @param data Labels for the group
242
+ * @example
243
+ * ```ts
244
+ * console.group('Group 1');
245
+ * console.log('Inside group 1');
246
+ * console.groupEnd();
247
+ * ```
248
+ */
249
+ group(...data: unknown[]): void;
250
+
251
+ /**
252
+ * Creates a new inline group in the console that is initially collapsed
253
+ * @param data Labels for the group
254
+ * @example
255
+ * ```ts
256
+ * console.groupCollapsed('Details');
257
+ * console.log('Hidden until expanded');
258
+ * console.groupEnd();
259
+ * ```
260
+ */
261
+ groupCollapsed(...data: unknown[]): void;
262
+
263
+ /**
264
+ * Exits the current inline group in the console
265
+ * @example
266
+ * ```ts
267
+ * console.group('Group');
268
+ * console.log('Grouped message');
269
+ * console.groupEnd();
270
+ * ```
271
+ */
272
+ groupEnd(): void;
273
+
274
+ /**
275
+ * Outputs an informational message to the console
276
+ * @param data Values to be printed to the console
277
+ * @example
278
+ * ```ts
279
+ * console.info('Application started', { version: '1.0.0' });
280
+ * ```
281
+ */
282
+ info(...data: unknown[]): void;
283
+
284
+ /**
285
+ * Outputs a message to the console
286
+ * @param data Values to be printed to the console
287
+ * @example
288
+ * ```ts
289
+ * console.log('Hello', 'World', 123);
290
+ * ```
291
+ */
292
+ log(...data: unknown[]): void;
293
+
294
+ /**
295
+ * Displays tabular data as a table
296
+ * @param tabularData Data to be displayed in table format
297
+ * @param properties Array of property names to be displayed
298
+ * @example
299
+ * ```ts
300
+ * console.table([
301
+ * { name: 'John', age: 30 },
302
+ * { name: 'Jane', age: 25 }
303
+ * ]);
304
+ * ```
305
+ */
306
+ table(tabularData?: unknown, properties?: string[]): void;
307
+
308
+ /**
309
+ * Starts a timer you can use to track how long an operation takes
310
+ * @param label Timer label. Defaults to 'default'
311
+ * @example
312
+ * ```ts
313
+ * console.time('operation');
314
+ * // ... some code
315
+ * console.timeEnd('operation');
316
+ * ```
317
+ */
318
+ time(label?: string): void;
319
+
320
+ /**
321
+ * Stops a timer that was previously started
322
+ * @param label Timer label to stop. Defaults to 'default'
323
+ * @example
324
+ * ```ts
325
+ * console.time('operation');
326
+ * // ... some code
327
+ * console.timeEnd('operation'); // Prints: operation: 1234ms
328
+ * ```
329
+ */
330
+ timeEnd(label?: string): void;
331
+
332
+ /**
333
+ * Logs the current value of a timer that was previously started
334
+ * @param label Timer label
335
+ * @param data Additional data to log
336
+ * @example
337
+ * ```ts
338
+ * console.time('process');
339
+ * // ... some code
340
+ * console.timeLog('process', 'Checkpoint A');
341
+ * ```
342
+ */
343
+ timeLog(label?: string, ...data: unknown[]): void;
344
+
345
+ /**
346
+ * Outputs a stack trace to the console
347
+ * @param data Values to be printed to the console
348
+ * @example
349
+ * ```ts
350
+ * console.trace('Trace message');
351
+ * ```
352
+ */
353
+ trace(...data: unknown[]): void;
354
+
355
+ /**
356
+ * Outputs a warning message to the console
357
+ * @param data Values to be printed to the console
358
+ * @example
359
+ * ```ts
360
+ * console.warn('Deprecated feature used');
361
+ * ```
362
+ */
363
+ warn(...data: unknown[]): void;
364
+ }
365
+
366
+ function isSet(value: unknown) {
367
+ try {
368
+ Set.prototype.has.call(value, null);
369
+ return true;
370
+ } catch {
371
+ return false;
372
+ }
373
+ }
374
+
375
+ function isMap(value: unknown) {
376
+ try {
377
+ Map.prototype.has.call(value, null);
378
+ return true;
379
+ } catch {
380
+ return false;
381
+ }
382
+ }
383
+
384
+ export function createConsole(printer: Printer): Console {
385
+ const groupStack: unknown[][] = [];
386
+ const countMap: Record<string, number> = { __proto__: null as never };
387
+ const timerTable: Record<string, number> = { __proto__: null as never };
388
+ const logger: Logger = (...args) => {
389
+ printer(args[0], format(args[1]));
390
+ };
391
+ return Object.defineProperties(
392
+ {
393
+ __proto__: {},
394
+ assert(condition: unknown = undefined, ...data: unknown[]) {
395
+ if (condition) {
396
+ return;
397
+ }
398
+ if (!data.length) {
399
+ data[0] = 'Assertion failed';
400
+ } else if (typeof data[0] === 'string') {
401
+ data[0] = `Assertion failed: ${data[0]}`;
402
+ } else {
403
+ data.unshift('Assertion failed');
404
+ }
405
+ logger('error', data);
406
+ },
407
+ clear() {
408
+ while (groupStack.length) {
409
+ logger('groupEnd', groupStack.pop()!);
410
+ }
411
+ printer('clear');
412
+ },
413
+ debug(...data: unknown[]) {
414
+ logger('debug', data);
415
+ },
416
+ error(...data: unknown[]) {
417
+ logger('error', data);
418
+ },
419
+ info(...data: unknown[]) {
420
+ logger('info', data);
421
+ },
422
+ log(...data: unknown[]) {
423
+ logger('log', data);
424
+ },
425
+ table(...args: unknown[]) {
426
+ const data = args[0];
427
+ const properties = args[1];
428
+
429
+ if (args.length > 2 || (properties !== undefined && !Array.isArray(properties))) {
430
+ logger('log', args);
431
+ return;
432
+ }
433
+
434
+ let resultData: Record<PropertyKey, unknown>;
435
+ const isSetObject = isSet(data);
436
+ const isMapObject = isMap(data);
437
+ const valuesKey = 'Values';
438
+ const indexKey = isSetObject || isMapObject ? '(iter idx)' : '(idx)';
439
+
440
+ if (isSetObject) {
441
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
442
+ resultData = [...(data as any)] as any;
443
+ } else if (isMapObject) {
444
+ let idx = 0;
445
+ resultData = { __proto__: null };
446
+
447
+ Map.prototype.forEach.call(data, (v, k) => {
448
+ resultData[idx] = { Key: k, Values: v };
449
+ idx++;
450
+ });
451
+ } else {
452
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
453
+ resultData = data as any;
454
+ }
455
+
456
+ const keys = Object.keys(resultData);
457
+ const numRows = keys.length;
458
+
459
+ const objectValues: Record<PropertyKey, unknown[]> = properties
460
+ ? Object.fromEntries(properties.map((name) => [name, new Array(numRows).fill('')]))
461
+ : {};
462
+ const indexKeys: PropertyKey[] = [];
463
+ const values: unknown[] = [];
464
+
465
+ let hasPrimitives = false;
466
+ keys.forEach((k, idx) => {
467
+ const value = resultData[k];
468
+ const primitive = value === null || (typeof value !== 'function' && typeof value !== 'object');
469
+ if (properties === undefined && primitive) {
470
+ hasPrimitives = true;
471
+ values.push(value);
472
+ } else {
473
+ const valueObj = value || {};
474
+ const keys = properties || Object.keys(valueObj);
475
+ for (let i = 0; i < keys.length; ++i) {
476
+ const k = keys[i] as PropertyKey;
477
+ if (!primitive && Reflect.has(valueObj, k)) {
478
+ if (!Reflect.has(objectValues, k)) {
479
+ objectValues[k] = new Array(numRows).fill('');
480
+ }
481
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
482
+ objectValues[k]![idx] = (valueObj as any)[k];
483
+ }
484
+ }
485
+ values.push('');
486
+ }
487
+
488
+ indexKeys.push(k);
489
+ });
490
+
491
+ const headerKeys = Object.keys(objectValues);
492
+ const bodyValues = Object.values(objectValues);
493
+ const headerProps = properties || [...headerKeys, !isMapObject && hasPrimitives && valuesKey];
494
+ const header = [indexKey, ...headerProps].filter(Boolean);
495
+ const body = [indexKeys, ...bodyValues, values];
496
+
497
+ printer('log', [
498
+ {
499
+ css: undefined,
500
+ type: 'table',
501
+ value: [
502
+ header,
503
+ ...Array.from(Object.assign({ __proto__: null, length: numRows }), (_, row) =>
504
+ header.map((_, col) => body[col]![row]),
505
+ ),
506
+ ],
507
+ },
508
+ ]);
509
+ },
510
+ trace(...data: unknown[]) {
511
+ logger('trace', [...data, '\n' + new Error().stack!.replace(stripMe, '')]);
512
+ },
513
+ warn(...data: unknown[]) {
514
+ logger('warn', data);
515
+ },
516
+ dir(item: unknown = undefined) {
517
+ logger('dir', [item]);
518
+ },
519
+ dirxml(...data: unknown[]) {
520
+ logger('log', data);
521
+ },
522
+ count(label: string | undefined = undefined) {
523
+ if (label === undefined) label = 'default';
524
+ label += '';
525
+ countMap[label] ??= 0;
526
+ logger('log', [`${label}: ${++countMap[label]!}`]);
527
+ },
528
+ countReset(label: string | undefined = undefined) {
529
+ if (label === undefined) label = 'default';
530
+ label += '';
531
+ if (Object.hasOwn(countMap, label)) {
532
+ countMap[label] = 0;
533
+ } else {
534
+ logger('warn', [`Counter ${label} doesn't exist.`]);
535
+ }
536
+ },
537
+ group(...data: unknown[]) {
538
+ groupStack.push(data);
539
+ logger('group', data);
540
+ },
541
+ groupCollapsed(...data: unknown[]) {
542
+ groupStack.push(data);
543
+ logger('groupCollapsed', data);
544
+ },
545
+ groupEnd() {
546
+ const group = groupStack.pop();
547
+ if (group) logger('groupEnd', group);
548
+ },
549
+ time(label: string | undefined = undefined) {
550
+ if (label === undefined) label = 'default';
551
+ label += '';
552
+ if (Object.hasOwn(timerTable, label)) {
553
+ logger('warn', [`Timer "${label}" already exists.`]);
554
+ } else {
555
+ timerTable[label] = Date.now();
556
+ }
557
+ },
558
+ timeLog(label: string | undefined = undefined, ...data: unknown[]) {
559
+ if (label === undefined) label = 'default';
560
+ label += '';
561
+ if (Object.hasOwn(timerTable, label)) {
562
+ logger('log', [`${label}: ${Date.now() - timerTable[label]!}ms`, ...data]);
563
+ } else {
564
+ logger('warn', [`Timer "${label}" doesn't exist.`]);
565
+ }
566
+ },
567
+ timeEnd(label: string | undefined = undefined, ...data: unknown[]) {
568
+ if (label === undefined) label = 'default';
569
+ label += '';
570
+ if (Object.hasOwn(timerTable, label)) {
571
+ logger('log', [`${label}: ${Date.now() - timerTable[label]!}ms - timer ended`, ...data]);
572
+ delete timerTable[label];
573
+ } else {
574
+ logger('warn', [`Timer "${label}" doesn't exist.`]);
575
+ }
576
+ },
577
+ },
578
+ {
579
+ [Symbol.toStringTag]: {
580
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
581
+ // @ts-ignore ts doesn't support __proto__
582
+ __proto__: null,
583
+ value: 'console',
584
+ writable: false,
585
+ enumerable: false,
586
+ configurable: true,
587
+ },
588
+ },
589
+ );
590
+ }
@@ -0,0 +1,94 @@
1
+ import jsTokens from 'js-tokens';
2
+
3
+ export function getSignature(source: string) {
4
+ let depth = 0;
5
+ let out = '';
6
+ let state = 0;
7
+ let isClass = false;
8
+ let maybeArrow = '';
9
+ let maybeGetSet = '';
10
+ loop: for (const t of jsTokens(source)) {
11
+ if (state === 0) {
12
+ if (t.type === 'WhiteSpace' || t.type.endsWith('Comment')) continue;
13
+ if (t.type === 'IdentifierName') {
14
+ if (t.value === 'async') {
15
+ out += t.value + ' ';
16
+ continue;
17
+ }
18
+ if (t.value === 'function' || (t.value === 'class' && (isClass = true))) {
19
+ out += t.value;
20
+ state = 1;
21
+ continue;
22
+ }
23
+ }
24
+ if (t.type === 'Punctuator' && t.value === '(') {
25
+ out += 'function ';
26
+ state = 2;
27
+ } else {
28
+ state = 3;
29
+ }
30
+ }
31
+ if (state === 1) {
32
+ if (t.type === 'WhiteSpace' || t.type.endsWith('Comment')) continue;
33
+ if (t.type === 'Punctuator' && t.value === '*') {
34
+ out += t.value;
35
+ continue;
36
+ }
37
+ if (t.type === 'IdentifierName') {
38
+ out += ' ' + t.value;
39
+ state = 2;
40
+ continue;
41
+ } else {
42
+ out += ' ';
43
+ state = 2;
44
+ }
45
+ }
46
+ if (state === 3) {
47
+ if (t.type === 'WhiteSpace' || t.type.endsWith('Comment')) {
48
+ if (maybeGetSet) maybeGetSet = '';
49
+ continue;
50
+ }
51
+ if (t.type === 'Punctuator') {
52
+ if (t.value === '(') {
53
+ out += 'function ' + maybeGetSet + maybeArrow;
54
+ state = 2;
55
+ } else if (t.value === '=>') {
56
+ out += 'function (' + maybeArrow + ')';
57
+ break loop;
58
+ }
59
+ }
60
+ if (t.type === 'IdentifierName' && (t.value === 'get' || t.value === 'set')) {
61
+ maybeGetSet = t.value;
62
+ } else {
63
+ maybeArrow += t.value;
64
+ }
65
+ }
66
+ if (state === 2) {
67
+ if (t.type === 'Punctuator') {
68
+ switch (t.value) {
69
+ case '=>':
70
+ if (depth === 0) {
71
+ break loop;
72
+ }
73
+ break;
74
+ case '{':
75
+ if (depth === 0) {
76
+ break loop;
77
+ }
78
+ // falls through
79
+ case '(':
80
+ case '[':
81
+ depth++;
82
+ break;
83
+ case ')':
84
+ case ']':
85
+ case '}':
86
+ depth--;
87
+ break;
88
+ }
89
+ }
90
+ out += t.value;
91
+ }
92
+ }
93
+ return out.trim() + (isClass ? ' {}' : '');
94
+ }