@phpsandbox/sdk 0.1.6 → 0.1.10

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 (59) hide show
  1. package/README.md +42 -50
  2. package/dist/beacon/index.d.ts +1 -7
  3. package/dist/beacon/index.js +14 -13
  4. package/dist/beacon/navigator.d.ts +8 -36
  5. package/dist/beacon/navigator.js +1 -62
  6. package/dist/browser/phpsandbox-sdk.esm.js +547 -691
  7. package/dist/browser/phpsandbox-sdk.esm.min.js +23 -23
  8. package/dist/browser/phpsandbox-sdk.iife.js +547 -691
  9. package/dist/browser/phpsandbox-sdk.iife.min.js +35 -35
  10. package/dist/composer.d.ts +10 -8
  11. package/dist/composer.js +18 -18
  12. package/dist/errors/index.d.ts +43 -8
  13. package/dist/errors/index.js +120 -14
  14. package/dist/feedback.js +1 -2
  15. package/dist/filesystem.d.ts +0 -17
  16. package/dist/filesystem.js +2 -21
  17. package/dist/git.d.ts +1 -5
  18. package/dist/git.js +6 -8
  19. package/dist/index.d.ts +66 -156
  20. package/dist/index.js +172 -289
  21. package/dist/integrations.d.ts +0 -2
  22. package/dist/integrations.js +5 -11
  23. package/dist/log.d.ts +0 -2
  24. package/dist/lsp.js +1 -1
  25. package/dist/metrics.d.ts +3 -3
  26. package/dist/ports.d.ts +1 -1
  27. package/dist/publications.d.ts +4 -54
  28. package/dist/publications.js +25 -103
  29. package/dist/runtime/rest.js +34 -17
  30. package/dist/runtime.d.ts +68 -0
  31. package/dist/runtime.js +46 -0
  32. package/dist/sandbox-integrations.d.ts +1 -2
  33. package/dist/sandbox-integrations.js +11 -14
  34. package/dist/schemas/publications.d.ts +0 -6
  35. package/dist/schemas/publications.js +0 -1
  36. package/dist/servers.d.ts +4 -2
  37. package/dist/servers.js +5 -8
  38. package/dist/shell.d.ts +2 -14
  39. package/dist/shell.js +1 -21
  40. package/dist/socket/index.d.ts +6 -7
  41. package/dist/socket/index.js +39 -25
  42. package/dist/types.d.ts +0 -12
  43. package/dist/types.js +1 -15
  44. package/docs/api-summary.md +28 -28
  45. package/docs/cdn-usage.md +43 -55
  46. package/docs/getting-started.md +43 -26
  47. package/examples/README.md +5 -35
  48. package/examples/basic.ts +9 -18
  49. package/examples/get.ts +2 -2
  50. package/examples/git-merge-smoke.ts +2 -3
  51. package/examples/laravel-load-test.ts +31 -36
  52. package/examples/notebook-readonly-smoke.ts +12 -26
  53. package/examples/publication-provider-label.ts +1 -1
  54. package/examples/publish-cloudflare-container.ts +2 -2
  55. package/examples/publish-laravel-cloud.ts +2 -2
  56. package/examples/publish-ssh-agent.ts +2 -2
  57. package/package.json +4 -3
  58. package/dist/container.d.ts +0 -65
  59. package/dist/container.js +0 -30
@@ -27,8 +27,6 @@ export declare class IntegrationApi {
27
27
  link(input: LinkIntegrationInput): Promise<Integration>;
28
28
  list(): Promise<Integration[]>;
29
29
  get(integration: IntegrationReference): Promise<Integration>;
30
- update(integration: IntegrationReference, input: UpdateIntegrationInput): Promise<Integration>;
31
- unlink(integration: IntegrationReference): Promise<void>;
32
30
  }
33
31
  export declare class Integration {
34
32
  readonly data: IntegrationData;
@@ -14,13 +14,6 @@ export class IntegrationApi {
14
14
  const response = await this.client.get(`/integrations/${encodeURIComponent(integrationId(integration))}`);
15
15
  return new Integration(response.data, this.client);
16
16
  }
17
- async update(integration, input) {
18
- const response = await this.client.patch(`/integrations/${encodeURIComponent(integrationId(integration))}`, input);
19
- return new Integration(response.data, this.client);
20
- }
21
- async unlink(integration) {
22
- await this.client.delete(`/integrations/${encodeURIComponent(integrationId(integration))}`);
23
- }
24
17
  }
25
18
  export class Integration {
26
19
  constructor(data, client) {
@@ -36,11 +29,12 @@ export class Integration {
36
29
  get label() {
37
30
  return this.data.label;
38
31
  }
39
- update(input) {
40
- return new IntegrationApi(this.client).update(this, input);
32
+ async update(input) {
33
+ const response = await this.client.patch(`/integrations/${encodeURIComponent(this.id)}`, input);
34
+ return new Integration(response.data, this.client);
41
35
  }
42
- unlink() {
43
- return new IntegrationApi(this.client).unlink(this);
36
+ async unlink() {
37
+ await this.client.delete(`/integrations/${encodeURIComponent(this.id)}`);
44
38
  }
45
39
  }
46
40
  export function integrationId(integration) {
package/dist/log.d.ts CHANGED
@@ -3,8 +3,6 @@ import { RuntimeTelemetry, type RuntimeWatchOptions } from './runtime-telemetry.
3
3
  export interface LogEvents {
4
4
  'notebook.log': string;
5
5
  }
6
- export interface LogActions {
7
- }
8
6
  export interface RuntimeLogEntry {
9
7
  timestamp: string;
10
8
  source: 'runtime';
package/dist/lsp.js CHANGED
@@ -30,7 +30,7 @@ export default class Lsp {
30
30
  return LspConnection.create(id, this);
31
31
  }
32
32
  whenSocketConnected() {
33
- return this.okra.whenConnected();
33
+ return this.okra.connected();
34
34
  }
35
35
  }
36
36
  export class LspConnection {
package/dist/metrics.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { NotebookInstance } from './index.js';
2
- import type { ContainerStats } from './container.js';
2
+ import type { RuntimeStats } from './runtime.js';
3
3
  import { RuntimeTelemetry, type RuntimeWatchOptions } from './runtime-telemetry.js';
4
4
  export default class Metrics {
5
5
  private readonly okra;
6
6
  private readonly telemetry;
7
7
  constructor(okra: NotebookInstance, telemetry: RuntimeTelemetry);
8
- current(): Promise<ContainerStats | null>;
9
- watch(options?: RuntimeWatchOptions): ReadableStream<ContainerStats>;
8
+ current(): Promise<RuntimeStats | null>;
9
+ watch(options?: RuntimeWatchOptions): ReadableStream<RuntimeStats>;
10
10
  }
package/dist/ports.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { NotebookInstance } from './index.js';
2
- import type { PortInfo } from './container.js';
2
+ import type { PortInfo } from './runtime.js';
3
3
  import { RuntimeTelemetry, type RuntimeWatchOptions } from './runtime-telemetry.js';
4
4
  export default class Ports {
5
5
  private readonly okra;
@@ -1,6 +1,6 @@
1
- import { type Client } from './index.js';
2
- import type { PublicationBuildStatus, PublicationDomainDnsRecordType, PublicationEventData, PublicationJurisdiction, PublicationLogChunkData, PublicationProtectionMode, PublicationRegion, PublicationReleaseStatus, PublicationSize, PublicationStatus, PublishStreamEvent, PublishStreamResult } from './schemas/publications.js';
3
- export * from './schemas/publications.js';
1
+ import type { Client } from './index.js';
2
+ import type { PublicationBuildStatus, PublicationEventData, PublicationJurisdiction, PublicationLogChunkData, PublicationProtectionMode, PublicationRegion, PublicationReleaseStatus, PublicationSize, PublicationStatus, PublishStreamEvent, PublishStreamResult } from './schemas/publications.js';
3
+ export type { BuiltInPublicationProviderName, PublicationBuildStatus, PublicationEventData, PublicationJurisdiction, PublicationLogChunkData, PublicationLogStream, PublicationProtectionMode, PublicationRegion, PublicationReleaseStatus, PublicationSize, PublicationStatus, PublishStreamEvent, PublishStreamLog, PublishStreamPhase, PublishStreamResult, PublishStreamSseEvent, } from './schemas/publications.js';
4
4
  export interface PublicationPlacement {
5
5
  regions?: PublicationRegion[];
6
6
  jurisdiction?: PublicationJurisdiction;
@@ -66,7 +66,6 @@ export interface PublishInput<TName extends PublicationProviderName = Publicatio
66
66
  protection?: PublicationProtectionInput;
67
67
  metadata?: Record<string, unknown>;
68
68
  }
69
- export type PublicationRunResult = PublishStreamResult;
70
69
  export interface PublicationBuildData<TProvider extends PublicationProviderName = PublicationProviderName> {
71
70
  id: string;
72
71
  status: PublicationBuildStatus;
@@ -81,32 +80,6 @@ export interface PublicationReleaseData {
81
80
  status: PublicationReleaseStatus;
82
81
  manifest: Record<string, unknown>;
83
82
  }
84
- export interface PublicationDomainDnsData {
85
- type: PublicationDomainDnsRecordType;
86
- name: string;
87
- value: string;
88
- }
89
- export interface PublicationDomainData {
90
- id: string;
91
- publicationId: string | null;
92
- hostname: string;
93
- status: string;
94
- provider: string;
95
- providerHostnameId: string | null;
96
- dns: PublicationDomainDnsData;
97
- ssl: {
98
- status: string | null;
99
- };
100
- validationErrors: unknown[];
101
- validationRecords: unknown[];
102
- metadata: Record<string, unknown>;
103
- verifiedAt: string | null;
104
- createdAt: string | null;
105
- updatedAt: string | null;
106
- }
107
- export interface CreatePublicationDomainInput {
108
- hostname: string;
109
- }
110
83
  export interface PublicationData<TProvider extends PublicationProviderName = PublicationProviderName> {
111
84
  id: string;
112
85
  slug: string;
@@ -118,7 +91,6 @@ export interface PublicationData<TProvider extends PublicationProviderName = Pub
118
91
  eventStreamUrl: string | null;
119
92
  originUrl: string | null;
120
93
  currentRelease?: PublicationReleaseData | null;
121
- domains?: PublicationDomainData[];
122
94
  latestBuild?: PublicationBuildData<TProvider> | null;
123
95
  deployedAt: string | null;
124
96
  createdAt: string | null;
@@ -134,28 +106,10 @@ export interface PublicationProtectionSessionData {
134
106
  expiresAt: string;
135
107
  url: string;
136
108
  }
137
- export declare class NotebookPublicationApi {
138
- private readonly client;
139
- constructor(client: Client);
140
- current<TProvider extends PublicationProviderName = PublicationProviderName>(notebookId: string): Promise<PublicationInstance<TProvider> | null>;
141
- publish<TProvider extends PublicationProviderName = PublicationProviderName>(notebookId: string, input?: PublishInput): Promise<PublicationRun<TProvider>>;
142
- }
143
109
  export declare class PublicationApi {
144
110
  private readonly client;
145
111
  constructor(client: Client);
146
112
  get<TProvider extends PublicationProviderName = PublicationProviderName>(id: string): Promise<PublicationInstance<TProvider>>;
147
- destroy(id: string): Promise<void>;
148
- setProtection<TProvider extends PublicationProviderName = PublicationProviderName>(id: string, input: UpdatePublicationProtectionInput): Promise<PublicationInstance<TProvider>>;
149
- protection(id: string): Promise<PublicationProtectionData>;
150
- createProtectionSession(id: string): Promise<PublicationProtectionSessionData>;
151
- disableProtection<TProvider extends PublicationProviderName = PublicationProviderName>(id: string): Promise<PublicationInstance<TProvider>>;
152
- listDomains(id: string): Promise<PublicationDomainData[]>;
153
- createDomain(id: string, input: CreatePublicationDomainInput): Promise<PublicationDomainData>;
154
- refreshDomain(id: string, domainId: string): Promise<PublicationDomainData>;
155
- deleteDomain(id: string, domainId: string): Promise<void>;
156
- events(id: string): Promise<ReadableStream<PublicationEventData>>;
157
- buildLogs(id: string, buildId: string): Promise<ReadableStream<PublicationLogChunkData>>;
158
- logs(id: string): Promise<ReadableStream<PublicationLogChunkData>>;
159
113
  }
160
114
  export declare class PublicationRun<TProvider extends PublicationProviderName = PublicationProviderName> {
161
115
  readonly initial: PublicationInstance<TProvider>;
@@ -171,7 +125,7 @@ export declare class PublicationRun<TProvider extends PublicationProviderName =
171
125
  private readonly subscribers;
172
126
  constructor(initial: PublicationInstance<TProvider>, client: Client);
173
127
  events(): AsyncIterable<PublishStreamEvent>;
174
- result(): Promise<PublicationRunResult>;
128
+ result(): Promise<PublishStreamResult>;
175
129
  publication(): Promise<PublicationInstance<TProvider>>;
176
130
  start(): this;
177
131
  private subscribe;
@@ -199,8 +153,4 @@ export declare class PublicationInstance<TProvider extends PublicationProviderNa
199
153
  protection(): Promise<PublicationProtectionData>;
200
154
  createProtectionSession(): Promise<PublicationProtectionSessionData>;
201
155
  disableProtection(): Promise<PublicationInstance<TProvider>>;
202
- domains(): Promise<PublicationDomainData[]>;
203
- addDomain(input: CreatePublicationDomainInput): Promise<PublicationDomainData>;
204
- refreshDomain(domainId: string): Promise<PublicationDomainData>;
205
- deleteDomain(domainId: string): Promise<void>;
206
156
  }
@@ -1,29 +1,4 @@
1
- import { ApiError } from './index.js';
2
1
  import { PublicationEventDataSchema, PublicationLogChunkDataSchema, PublishStreamSseEventNameSchema, PublishStreamSseEventSchema, } from './schemas/publications.js';
3
- export * from './schemas/publications.js';
4
- export class NotebookPublicationApi {
5
- constructor(client) {
6
- this.client = client;
7
- }
8
- async current(notebookId) {
9
- try {
10
- const response = await this.client.get(`/notebook/${notebookId}/publication`);
11
- return new PublicationInstance(response.data, this.client, notebookId);
12
- }
13
- catch (error) {
14
- if (error instanceof ApiError && error.status === 404) {
15
- return null;
16
- }
17
- throw error;
18
- }
19
- }
20
- async publish(notebookId, input) {
21
- const response = await this.client.post(`/notebook/${notebookId}/publication`, input);
22
- const run = new PublicationRun(new PublicationInstance(response.data, this.client, notebookId), this.client);
23
- run.start();
24
- return run;
25
- }
26
- }
27
2
  export class PublicationApi {
28
3
  constructor(client) {
29
4
  this.client = client;
@@ -32,49 +7,6 @@ export class PublicationApi {
32
7
  const response = await this.client.get(`/publications/${id}`);
33
8
  return new PublicationInstance(response.data, this.client);
34
9
  }
35
- async destroy(id) {
36
- await this.client.delete(`/publications/${id}`);
37
- }
38
- async setProtection(id, input) {
39
- const response = await this.client.put(`/publications/${id}/protection`, input);
40
- return new PublicationInstance(response.data, this.client);
41
- }
42
- async protection(id) {
43
- const response = await this.client.get(`/publications/${id}/protection`);
44
- return response.data;
45
- }
46
- async createProtectionSession(id) {
47
- const response = await this.client.post(`/publications/${id}/protection/session`);
48
- return response.data;
49
- }
50
- async disableProtection(id) {
51
- const response = await this.client.delete(`/publications/${id}/protection`);
52
- return new PublicationInstance(response.data, this.client);
53
- }
54
- async listDomains(id) {
55
- const response = await this.client.get(`/publications/${id}/domains`);
56
- return unwrapPublicationDomainCollection(response.data);
57
- }
58
- async createDomain(id, input) {
59
- const response = await this.client.post(`/publications/${id}/domains`, input);
60
- return response.data;
61
- }
62
- async refreshDomain(id, domainId) {
63
- const response = await this.client.post(`/publications/${id}/domains/${domainId}/refresh`);
64
- return response.data;
65
- }
66
- async deleteDomain(id, domainId) {
67
- await this.client.delete(`/publications/${id}/domains/${domainId}`);
68
- }
69
- async events(id) {
70
- return parseNdjsonStream(await this.client.stream(`/publications/${id}/events/stream`), PublicationEventDataSchema);
71
- }
72
- async buildLogs(id, buildId) {
73
- return parseNdjsonStream(await this.client.stream(`/publications/${id}/builds/${buildId}/logs/stream`), PublicationLogChunkDataSchema);
74
- }
75
- async logs(id) {
76
- return parseNdjsonStream(await this.client.stream(`/publications/${id}/logs/stream`), PublicationLogChunkDataSchema);
77
- }
78
10
  }
79
11
  export class PublicationRun {
80
12
  constructor(initial, client) {
@@ -197,14 +129,17 @@ export class PublicationInstance {
197
129
  const response = await this.client.get(`/publications/${this.data.id}`);
198
130
  return new PublicationInstance(response.data, this.client, this.notebookId);
199
131
  }
200
- publish() {
132
+ async publish() {
201
133
  if (!this.notebookId) {
202
134
  return Promise.reject(new Error('Publishing requires a notebook-scoped publication instance.'));
203
135
  }
204
- return this.client.notebook.publication.publish(this.notebookId);
136
+ const response = await this.client.post(`/notebook/${this.notebookId}/publication`);
137
+ const run = new PublicationRun(new PublicationInstance(response.data, this.client, this.notebookId), this.client);
138
+ run.start();
139
+ return run;
205
140
  }
206
- destroy() {
207
- return this.client.publications.destroy(this.data.id);
141
+ async destroy() {
142
+ await this.client.delete(`/publications/${this.data.id}`);
208
143
  }
209
144
  async wait(options = {}) {
210
145
  const intervalMs = options.intervalMs ?? 1000;
@@ -220,41 +155,34 @@ export class PublicationInstance {
220
155
  }
221
156
  return current;
222
157
  }
223
- events() {
224
- return this.client.publications.events(this.data.id);
158
+ async events() {
159
+ return parseNdjsonStream(await this.client.stream(`/publications/${this.data.id}/events/stream`), PublicationEventDataSchema);
225
160
  }
226
161
  buildLogs(buildId = this.data.latestBuild?.id ?? '') {
227
162
  if (buildId === '') {
228
163
  return Promise.reject(new Error('Publication has no latest build id'));
229
164
  }
230
- return this.client.publications.buildLogs(this.data.id, buildId);
165
+ return this.client.stream(`/publications/${this.data.id}/builds/${buildId}/logs/stream`)
166
+ .then((stream) => parseNdjsonStream(stream, PublicationLogChunkDataSchema));
231
167
  }
232
- logs() {
233
- return this.client.publications.logs(this.data.id);
168
+ async logs() {
169
+ return parseNdjsonStream(await this.client.stream(`/publications/${this.data.id}/logs/stream`), PublicationLogChunkDataSchema);
234
170
  }
235
- setProtection(input) {
236
- return this.client.publications.setProtection(this.data.id, input);
237
- }
238
- protection() {
239
- return this.client.publications.protection(this.data.id);
240
- }
241
- createProtectionSession() {
242
- return this.client.publications.createProtectionSession(this.data.id);
243
- }
244
- disableProtection() {
245
- return this.client.publications.disableProtection(this.data.id);
246
- }
247
- domains() {
248
- return this.client.publications.listDomains(this.data.id);
171
+ async setProtection(input) {
172
+ const response = await this.client.put(`/publications/${this.data.id}/protection`, input);
173
+ return new PublicationInstance(response.data, this.client, this.notebookId);
249
174
  }
250
- addDomain(input) {
251
- return this.client.publications.createDomain(this.data.id, input);
175
+ async protection() {
176
+ const response = await this.client.get(`/publications/${this.data.id}/protection`);
177
+ return response.data;
252
178
  }
253
- refreshDomain(domainId) {
254
- return this.client.publications.refreshDomain(this.data.id, domainId);
179
+ async createProtectionSession() {
180
+ const response = await this.client.post(`/publications/${this.data.id}/protection/session`);
181
+ return response.data;
255
182
  }
256
- deleteDomain(domainId) {
257
- return this.client.publications.deleteDomain(this.data.id, domainId);
183
+ async disableProtection() {
184
+ const response = await this.client.delete(`/publications/${this.data.id}/protection`);
185
+ return new PublicationInstance(response.data, this.client, this.notebookId);
258
186
  }
259
187
  }
260
188
  function publicationStatusIsTerminal(status) {
@@ -263,12 +191,6 @@ function publicationStatusIsTerminal(status) {
263
191
  function delay(ms) {
264
192
  return new Promise((resolve) => setTimeout(resolve, ms));
265
193
  }
266
- function unwrapPublicationDomainCollection(input) {
267
- if (Array.isArray(input)) {
268
- return input;
269
- }
270
- return Array.isArray(input.data) ? input.data : [];
271
- }
272
194
  class AsyncPublishEventQueue {
273
195
  constructor() {
274
196
  this.values = [];
@@ -1,13 +1,9 @@
1
- import { ErrorEvent } from '../types.js';
2
- import { UnsupportedRuntimeTransportError } from '../errors/index.js';
1
+ import { PHPSandboxError, RemoteError, TransportError, remoteError } from '../errors/index.js';
3
2
  const restActionRoutes = {
4
3
  'runtime.ready': { method: 'GET', path: '/runtime' },
5
- ping: { method: 'GET', path: '/runtime/ping' },
6
4
  'config.get': { method: 'GET', path: '/runtime/config' },
7
5
  'config.update': { method: 'PATCH', path: '/runtime/config' },
8
6
  'config.set-ports': { method: 'PUT', path: '/runtime/config/ports' },
9
- 'container.state': { method: 'GET', path: '/runtime/container' },
10
- 'container.start': { method: 'POST', path: '/runtime/container/start' },
11
7
  'container.restart': { method: 'POST', path: '/runtime/container/restart' },
12
8
  'container.stop': { method: 'POST', path: '/runtime/container/stop' },
13
9
  'container.set-php': { method: 'PUT', path: '/runtime/container/php' },
@@ -60,10 +56,15 @@ export class RestRuntimeInvoker {
60
56
  const actionUrl = new URL(runtimeUrl);
61
57
  const ticket = actionUrl.searchParams.get('ticket') ?? '';
62
58
  if (ticket === '') {
63
- throw new UnsupportedRuntimeTransportError('The notebook connection does not contain a runtime ticket.');
59
+ throw new PHPSandboxError('The notebook connection does not contain a runtime ticket.');
64
60
  }
65
61
  this.ticket = ticket;
66
- actionUrl.protocol = actionUrl.protocol === 'wss:' ? 'https:' : 'http:';
62
+ if (actionUrl.protocol === 'wss:') {
63
+ actionUrl.protocol = 'https:';
64
+ }
65
+ else if (actionUrl.protocol === 'ws:') {
66
+ actionUrl.protocol = 'http:';
67
+ }
67
68
  actionUrl.pathname = `/api/v1/notebooks/${encodeURIComponent(this.notebookId)}`;
68
69
  actionUrl.searchParams.delete('ticket');
69
70
  actionUrl.hash = '';
@@ -79,27 +80,28 @@ export class RestRuntimeInvoker {
79
80
  return this.invokeRaw(action, data ?? {}, options);
80
81
  }
81
82
  send() {
82
- throw new UnsupportedRuntimeTransportError('Fire-and-forget actions require the realtime transport.');
83
+ throw new PHPSandboxError('Fire-and-forget actions require the realtime transport.');
83
84
  }
84
85
  async invokeRaw(action, data, options) {
85
86
  const abort = runtimeAbort(options);
86
87
  try {
87
88
  for (let attempt = 0; attempt < 2; attempt += 1) {
88
- const response = await this.fetch(this.request(action, data, abort.signal));
89
+ const response = await runtimeFetch(this.fetch, this.request(action, data, abort.signal));
89
90
  if (response.status === 401 && attempt === 0 && this.refresh !== undefined) {
90
91
  await this.refreshAuthorization();
91
92
  continue;
92
93
  }
93
94
  const payload = await runtimeActionResponse(response);
94
95
  if (!response.ok || payload.error !== undefined) {
95
- const error = payload.error ?? payload;
96
- const message = typeof error.message === 'string' ? error.message : `Runtime action ${action} failed.`;
97
- const code = typeof error.code === 'number' ? error.code : response.status;
98
- throw new ErrorEvent(code, message, error);
96
+ throw remoteError('runtime', payload.error, response);
99
97
  }
100
98
  return payload.data;
101
99
  }
102
- throw new ErrorEvent(401, `Runtime action ${action} could not be authorized.`, {});
100
+ throw new RemoteError(`Runtime action ${action} could not be authorized.`, {
101
+ source: 'runtime',
102
+ status: 401,
103
+ code: 'AuthenticationRequired',
104
+ });
103
105
  }
104
106
  finally {
105
107
  abort.dispose();
@@ -108,7 +110,7 @@ export class RestRuntimeInvoker {
108
110
  request(action, data, signal) {
109
111
  const route = restActionRoutes[action];
110
112
  if (route === undefined) {
111
- throw new UnsupportedRuntimeTransportError(`Runtime action ${action} requires the realtime transport.`);
113
+ throw new PHPSandboxError(`Runtime action ${action} requires the realtime transport.`);
112
114
  }
113
115
  const url = new URL(this.actionUrl);
114
116
  url.pathname += route.path;
@@ -122,6 +124,7 @@ export class RestRuntimeInvoker {
122
124
  headers,
123
125
  body: route.method === 'GET' ? undefined : body,
124
126
  signal,
127
+ redirect: 'error',
125
128
  });
126
129
  }
127
130
  async refreshAuthorization() {
@@ -136,19 +139,33 @@ export class RestRuntimeInvoker {
136
139
  }
137
140
  }
138
141
  }
142
+ async function runtimeFetch(fetch, request) {
143
+ try {
144
+ return await fetch(request);
145
+ }
146
+ catch (error) {
147
+ if (error instanceof DOMException && error.name === 'AbortError') {
148
+ throw error;
149
+ }
150
+ throw new TransportError('Unable to reach the PHPSandbox runtime.', 'ConnectionFailed', error);
151
+ }
152
+ }
139
153
  async function runtimeActionResponse(response) {
140
154
  if (response.ok && response.headers.get('content-type')?.toLowerCase().startsWith('application/octet-stream')) {
141
155
  return { data: new Uint8Array(await response.arrayBuffer()) };
142
156
  }
143
157
  const body = await response.text();
144
158
  if (body === '') {
159
+ if (response.ok) {
160
+ throw new TransportError('PHPSandbox runtime returned an empty response.', 'InvalidResponse', response);
161
+ }
145
162
  return {};
146
163
  }
147
164
  try {
148
165
  return JSON.parse(body);
149
166
  }
150
- catch {
151
- return { error: { code: response.status, message: body } };
167
+ catch (error) {
168
+ throw new TransportError('PHPSandbox runtime returned an invalid JSON response.', 'InvalidResponse', error);
152
169
  }
153
170
  }
154
171
  function runtimeActionRequestBody(action, data, url, headers) {
@@ -0,0 +1,68 @@
1
+ import type { Action, NotebookInstance } from './index.js';
2
+ import Logs, { type RuntimeLogEntry } from './log.js';
3
+ import Metrics from './metrics.js';
4
+ import Ports from './ports.js';
5
+ import { type RuntimeTelemetryFeature, type RuntimeWatchOptions } from './runtime-telemetry.js';
6
+ export interface PortInfo {
7
+ subdomain: string;
8
+ url: string;
9
+ default: boolean;
10
+ localPort?: number;
11
+ externalPort?: number;
12
+ port: number;
13
+ }
14
+ export interface RuntimeResourceUsage {
15
+ usage: number;
16
+ limit: number;
17
+ }
18
+ export interface RuntimeStats {
19
+ cpu: RuntimeResourceUsage;
20
+ memory: RuntimeResourceUsage;
21
+ disk: RuntimeResourceUsage;
22
+ }
23
+ export interface RuntimeEvents {
24
+ 'container.stats': RuntimeStats;
25
+ 'container.ports': PortInfo[];
26
+ }
27
+ export interface RuntimeActions {
28
+ 'container.restart': Action<{}, void>;
29
+ 'container.stop': Action<{}, void>;
30
+ 'container.opened-ports': Action<{}, PortInfo[]>;
31
+ 'container.stats': Action<{}, RuntimeStats | null>;
32
+ 'container.set-php': Action<{
33
+ version: string;
34
+ }, {
35
+ version: string;
36
+ }>;
37
+ 'container.stream-telemetry': Action<{
38
+ features: RuntimeTelemetryFeature[];
39
+ }, void>;
40
+ 'container.stop-telemetry': Action<{}, void>;
41
+ }
42
+ export declare class RuntimeMetrics {
43
+ private readonly metrics;
44
+ constructor(metrics: Metrics);
45
+ current(): Promise<RuntimeStats | null>;
46
+ watch(options?: RuntimeWatchOptions): ReadableStream<RuntimeStats>;
47
+ }
48
+ export declare class RuntimeLogs {
49
+ private readonly logs;
50
+ constructor(logs: Logs);
51
+ follow(options?: RuntimeWatchOptions): ReadableStream<RuntimeLogEntry>;
52
+ }
53
+ export declare class RuntimePorts {
54
+ private readonly ports;
55
+ constructor(ports: Ports);
56
+ list(): Promise<PortInfo[]>;
57
+ watch(options?: RuntimeWatchOptions): ReadableStream<PortInfo[]>;
58
+ }
59
+ export default class Runtime {
60
+ private readonly notebook;
61
+ readonly metrics: RuntimeMetrics;
62
+ readonly logs: RuntimeLogs;
63
+ readonly ports: RuntimePorts;
64
+ constructor(notebook: NotebookInstance);
65
+ setPhpVersion(version: string): Promise<{
66
+ version: string;
67
+ }>;
68
+ }
@@ -0,0 +1,46 @@
1
+ import Logs from './log.js';
2
+ import Metrics from './metrics.js';
3
+ import Ports from './ports.js';
4
+ import { RuntimeTelemetry, } from './runtime-telemetry.js';
5
+ export class RuntimeMetrics {
6
+ constructor(metrics) {
7
+ this.metrics = metrics;
8
+ }
9
+ current() {
10
+ return this.metrics.current();
11
+ }
12
+ watch(options = {}) {
13
+ return this.metrics.watch(options);
14
+ }
15
+ }
16
+ export class RuntimeLogs {
17
+ constructor(logs) {
18
+ this.logs = logs;
19
+ }
20
+ follow(options = {}) {
21
+ return this.logs.follow(options);
22
+ }
23
+ }
24
+ export class RuntimePorts {
25
+ constructor(ports) {
26
+ this.ports = ports;
27
+ }
28
+ list() {
29
+ return this.ports.list();
30
+ }
31
+ watch(options = {}) {
32
+ return this.ports.watch(options);
33
+ }
34
+ }
35
+ export default class Runtime {
36
+ constructor(notebook) {
37
+ this.notebook = notebook;
38
+ const telemetry = new RuntimeTelemetry(notebook);
39
+ this.metrics = new RuntimeMetrics(new Metrics(notebook, telemetry));
40
+ this.logs = new RuntimeLogs(new Logs(notebook, telemetry));
41
+ this.ports = new RuntimePorts(new Ports(notebook, telemetry));
42
+ }
43
+ setPhpVersion(version) {
44
+ return this.notebook.invoke('container.set-php', { version });
45
+ }
46
+ }
@@ -25,8 +25,6 @@ export declare class SandboxIntegrationApi {
25
25
  attach(input: AttachSandboxIntegrationInput): Promise<SandboxIntegration>;
26
26
  list(): Promise<SandboxIntegration[]>;
27
27
  get(integration: SandboxIntegrationReference): Promise<SandboxIntegration>;
28
- update(integration: SandboxIntegrationReference, input: UpdateSandboxIntegrationInput): Promise<SandboxIntegration>;
29
- detach(integration: SandboxIntegrationReference): Promise<void>;
30
28
  private path;
31
29
  private bindingPath;
32
30
  }
@@ -40,5 +38,6 @@ export declare class SandboxIntegration {
40
38
  get effects(): SandboxIntegrationEffect[];
41
39
  update(input: UpdateSandboxIntegrationInput): Promise<SandboxIntegration>;
42
40
  detach(): Promise<void>;
41
+ private path;
43
42
  }
44
43
  export declare function sandboxIntegrationId(integration: SandboxIntegrationReference): string;
@@ -19,16 +19,6 @@ export class SandboxIntegrationApi {
19
19
  const response = await this.client.get(this.bindingPath(integration));
20
20
  return new SandboxIntegration(response.data, this.notebookId, this.client);
21
21
  }
22
- async update(integration, input) {
23
- const response = await this.client.patch(this.bindingPath(integration), {
24
- ...input,
25
- integration: input.integration === undefined ? undefined : integrationId(input.integration),
26
- });
27
- return new SandboxIntegration(response.data, this.notebookId, this.client);
28
- }
29
- async detach(integration) {
30
- await this.client.delete(this.bindingPath(integration));
31
- }
32
22
  path() {
33
23
  return `/notebook/${this.notebookId}/integrations`;
34
24
  }
@@ -51,11 +41,18 @@ export class SandboxIntegration {
51
41
  get effects() {
52
42
  return this.data.effects;
53
43
  }
54
- update(input) {
55
- return new SandboxIntegrationApi(this.notebookId, this.client).update(this, input);
44
+ async update(input) {
45
+ const response = await this.client.patch(this.path(), {
46
+ ...input,
47
+ integration: input.integration === undefined ? undefined : integrationId(input.integration),
48
+ });
49
+ return new SandboxIntegration(response.data, this.notebookId, this.client);
50
+ }
51
+ async detach() {
52
+ await this.client.delete(this.path());
56
53
  }
57
- detach() {
58
- return new SandboxIntegrationApi(this.notebookId, this.client).detach(this);
54
+ path() {
55
+ return `/notebook/${this.notebookId}/integrations/${encodeURIComponent(this.id)}`;
59
56
  }
60
57
  }
61
58
  export function sandboxIntegrationId(integration) {