@phpsandbox/sdk 0.1.13 → 0.1.15

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 (40) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +7 -18
  3. package/dist/browser/phpsandbox-sdk.esm.js +25 -14
  4. package/dist/browser/phpsandbox-sdk.esm.min.js +9 -9
  5. package/dist/browser/phpsandbox-sdk.iife.js +25 -14
  6. package/dist/browser/phpsandbox-sdk.iife.min.js +9 -9
  7. package/dist/errors/index.d.ts +7 -1
  8. package/dist/errors/index.js +2 -1
  9. package/dist/index.d.ts +27 -6
  10. package/dist/index.js +16 -12
  11. package/dist/{integrations.d.ts → integrations/account.d.ts} +1 -1
  12. package/dist/integrations/index.d.ts +2 -0
  13. package/dist/integrations/index.js +2 -0
  14. package/dist/{sandbox-integrations.d.ts → integrations/sandbox.d.ts} +2 -2
  15. package/dist/{sandbox-integrations.js → integrations/sandbox.js} +1 -1
  16. package/dist/log.d.ts +1 -1
  17. package/dist/log.js +1 -1
  18. package/dist/metrics.d.ts +1 -1
  19. package/dist/metrics.js +1 -1
  20. package/dist/ports.d.ts +1 -1
  21. package/dist/ports.js +1 -1
  22. package/dist/runtime.d.ts +1 -1
  23. package/dist/runtime.js +1 -1
  24. package/package.json +5 -9
  25. package/docs/api-summary.md +0 -181
  26. package/docs/cdn-usage.md +0 -68
  27. package/docs/getting-started.md +0 -169
  28. package/examples/README.md +0 -203
  29. package/examples/basic.ts +0 -15
  30. package/examples/get.ts +0 -22
  31. package/examples/git-merge-smoke.ts +0 -395
  32. package/examples/laravel-load-test.ts +0 -2800
  33. package/examples/notebook-readonly-smoke.ts +0 -806
  34. package/examples/publication-provider-label.ts +0 -11
  35. package/examples/publish-cloudflare-container.ts +0 -65
  36. package/examples/publish-laravel-cloud.ts +0 -123
  37. package/examples/publish-ssh-agent.ts +0 -211
  38. /package/dist/{integrations.js → integrations/account.js} +0 -0
  39. /package/dist/{runtime-telemetry.d.ts → telemetry.d.ts} +0 -0
  40. /package/dist/{runtime-telemetry.js → telemetry.js} +0 -0
@@ -1,395 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import https from 'node:https';
3
- import axios from 'axios';
4
- import NodeWebSocket from 'ws';
5
- import { PHPSandbox, type GitDiffPreview, type GitMergeResult, type NotebookInstance } from '@phpsandbox/sdk';
6
-
7
- const DEFAULT_API_URL = 'https://api.phpsandbox.io/v1';
8
- const DEFAULT_LOCAL_API_URL = 'https://api.phpsandbox.test/v1';
9
-
10
- interface ParsedArgs {
11
- get(key: string): string | boolean | undefined;
12
- }
13
-
14
- interface Options {
15
- token: string;
16
- notebookId: string;
17
- apiUrl: string;
18
- ref: string;
19
- author: string;
20
- message: string;
21
- editPath: string;
22
- editContent?: string;
23
- local: boolean;
24
- apply: boolean;
25
- deleteFork: boolean;
26
- allowCheckpoint: boolean;
27
- insecureTls: boolean;
28
- caFile?: string;
29
- }
30
-
31
- async function main(): Promise<void> {
32
- const options = parseOptions(process.argv.slice(2));
33
- const transport = await createNodeTransport(options);
34
- const client = PHPSandbox.realtime(options.token, options.apiUrl, {
35
- ...transport,
36
- });
37
-
38
- let base: NotebookInstance | null = null;
39
- let fork: NotebookInstance | null = null;
40
- let shouldDeleteFork = false;
41
-
42
- try {
43
- base = await client.notebook.get(options.notebookId);
44
- console.log(`Base notebook: ${base.data.id}`);
45
-
46
- await base.ready();
47
- await ensureBaseCheckpoint(base, options);
48
-
49
- fork = await base.fork();
50
- shouldDeleteFork = options.deleteFork;
51
- const forkNotebookId = fork.data.id;
52
- console.log(`Fork notebook: ${forkNotebookId}`);
53
-
54
- await fork.ready();
55
- const editContent = buildEditContent(base.data.id, forkNotebookId, options);
56
- await fork.files.write(options.editPath, editContent);
57
- console.log(`Edited fork file: ${options.editPath}`);
58
-
59
- const forkCheckpoint = await fork.git.checkpoint(options.author, `Smoke edit in fork ${forkNotebookId}`, options.ref, false);
60
- console.log(`Fork checkpoint: ${forkCheckpoint.ref}`);
61
-
62
- console.log(`Diff source notebook: ${forkNotebookId}`);
63
-
64
- const preview = await retry(
65
- () =>
66
- base!.git.diff({
67
- notebookId: forkNotebookId,
68
- token: options.token,
69
- ref: options.ref,
70
- }),
71
- 5,
72
- 1_000
73
- );
74
- printPreview(preview);
75
-
76
- if (!options.apply) {
77
- console.log('Merge skipped. Pass --apply to mutate the base notebook.');
78
- return;
79
- }
80
-
81
- const merge = await base.git.merge({
82
- notebookId: forkNotebookId,
83
- token: options.token,
84
- ref: options.ref,
85
- expectedBaseRef: preview.baseRef,
86
- author: options.author,
87
- message: options.message,
88
- });
89
- printMerge(merge);
90
-
91
- if (merge.status === 'conflict') {
92
- shouldDeleteFork = false;
93
- }
94
- } finally {
95
- if (shouldDeleteFork && fork) {
96
- try {
97
- await fork.destroy();
98
- console.log(`Deleted fork notebook: ${fork.data.id}`);
99
- } catch (error) {
100
- console.error(`Could not delete fork notebook ${fork.data.id}: ${formatError(error)}`);
101
- }
102
- }
103
-
104
- fork?.dispose();
105
- base?.dispose();
106
- }
107
- }
108
-
109
- async function ensureBaseCheckpoint(base: NotebookInstance, options: Options): Promise<void> {
110
- const status = await base.git.status();
111
- console.log(`Base Git: initialized=${status.initialized} clean=${status.clean} branch=${status.branch || '-'} ref=${status.ref || '-'}`);
112
-
113
- if (status.initialized && status.clean) {
114
- return;
115
- }
116
-
117
- if (!options.allowCheckpoint) {
118
- const reason = status.initialized
119
- ? 'Base notebook has uncommitted changes.'
120
- : 'Base notebook Git repository is not initialized.';
121
- throw new Error(`${reason} Re-run with --allow-checkpoint to create a base checkpoint before forking.`);
122
- }
123
-
124
- const checkpoint = await base.git.checkpoint(
125
- options.author,
126
- status.initialized ? 'Base checkpoint before fork merge smoke' : 'Initialize base Git history for fork merge smoke',
127
- options.ref,
128
- !status.initialized
129
- );
130
- console.log(`Base checkpoint: ${checkpoint.ref}`);
131
- }
132
-
133
- function buildEditContent(baseId: string, forkId: string, options: Options): string {
134
- if (options.editContent !== undefined) {
135
- return options.editContent;
136
- }
137
-
138
- return [
139
- 'PHPSandbox Git merge smoke',
140
- `base=${baseId}`,
141
- `fork=${forkId}`,
142
- `createdAt=${new Date().toISOString()}`,
143
- '',
144
- ].join('\n');
145
- }
146
-
147
- function printPreview(preview: GitDiffPreview): void {
148
- console.log(`Diff: status=${preview.status} base=${preview.baseRef || '-'} target=${preview.targetRef || '-'}`);
149
- if (preview.status === 'unrelated') {
150
- console.log(preview.message ?? 'Base and source do not share Git history.');
151
- return;
152
- }
153
-
154
- console.log(`Changed files: ${preview.files.length}`);
155
- for (const file of preview.files.slice(0, 20)) {
156
- const oldPath = file.oldPath ? `${file.oldPath} -> ` : '';
157
- console.log(`- ${file.status} ${oldPath}${file.path}`);
158
- }
159
- if (preview.files.length > 20) {
160
- console.log(`- ... ${preview.files.length - 20} more`);
161
- }
162
- }
163
-
164
- function printMerge(merge: GitMergeResult): void {
165
- console.log(`Merge: status=${merge.status}`);
166
- if ('message' in merge && merge.message) {
167
- console.log(merge.message);
168
- }
169
-
170
- if (merge.status === 'conflict') {
171
- for (const conflict of merge.conflicts) {
172
- console.log(`- ${conflict.status} ${conflict.file} (${conflict.type})`);
173
- }
174
- return;
175
- }
176
-
177
- if ('ref' in merge && merge.ref) {
178
- console.log(`Result ref: ${merge.ref}`);
179
- }
180
- }
181
-
182
- function parseOptions(argv: string[]): Options {
183
- const args = parseArgs(argv);
184
-
185
- if (booleanArg(args, 'help') === true) {
186
- printHelp();
187
- process.exit(0);
188
- }
189
-
190
- const token = stringArg(args, 'token') ?? process.env.PHPSANDBOX_TOKEN;
191
- if (!token) {
192
- throw new Error('Missing PHPSANDBOX_TOKEN. Pass it via the environment or --token.');
193
- }
194
-
195
- const local = booleanArg(args, 'local') ?? false;
196
- const notebookId = stringArg(args, 'notebook-id') ?? '10ywr';
197
- const caFile = stringArg(args, 'ca-file');
198
-
199
- return {
200
- token,
201
- notebookId,
202
- apiUrl: stringArg(args, 'api-url') ?? process.env.PHPSANDBOX_API_URL ?? (local ? DEFAULT_LOCAL_API_URL : DEFAULT_API_URL),
203
- ref: stringArg(args, 'ref') ?? 'main',
204
- author: stringArg(args, 'author') ?? 'PHPSandbox <hi@phpsandbox.io>',
205
- message: stringArg(args, 'message') ?? `Merge fork into ${notebookId}`,
206
- editPath: stringArg(args, 'edit-path') ?? 'phpsandbox-git-merge-smoke.txt',
207
- editContent: stringArg(args, 'edit-content'),
208
- local,
209
- apply: booleanArg(args, 'apply') ?? false,
210
- deleteFork: booleanArg(args, 'delete-fork') ?? false,
211
- allowCheckpoint: booleanArg(args, 'allow-checkpoint') ?? false,
212
- insecureTls: booleanArg(args, 'insecure-tls') ?? (local && !caFile),
213
- caFile,
214
- };
215
- }
216
-
217
- async function createNodeTransport(options: Options): Promise<{
218
- fetch?: typeof globalThis.fetch;
219
- webSocket?: typeof NodeWebSocket;
220
- }> {
221
- if (!options.insecureTls && !options.caFile) {
222
- return {};
223
- }
224
-
225
- if (!options.apiUrl.startsWith('https://')) {
226
- return {};
227
- }
228
-
229
- const ca = options.caFile ? await fs.readFile(options.caFile, 'utf8') : undefined;
230
- const agentOptions = {
231
- ca,
232
- rejectUnauthorized: !options.insecureTls,
233
- };
234
- const httpsAgent = new https.Agent(agentOptions);
235
-
236
- class ConfiguredWebSocket extends NodeWebSocket {
237
- public constructor(address: string | URL, protocols?: string | string[]) {
238
- super(address, protocols, agentOptions);
239
- }
240
- }
241
-
242
- const fetch: typeof globalThis.fetch = async (input, init) => {
243
- const request = input instanceof Request ? input : new Request(input, init);
244
- const headers: Record<string, string> = {};
245
- request.headers.forEach((value, key) => {
246
- headers[key] = value;
247
- });
248
- const body =
249
- request.method === 'GET' || request.method === 'HEAD'
250
- ? undefined
251
- : Buffer.from(await request.arrayBuffer());
252
-
253
- const response = await axios.request<ArrayBuffer>({
254
- url: request.url,
255
- method: request.method as any,
256
- headers,
257
- data: body,
258
- responseType: 'arraybuffer',
259
- validateStatus: () => true,
260
- httpsAgent,
261
- });
262
-
263
- const responseHeaders = new Headers();
264
- for (const [key, value] of Object.entries(response.headers)) {
265
- if (Array.isArray(value)) {
266
- for (const item of value) {
267
- responseHeaders.append(key, String(item));
268
- }
269
- } else if (value !== undefined) {
270
- responseHeaders.set(key, String(value));
271
- }
272
- }
273
-
274
- return new Response(response.data, {
275
- status: response.status,
276
- statusText: response.statusText,
277
- headers: responseHeaders,
278
- });
279
- };
280
-
281
- return { fetch, webSocket: ConfiguredWebSocket as typeof NodeWebSocket };
282
- }
283
-
284
- function parseArgs(argv: string[]): ParsedArgs {
285
- const values = new Map<string, string | boolean>();
286
-
287
- for (let index = 0; index < argv.length; index += 1) {
288
- const current = argv[index];
289
- if (!current.startsWith('--')) {
290
- continue;
291
- }
292
-
293
- if (current.startsWith('--no-')) {
294
- values.set(current.slice(5), false);
295
- continue;
296
- }
297
-
298
- const assignment = current.match(/^--([^=]+)=(.*)$/);
299
- if (assignment) {
300
- values.set(assignment[1], assignment[2]);
301
- continue;
302
- }
303
-
304
- const key = current.slice(2);
305
- const next = argv[index + 1];
306
- if (next && !next.startsWith('--')) {
307
- values.set(key, next);
308
- index += 1;
309
- continue;
310
- }
311
-
312
- values.set(key, true);
313
- }
314
-
315
- return {
316
- get(key: string) {
317
- return values.get(key);
318
- },
319
- };
320
- }
321
-
322
- function stringArg(args: ParsedArgs, key: string): string | undefined {
323
- const value = args.get(key);
324
- return typeof value === 'string' && value !== '' ? value : undefined;
325
- }
326
-
327
- function booleanArg(args: ParsedArgs, key: string): boolean | undefined {
328
- const value = args.get(key);
329
- if (typeof value === 'boolean') {
330
- return value;
331
- }
332
- if (value === undefined) {
333
- return undefined;
334
- }
335
- if (value === 'true' || value === '1') {
336
- return true;
337
- }
338
- if (value === 'false' || value === '0') {
339
- return false;
340
- }
341
- throw new Error(`Invalid --${key}: ${value}`);
342
- }
343
-
344
- async function retry<T>(callback: () => Promise<T>, attempts: number, delayMs: number): Promise<T> {
345
- let lastError: unknown;
346
-
347
- for (let attempt = 1; attempt <= attempts; attempt += 1) {
348
- try {
349
- return await callback();
350
- } catch (error) {
351
- lastError = error;
352
- if (attempt === attempts) {
353
- break;
354
- }
355
-
356
- console.log(`Retrying after transient Git source lookup failure (${attempt}/${attempts}): ${formatError(error)}`);
357
- await sleep(delayMs);
358
- }
359
- }
360
-
361
- throw lastError;
362
- }
363
-
364
- function sleep(delayMs: number): Promise<void> {
365
- return new Promise((resolve) => setTimeout(resolve, delayMs));
366
- }
367
-
368
- function formatError(error: unknown): string {
369
- return error instanceof Error ? error.message : String(error);
370
- }
371
-
372
- function printHelp(): void {
373
- console.log(`
374
- Usage:
375
- PHPSANDBOX_TOKEN=... npx tsx examples/git-merge-smoke.ts --local --notebook-id=10ywr
376
-
377
- Options:
378
- --notebook-id=<id> Base notebook to open. Defaults to 10ywr.
379
- --ref=<ref> Source Git ref. Defaults to main.
380
- --edit-path=<path> File to write in the fork. Defaults to phpsandbox-git-merge-smoke.txt.
381
- --edit-content=<text> Exact content to write into edit-path.
382
- --apply Apply merge into the base notebook after diffing.
383
- --delete-fork Delete the created fork after the smoke run.
384
- --allow-checkpoint Create a base checkpoint before forking if needed.
385
- --local Use the local API host.
386
- --api-url=<url> API URL. Defaults to ${DEFAULT_API_URL}, or ${DEFAULT_LOCAL_API_URL} with --local.
387
- --insecure-tls Disable TLS verification for local HTTPS/WSS.
388
- --ca-file=<path> Trust a custom CA for local HTTPS/WSS.
389
- `);
390
- }
391
-
392
- main().catch((error) => {
393
- console.error(error instanceof Error ? error.message : error);
394
- process.exitCode = 1;
395
- });