@phpsandbox/sdk 0.0.66 → 0.0.67
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/README.md +5 -4
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +2 -2
- package/dist/browser/phpsandbox-sdk.esm.js +24 -21
- package/dist/browser/phpsandbox-sdk.esm.min.js +2 -2
- package/dist/browser/phpsandbox-sdk.iife.js +24 -21
- package/dist/browser/phpsandbox-sdk.iife.min.js +2 -2
- package/dist/deployments.d.ts +2 -0
- package/dist/deployments.js +6 -5
- package/dist/index.d.ts +4 -0
- package/dist/index.js +8 -6
- package/dist/runtime/rest.js +10 -10
- package/docs/api-summary.md +3 -3
- package/examples/README.md +5 -5
- package/examples/laravel-load-test.ts +12 -12
- package/examples/notebook-readonly-smoke.ts +20 -20
- package/package.json +1 -1
package/dist/deployments.d.ts
CHANGED
|
@@ -153,6 +153,8 @@ export interface DeploymentData<TProvider extends DeploymentTargetProvider = Dep
|
|
|
153
153
|
strategy: string;
|
|
154
154
|
targetProvider: TProvider;
|
|
155
155
|
options: DeploymentOptionsForProvider<TProvider>;
|
|
156
|
+
eventStreamUrl?: string | null;
|
|
157
|
+
/** @deprecated Use eventStreamUrl instead. */
|
|
156
158
|
okraUrl: string | null;
|
|
157
159
|
originUrl: string | null;
|
|
158
160
|
currentRelease?: DeploymentReleaseData | null;
|
package/dist/deployments.js
CHANGED
|
@@ -124,8 +124,9 @@ export class DeploymentRun {
|
|
|
124
124
|
if (this.started) {
|
|
125
125
|
return this;
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
const eventStreamUrl = this.initial.data.eventStreamUrl ?? this.initial.data.okraUrl;
|
|
128
|
+
if (!eventStreamUrl) {
|
|
129
|
+
throw new Error(`Deployment ${this.initial.data.id} has no eventStreamUrl for SSE streaming`);
|
|
129
130
|
}
|
|
130
131
|
this.started = true;
|
|
131
132
|
this.resultPromise = new Promise((resolve, reject) => {
|
|
@@ -133,7 +134,7 @@ export class DeploymentRun {
|
|
|
133
134
|
this.rejectResult = reject;
|
|
134
135
|
});
|
|
135
136
|
this.resultPromise.catch(() => undefined);
|
|
136
|
-
void this.consumeStream(
|
|
137
|
+
void this.consumeStream(eventStreamUrl);
|
|
137
138
|
return this;
|
|
138
139
|
}
|
|
139
140
|
subscribe() {
|
|
@@ -152,9 +153,9 @@ export class DeploymentRun {
|
|
|
152
153
|
}
|
|
153
154
|
return queue;
|
|
154
155
|
}
|
|
155
|
-
async consumeStream(
|
|
156
|
+
async consumeStream(eventStreamUrl) {
|
|
156
157
|
try {
|
|
157
|
-
const stream = await this.client.sse(
|
|
158
|
+
const stream = await this.client.sse(eventStreamUrl);
|
|
158
159
|
for await (const event of readDeployStreamEvents(stream)) {
|
|
159
160
|
this.publish(event);
|
|
160
161
|
if (event.type === 'result') {
|
package/dist/index.d.ts
CHANGED
|
@@ -307,6 +307,8 @@ export interface PHPSandboxClientOptions {
|
|
|
307
307
|
telemetry?: Set<TelemetryFeature>;
|
|
308
308
|
fetch?: typeof globalThis.fetch;
|
|
309
309
|
webSocket?: unknown;
|
|
310
|
+
runtimeUrlProvider?: (notebookId: string) => Promise<string>;
|
|
311
|
+
/** @deprecated Use runtimeUrlProvider instead. */
|
|
310
312
|
okraUrlProvider?: (notebookId: string) => Promise<string>;
|
|
311
313
|
}
|
|
312
314
|
export type RuntimeTransport = 'realtime' | 'rest';
|
|
@@ -377,6 +379,8 @@ export declare class NotebookMailApi {
|
|
|
377
379
|
}
|
|
378
380
|
export interface NotebookData {
|
|
379
381
|
id: string;
|
|
382
|
+
runtimeUrl?: string;
|
|
383
|
+
/** @deprecated Use runtimeUrl instead. */
|
|
380
384
|
okraUrl: string;
|
|
381
385
|
gitUrl: string;
|
|
382
386
|
type?: string;
|
package/dist/index.js
CHANGED
|
@@ -347,24 +347,26 @@ export class NotebookInstance {
|
|
|
347
347
|
this.initialized = false;
|
|
348
348
|
_NotebookInstance_initPromise.set(this, null);
|
|
349
349
|
this.disposables = [];
|
|
350
|
+
const runtimeUrl = data.runtimeUrl ?? data.okraUrl;
|
|
351
|
+
const runtimeUrlProvider = client.options.runtimeUrlProvider ?? client.options.okraUrlProvider;
|
|
350
352
|
this.emitter = EventManager.createInstance();
|
|
351
353
|
if (client.runtimeTransport === 'rest') {
|
|
352
354
|
this.socket = null;
|
|
353
|
-
this.restInvoker = new RestRuntimeInvoker(data, client.options.fetch ?? globalThis.fetch,
|
|
355
|
+
this.restInvoker = new RestRuntimeInvoker(data, client.options.fetch ?? globalThis.fetch, runtimeUrlProvider === undefined
|
|
354
356
|
? undefined
|
|
355
|
-
: () =>
|
|
357
|
+
: () => runtimeUrlProvider(data.id));
|
|
356
358
|
}
|
|
357
359
|
else {
|
|
358
360
|
this.restInvoker = null;
|
|
359
361
|
let initialConnection = true;
|
|
360
|
-
const connectionUrl =
|
|
361
|
-
?
|
|
362
|
+
const connectionUrl = runtimeUrlProvider === undefined
|
|
363
|
+
? runtimeUrl
|
|
362
364
|
: async () => {
|
|
363
365
|
if (initialConnection) {
|
|
364
366
|
initialConnection = false;
|
|
365
|
-
return
|
|
367
|
+
return runtimeUrl;
|
|
366
368
|
}
|
|
367
|
-
return
|
|
369
|
+
return runtimeUrlProvider(data.id);
|
|
368
370
|
};
|
|
369
371
|
this.socket = new Transport(connectionUrl, this.emitter, {
|
|
370
372
|
debug: client.options.debug,
|
package/dist/runtime/rest.js
CHANGED
|
@@ -62,20 +62,20 @@ export class RestRuntimeInvoker {
|
|
|
62
62
|
this.refresh = refresh;
|
|
63
63
|
this.refreshPromise = null;
|
|
64
64
|
this.notebookId = data.id;
|
|
65
|
-
this.configure(data.okraUrl);
|
|
65
|
+
this.configure(data.runtimeUrl ?? data.okraUrl);
|
|
66
66
|
}
|
|
67
|
-
configure(
|
|
68
|
-
const
|
|
69
|
-
const ticket =
|
|
67
|
+
configure(runtimeUrl) {
|
|
68
|
+
const actionUrl = new URL(runtimeUrl);
|
|
69
|
+
const ticket = actionUrl.searchParams.get('ticket') ?? '';
|
|
70
70
|
if (ticket === '') {
|
|
71
71
|
throw new UnsupportedRuntimeTransportError('The notebook connection does not contain a runtime ticket.');
|
|
72
72
|
}
|
|
73
73
|
this.ticket = ticket;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.actionUrl =
|
|
74
|
+
actionUrl.protocol = actionUrl.protocol === 'wss:' ? 'https:' : 'http:';
|
|
75
|
+
actionUrl.pathname = `/api/v1/notebooks/${encodeURIComponent(this.notebookId)}`;
|
|
76
|
+
actionUrl.searchParams.delete('ticket');
|
|
77
|
+
actionUrl.hash = '';
|
|
78
|
+
this.actionUrl = actionUrl;
|
|
79
79
|
}
|
|
80
80
|
ready() {
|
|
81
81
|
return this.invokeRaw('runtime.ready', {}, {});
|
|
@@ -134,7 +134,7 @@ export class RestRuntimeInvoker {
|
|
|
134
134
|
}
|
|
135
135
|
async refreshAuthorization() {
|
|
136
136
|
if (this.refreshPromise === null) {
|
|
137
|
-
this.refreshPromise = this.refresh().then((
|
|
137
|
+
this.refreshPromise = this.refresh().then((runtimeUrl) => this.configure(runtimeUrl));
|
|
138
138
|
}
|
|
139
139
|
try {
|
|
140
140
|
await this.refreshPromise;
|
package/docs/api-summary.md
CHANGED
|
@@ -13,7 +13,7 @@ const client = PHPSandbox.realtime(token, 'https://api.phpsandbox.io/v1', {
|
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
const restClient = PHPSandbox.rest(token, 'https://api.phpsandbox.io/v1', {
|
|
16
|
-
|
|
16
|
+
runtimeUrlProvider: async (notebookId) => getFreshRuntimeUrl(notebookId),
|
|
17
17
|
});
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -116,11 +116,11 @@ Connection lifecycle methods, streaming actions, and event subscriptions require
|
|
|
116
116
|
|
|
117
117
|
### Laravel (`notebook.laravel`)
|
|
118
118
|
|
|
119
|
-
- Wrapper exists in the SDK, but the current
|
|
119
|
+
- Wrapper exists in the SDK, but the current runtime action set does not expose Laravel maintenance actions.
|
|
120
120
|
|
|
121
121
|
### Auth (`notebook.auth`)
|
|
122
122
|
|
|
123
|
-
- `login(
|
|
123
|
+
- `login(runtimeUrl)` using a fresh ticket-bearing runtime URL
|
|
124
124
|
- `logout()`
|
|
125
125
|
|
|
126
126
|
### Log (`notebook.log`)
|
package/examples/README.md
CHANGED
|
@@ -96,7 +96,7 @@ Useful flags:
|
|
|
96
96
|
|
|
97
97
|
- `--no-cleanup` keeps every notebook after the run.
|
|
98
98
|
- `--keep-failed` keeps failed notebooks for inspection. By default, failures are deleted too.
|
|
99
|
-
- `--
|
|
99
|
+
- `--runtime-origin=ws://127.0.0.1:9511` preserves the SDK-issued websocket path/auth but routes runtime traffic to another host.
|
|
100
100
|
- `--include-container-restart` adds a mutable `container.start()` restart check on the fresh notebook.
|
|
101
101
|
- `--include-download` adds `file.download()` to the baseline.
|
|
102
102
|
- `--include-preview` fetches the initialized preview URL in init mode, or the route written by the scenario in full mode.
|
|
@@ -104,7 +104,7 @@ Useful flags:
|
|
|
104
104
|
- `--require-init-messages="Preparing...|Setting up...|Opening..."` asserts frontend-visible init messages were received in order.
|
|
105
105
|
- `--expect-provisioned=true` asserts the `notebook.ready()` initialization result reports provisioning work.
|
|
106
106
|
- `--expect-provisioning-action=template` asserts the initialization result action.
|
|
107
|
-
- `--reauthorization-url=wss://...?ticket=...` exercises `notebook.auth.login()` / `logout()` with a fresh ticket-bearing
|
|
107
|
+
- `--reauthorization-url=wss://...?ticket=...` exercises `notebook.auth.login()` / `logout()` with a fresh ticket-bearing runtime URL.
|
|
108
108
|
- `--mode=init` limits the scenario to notebook create, ready, and delete so you can isolate init capacity.
|
|
109
109
|
- `--init-only` is shorthand for `--mode=init`.
|
|
110
110
|
- `--stop-on-failure-level` stops after the first level that has at least one failing scenario.
|
|
@@ -131,7 +131,7 @@ Default baseline coverage:
|
|
|
131
131
|
Intentionally excluded by default:
|
|
132
132
|
|
|
133
133
|
- `beacon`, because it needs a browser iframe runtime
|
|
134
|
-
- `auth`, because it needs a fresh ticket-bearing
|
|
134
|
+
- `auth`, because it needs a fresh ticket-bearing runtime URL
|
|
135
135
|
- `laravel.maintenance.*`, because it is not part of the current okra websocket action set
|
|
136
136
|
- `lsp`, because it is useful but more brittle than the rest of the headless baseline
|
|
137
137
|
|
|
@@ -153,12 +153,12 @@ PHPSANDBOX_TOKEN=your-token npx tsx examples/notebook-readonly-smoke.ts \
|
|
|
153
153
|
--notebook-id=gypjc
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
-
To preserve the SDK-issued notebook auth/path but route the websocket to a different
|
|
156
|
+
To preserve the SDK-issued notebook auth/path but route the websocket to a different runtime host:
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
159
|
PHPSANDBOX_TOKEN=your-token npx tsx examples/notebook-readonly-smoke.ts \
|
|
160
160
|
--notebook-id=gypjc \
|
|
161
|
-
--
|
|
161
|
+
--runtime-origin=ws://127.0.0.1:9501
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
Default smoke coverage:
|
|
@@ -59,8 +59,8 @@ interface LoadTestOptions {
|
|
|
59
59
|
token: string;
|
|
60
60
|
local: boolean;
|
|
61
61
|
apiUrl: string;
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
runtimeOrigin?: string;
|
|
63
|
+
runtimeUrl?: string;
|
|
64
64
|
template: string;
|
|
65
65
|
mode: LoadTestMode;
|
|
66
66
|
stopOnFailureLevel: boolean;
|
|
@@ -478,7 +478,7 @@ async function runScenario(
|
|
|
478
478
|
|
|
479
479
|
const data = {
|
|
480
480
|
...created.data,
|
|
481
|
-
|
|
481
|
+
runtimeUrl: resolveRuntimeUrl(created.data.runtimeUrl ?? created.data.okraUrl, options),
|
|
482
482
|
};
|
|
483
483
|
created.dispose();
|
|
484
484
|
|
|
@@ -1071,8 +1071,8 @@ function renderConfiguration(options: LoadTestOptions): string {
|
|
|
1071
1071
|
` mode: ${options.mode}`,
|
|
1072
1072
|
` stop on failing level: ${options.stopOnFailureLevel}`,
|
|
1073
1073
|
` api url: ${options.apiUrl}`,
|
|
1074
|
-
options.
|
|
1075
|
-
options.
|
|
1074
|
+
options.runtimeOrigin ? ` runtime origin override: ${options.runtimeOrigin}` : undefined,
|
|
1075
|
+
options.runtimeUrl ? ` runtime url override: ${redactUrl(options.runtimeUrl)}` : undefined,
|
|
1076
1076
|
` levels: ${options.levels.join(', ')}`,
|
|
1077
1077
|
` waves per level: ${options.waves}`,
|
|
1078
1078
|
` visibility: ${options.visibility}`,
|
|
@@ -2400,8 +2400,8 @@ function parseOptions(argv: string[]): LoadTestOptions {
|
|
|
2400
2400
|
token,
|
|
2401
2401
|
local,
|
|
2402
2402
|
apiUrl,
|
|
2403
|
-
|
|
2404
|
-
|
|
2403
|
+
runtimeOrigin: stringArg(args, 'runtime-origin'),
|
|
2404
|
+
runtimeUrl: stringArg(args, 'runtime-url'),
|
|
2405
2405
|
template: stringArg(args, 'template') ?? 'laravel',
|
|
2406
2406
|
mode: resolveMode(args),
|
|
2407
2407
|
stopOnFailureLevel: booleanArg(args, 'stop-on-failure-level') ?? false,
|
|
@@ -2433,17 +2433,17 @@ function parseOptions(argv: string[]): LoadTestOptions {
|
|
|
2433
2433
|
};
|
|
2434
2434
|
}
|
|
2435
2435
|
|
|
2436
|
-
function
|
|
2437
|
-
if (options.
|
|
2438
|
-
return options.
|
|
2436
|
+
function resolveRuntimeUrl(original: string, options: LoadTestOptions): string {
|
|
2437
|
+
if (options.runtimeUrl) {
|
|
2438
|
+
return options.runtimeUrl;
|
|
2439
2439
|
}
|
|
2440
2440
|
|
|
2441
|
-
if (!options.
|
|
2441
|
+
if (!options.runtimeOrigin) {
|
|
2442
2442
|
return original;
|
|
2443
2443
|
}
|
|
2444
2444
|
|
|
2445
2445
|
const source = new URL(original);
|
|
2446
|
-
const target = new URL(options.
|
|
2446
|
+
const target = new URL(options.runtimeOrigin);
|
|
2447
2447
|
source.protocol = target.protocol;
|
|
2448
2448
|
source.host = target.host;
|
|
2449
2449
|
return source.toString();
|
|
@@ -24,8 +24,8 @@ interface SmokeOptions {
|
|
|
24
24
|
local: boolean;
|
|
25
25
|
apiUrl: string;
|
|
26
26
|
notebookId: string;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
runtimeUrl?: string;
|
|
28
|
+
runtimeOrigin?: string;
|
|
29
29
|
includeExec: boolean;
|
|
30
30
|
includeDownload: boolean;
|
|
31
31
|
includePreview: boolean;
|
|
@@ -67,8 +67,8 @@ interface SmokeArtifact {
|
|
|
67
67
|
apiUrl: string;
|
|
68
68
|
local: boolean;
|
|
69
69
|
notebookId: string;
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
runtimeUrl?: string;
|
|
71
|
+
runtimeOrigin?: string;
|
|
72
72
|
includeExec: boolean;
|
|
73
73
|
includeDownload: boolean;
|
|
74
74
|
includePreview: boolean;
|
|
@@ -77,7 +77,7 @@ interface SmokeArtifact {
|
|
|
77
77
|
id: string;
|
|
78
78
|
title?: string;
|
|
79
79
|
type?: string;
|
|
80
|
-
|
|
80
|
+
runtimeUrl: string;
|
|
81
81
|
};
|
|
82
82
|
initTrace: InitTraceArtifact;
|
|
83
83
|
steps: StepArtifact[];
|
|
@@ -103,13 +103,13 @@ async function main(): Promise<void> {
|
|
|
103
103
|
const result = await client.get<NotebookData>(`/notebook/${options.notebookId}`);
|
|
104
104
|
return {
|
|
105
105
|
data: result.data,
|
|
106
|
-
detail: `id=${result.data.id} type=${result.data.type ?? ''} title=${result.data.title ?? ''}
|
|
106
|
+
detail: `id=${result.data.id} type=${result.data.type ?? ''} title=${result.data.title ?? ''} runtimeUrl=${redactUrl(result.data.runtimeUrl ?? result.data.okraUrl)}`,
|
|
107
107
|
};
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
notebookData = {
|
|
111
111
|
...response.data,
|
|
112
|
-
|
|
112
|
+
runtimeUrl: resolveRuntimeUrl(response.data.runtimeUrl ?? response.data.okraUrl, options),
|
|
113
113
|
};
|
|
114
114
|
|
|
115
115
|
notebook = new NotebookInstance(notebookData, client);
|
|
@@ -289,8 +289,8 @@ async function main(): Promise<void> {
|
|
|
289
289
|
apiUrl: options.apiUrl,
|
|
290
290
|
local: options.local,
|
|
291
291
|
notebookId: options.notebookId,
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
runtimeUrl: options.runtimeUrl ? redactUrl(options.runtimeUrl) : undefined,
|
|
293
|
+
runtimeOrigin: options.runtimeOrigin,
|
|
294
294
|
includeExec: options.includeExec,
|
|
295
295
|
includeDownload: options.includeDownload,
|
|
296
296
|
includePreview: options.includePreview,
|
|
@@ -300,7 +300,7 @@ async function main(): Promise<void> {
|
|
|
300
300
|
id: notebookData.id,
|
|
301
301
|
title: notebookData.title,
|
|
302
302
|
type: notebookData.type,
|
|
303
|
-
|
|
303
|
+
runtimeUrl: redactUrl(notebookData.runtimeUrl ?? notebookData.okraUrl),
|
|
304
304
|
}
|
|
305
305
|
: undefined,
|
|
306
306
|
initTrace,
|
|
@@ -471,17 +471,17 @@ async function readStreamToString(stream: ReadableStream<string>, timeoutMs: num
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
function
|
|
475
|
-
if (options.
|
|
476
|
-
return options.
|
|
474
|
+
function resolveRuntimeUrl(original: string, options: SmokeOptions): string {
|
|
475
|
+
if (options.runtimeUrl) {
|
|
476
|
+
return options.runtimeUrl;
|
|
477
477
|
}
|
|
478
478
|
|
|
479
|
-
if (!options.
|
|
479
|
+
if (!options.runtimeOrigin) {
|
|
480
480
|
return original;
|
|
481
481
|
}
|
|
482
482
|
|
|
483
483
|
const source = new URL(original);
|
|
484
|
-
const target = new URL(options.
|
|
484
|
+
const target = new URL(options.runtimeOrigin);
|
|
485
485
|
source.protocol = target.protocol;
|
|
486
486
|
source.host = target.host;
|
|
487
487
|
return source.toString();
|
|
@@ -508,8 +508,8 @@ function parseOptions(argv: string[]): SmokeOptions {
|
|
|
508
508
|
process.env.PHPSANDBOX_API_URL ??
|
|
509
509
|
((booleanArg(args, 'local') ?? false) ? DEFAULT_LOCAL_API_URL : DEFAULT_API_URL),
|
|
510
510
|
notebookId: stringArg(args, 'notebook-id') ?? 'gypjc',
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
runtimeUrl: stringArg(args, 'runtime-url'),
|
|
512
|
+
runtimeOrigin: stringArg(args, 'runtime-origin'),
|
|
513
513
|
includeExec: booleanArg(args, 'include-exec') ?? false,
|
|
514
514
|
includeDownload: booleanArg(args, 'include-download') ?? false,
|
|
515
515
|
includePreview: booleanArg(args, 'include-preview') ?? false,
|
|
@@ -786,7 +786,7 @@ function printSummary(artifact: SmokeArtifact): void {
|
|
|
786
786
|
console.log('\n=== Notebook readonly smoke summary ===');
|
|
787
787
|
console.log(`Status: ${artifact.status}`);
|
|
788
788
|
console.log(`Notebook: ${artifact.options.notebookId}`);
|
|
789
|
-
console.log(`
|
|
789
|
+
console.log(`Runtime URL: ${artifact.notebook?.runtimeUrl ?? 'unresolved'}`);
|
|
790
790
|
console.log(`Duration: ${formatDuration(artifact.durationMs)}`);
|
|
791
791
|
console.log(`Steps: passed=${passed} failed=${failed} skipped=${skipped}`);
|
|
792
792
|
|
|
@@ -806,8 +806,8 @@ Options:
|
|
|
806
806
|
--local Use ${DEFAULT_LOCAL_API_URL} and default to insecure TLS.
|
|
807
807
|
--insecure-tls Disable TLS verification for local HTTPS/WSS.
|
|
808
808
|
--ca-file=<path> Trust a custom CA for local HTTPS/WSS.
|
|
809
|
-
--
|
|
810
|
-
--
|
|
809
|
+
--runtime-url=<url> Replace the notebook runtimeUrl with a full WebSocket URL.
|
|
810
|
+
--runtime-origin=<origin> Replace only the runtimeUrl origin, preserving path/query auth.
|
|
811
811
|
--include-exec Also run shell, terminal, and REPL read-safe execution checks.
|
|
812
812
|
--include-download Also run a read-only file.download() check for a known file.
|
|
813
813
|
--include-preview Fetch the initialized preview URL.
|
package/package.json
CHANGED