@phpsandbox/sdk 0.1.19 → 0.2.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.
@@ -36,8 +36,8 @@ export interface HttpResponseDiagnostics {
36
36
  }
37
37
  export declare class TransportError<TCode extends TransportErrorCode = TransportErrorCode> extends PHPSandboxError {
38
38
  readonly code: TCode;
39
- readonly response?: HttpResponseDiagnostics;
40
- constructor(message: string, code: TCode, cause?: unknown, response?: HttpResponseDiagnostics);
39
+ readonly response?: HttpResponseDiagnostics | undefined;
40
+ constructor(message: string, code: TCode, cause?: unknown, response?: HttpResponseDiagnostics | undefined);
41
41
  static is(error: unknown): error is TransportError;
42
42
  static is<TCode extends TransportErrorCode>(error: unknown, code: TCode): error is TransportError<TCode>;
43
43
  is<TExpectedCode extends TransportErrorCode>(code: TExpectedCode): this is this & TransportError<TExpectedCode>;
@@ -1,23 +1,29 @@
1
- import * as MittModule from 'mitt';
1
+ import mitt from 'mitt';
2
2
  import type { EventHandlerMap, EventType, Handler } from 'mitt';
3
3
  import { Disposable } from '../types.js';
4
- export interface EventDispatcher {
5
- listen: (event: EventType, callback: Handler<unknown>, context?: unknown) => Disposable;
6
- once: (event: EventType, callback: Handler<unknown>, context?: unknown) => Disposable;
7
- emit: (event: EventType, ...args: unknown[]) => void;
8
- removeListener: (event?: EventType, callbackSignature?: Handler<unknown>) => void;
4
+ export interface EventDispatcher<Events extends Record<EventType, unknown> = Record<EventType, unknown>> {
5
+ listen: <Key extends keyof Events>(event: Key, callback: Handler<Events[Key]>, context?: unknown) => Disposable;
6
+ once: <Key extends keyof Events>(event: Key, callback: Handler<Events[Key]>, context?: unknown) => Disposable;
7
+ emit: {
8
+ <Key extends keyof Events>(event: Key, payload: Events[Key]): void;
9
+ <Key extends keyof Events>(event: undefined extends Events[Key] ? Key : never): void;
10
+ };
11
+ removeListener: <Key extends keyof Events>(event?: Key, callbackSignature?: Handler<Events[Key]>) => void;
9
12
  }
10
- export declare function mittWithOnce<Events extends Record<EventType, unknown>>(all?: EventHandlerMap<Events>): MittModule.Emitter<Events> & {
11
- once<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): Disposable;
13
+ export declare function mittWithOnce<Events extends Record<EventType, unknown>>(all?: EventHandlerMap<Events>): mitt.Emitter<Events> & {
14
+ once: <Key extends keyof Events>(type: Key, fn: Handler<Events[Key]>) => {
15
+ dispose: () => void;
16
+ };
12
17
  };
13
- export default class EventManager implements EventDispatcher {
18
+ export default class EventManager<Events extends Record<EventType, unknown> = Record<EventType, unknown>> implements EventDispatcher<Events> {
14
19
  private readonly emitter;
15
- listen(event: EventType, callback: Handler<unknown>): Disposable;
16
- emit(event: EventType, ...data: unknown[]): void;
17
- once(event: EventType, callback: Handler<unknown>): Disposable;
18
- static make(): EventDispatcher;
20
+ listen<Key extends keyof Events>(event: Key, callback: Handler<Events[Key]>): Disposable;
21
+ emit<Key extends keyof Events>(event: Key, payload: Events[Key]): void;
22
+ emit<Key extends keyof Events>(event: undefined extends Events[Key] ? Key : never): void;
23
+ once<Key extends keyof Events>(event: Key, callback: Handler<Events[Key]>): Disposable;
24
+ static make<Events extends Record<EventType, unknown> = Record<EventType, unknown>>(): EventDispatcher<Events>;
19
25
  static refresh(): EventDispatcher;
20
- removeListener(event?: EventType, callbackSignature?: Handler<unknown>): void;
26
+ removeListener<Key extends keyof Events>(event?: Key, callbackSignature?: Handler<Events[Key]>): void;
21
27
  static createInstance(): EventDispatcher;
22
- inspect(): EventHandlerMap<Record<EventType, unknown>>;
28
+ inspect(): EventHandlerMap<Events>;
23
29
  }
@@ -1,8 +1,8 @@
1
- import * as MittModule from 'mitt';
2
- const createMitt = (MittModule.default ?? MittModule);
1
+ import mitt from 'mitt';
2
+ const createMitt = typeof mitt === 'function' ? mitt : mitt.default;
3
3
  export function mittWithOnce(all) {
4
4
  const inst = createMitt(all);
5
- inst.once = (type, fn) => {
5
+ const once = (type, fn) => {
6
6
  const onceHandler = (event) => {
7
7
  inst.off(type, onceHandler);
8
8
  fn(event);
@@ -14,7 +14,7 @@ export function mittWithOnce(all) {
14
14
  },
15
15
  };
16
16
  };
17
- return inst;
17
+ return Object.assign(inst, { once });
18
18
  }
19
19
  export default class EventManager {
20
20
  constructor() {
@@ -28,7 +28,7 @@ export default class EventManager {
28
28
  return { dispose };
29
29
  }
30
30
  emit(event, ...data) {
31
- this.emitter.emit(event, data.length > 1 ? data : data[0]);
31
+ this.emitter.emit(event, data[0]);
32
32
  }
33
33
  once(event, callback) {
34
34
  return this.emitter.once(event, callback);
@@ -40,6 +40,10 @@ export default class EventManager {
40
40
  return new EventManager();
41
41
  }
42
42
  removeListener(event, callbackSignature) {
43
+ if (event === undefined) {
44
+ this.emitter.all.clear();
45
+ return;
46
+ }
43
47
  this.emitter.off(event, callbackSignature);
44
48
  }
45
49
  static createInstance() {
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { NotebookPublication } from './publication/index.js';
1
2
  import { Filesystem, FilesystemActions, FilesystemEvents } from './filesystem.js';
2
3
  import Terminals, { TerminalEvents, TerminalActions, type SpawnOptions } from './terminal.js';
3
4
  import Auth, { AuthActions } from './auth.js';
@@ -9,16 +10,16 @@ import Repl, { ReplActions, ReplEvents } from './repl.js';
9
10
  import { ShellEvents, ShellActions, type ProcessResult, type ShellProcess } from './shell.js';
10
11
  import Services, { ServiceActions, ServiceEvents } from './services.js';
11
12
  import Config, { ConfigActions } from './config.js';
12
- import Git, { GitActions } from './git.js';
13
+ import Git, { GitActions, type GitSyncAuthor } from './git.js';
13
14
  import { Disposable } from './types.js';
14
15
  import { RemoteError } from './errors/index.js';
15
- import { PublicationApi, PublicationInstance, PublicationRun, type PublishInput } from './publications.js';
16
+ import { PublicationApi } from './publication/index.js';
16
17
  import { ServerApi } from './servers.js';
17
18
  import { Feedback } from './feedback.js';
18
19
  import { IntegrationApi, SandboxIntegrationApi } from './integrations/index.js';
19
20
  export type { Disposable } from './types.js';
20
21
  export { LspConnection } from './lsp.js';
21
- export { FileChangeFilter, FileChangeType, FileType, } from './filesystem.js';
22
+ export { FileChangeFilter, FileChangeType, FileType } from './filesystem.js';
22
23
  export type { FileChange, FileContents, FileDeleteOptions, FileDownloadOptions, FileFollowOptions, FileLineRange, FileLineRangeResult, FileListOptions, FileOverwriteOptions, FileResult, FileSearchOptions, FileTailOptions, FileWriteOptions, Filesystem, Stats, TextSearchMatch, TextSearchOptions, TextSearchQuery, TextSearchResult, WatchOptions, } from './filesystem.js';
23
24
  export type { PortInfo } from './runtime.js';
24
25
  export type { RuntimeLogEntry } from './log.js';
@@ -30,9 +31,9 @@ export type { GitChangedFile, GitCheckoutResult, GitConflict, GitConflictsResult
30
31
  export { notebookBuiltinServices, notebookKnownServices } from './services.js';
31
32
  export type { NotebookBuiltinServiceName, NotebookKnownServiceName, NotebookServiceCommand, NotebookServiceKind, NotebookServiceName, NotebookServiceSource, NotebookServiceState, NotebookServiceStatus, ServiceLogOptions, ServiceLogStreamOptions, } from './services.js';
32
33
  export type { ConfigPortMapping, ProjectConfig } from './config.js';
33
- export { PHPSandboxError, RemoteError, TransportError, } from './errors/index.js';
34
+ export { PHPSandboxError, RemoteError, TransportError } from './errors/index.js';
34
35
  export type { HttpResponseDiagnostics, RemoteErrorCode, RemoteErrorSource, TransportErrorCode, ValidationErrorDetails, } from './errors/index.js';
35
- export type { BuiltInPublicationProviderName, CloudflareContainersProviderOptions, LaravelCloudProviderData, LaravelCloudProviderInput, LaravelCloudRegion, PublicationBuildData, PublicationBuildStatus, PublicationData, PublicationDomainData, PublicationDomains, PublicationEventData, PublicationInstance, PublicationJurisdiction, PublicationLogChunkData, PublicationLogStream, PublicationPlacement, PublicationProtectionData, PublicationProtectionInput, PublicationProtectionMode, PublicationProtectionSessionData, PublicationProvider, PublicationProviderData, PublicationProviderInput, PublicationProviderInputs, PublicationProviderName, PublicationRegion, PublicationReleaseData, PublicationReleaseStatus, PublicationRun, PublicationSize, PublicationStatus, PublishInput, PublishStreamEvent, PublishStreamLog, PublishStreamPhase, PublishStreamResult, PublishStreamSseEvent, SshServerProviderOptions, UpdatePublicationProtectionInput, } from './publications.js';
36
+ export * from './publication/index.js';
36
37
  export type { CreateServerInput, ServerData, ServerInstance, ServerListOptions, ServerSshOptions } from './servers.js';
37
38
  export type { ConfigureFeedbackInput, FeedbackData, FeedbackListOptions, FeedbackSource, FeedbackStatus, FeedbackType, FeedbackWidgetConfig, FeedbackWidgetTarget, SubmitFeedbackInput, UpdateFeedbackInput, } from './feedback.js';
38
39
  export type { Integration, IntegrationAuthorization, IntegrationAuthorizationType, IntegrationData, IntegrationProvider, IntegrationReference, LinkIntegrationInput, UpdateIntegrationInput, AttachSandboxIntegrationInput, SandboxIntegration, SandboxIntegrationData, SandboxIntegrationEffect, SandboxIntegrationReference, UpdateSandboxIntegrationInput, } from './integrations/index.js';
@@ -133,7 +134,12 @@ export interface NotebookImportInspectionData {
133
134
  reason: string | null;
134
135
  requiredSecrets: NotebookImportRequiredSecret[];
135
136
  }
137
+ export interface NotebookGitConfiguration {
138
+ /** Set a sandbox default identity, or use null to reset it to the Helfer bot. */
139
+ author: GitSyncAuthor | null;
140
+ }
136
141
  export interface NotebookLifecycleInput {
142
+ git?: NotebookGitConfiguration;
137
143
  persistent?: true;
138
144
  }
139
145
  export interface CreateNotebookInput extends NotebookLifecycleInput {
@@ -144,6 +150,11 @@ export interface CreateNotebookInput extends NotebookLifecycleInput {
144
150
  secrets?: UpsertNotebookSecretItemInput[];
145
151
  }
146
152
  export type ForkNotebookInput = NotebookLifecycleInput;
153
+ export interface UpdateNotebookInput {
154
+ title?: string;
155
+ visibility?: 'public' | 'private' | 'unlisted';
156
+ git?: NotebookGitConfiguration;
157
+ }
147
158
  export interface NotebookPolicyData {
148
159
  autoDeleteWhenStale: boolean;
149
160
  staleAfterMinutes: number | null;
@@ -330,8 +341,8 @@ declare class ClientImplementation {
330
341
  post<T extends unknown>(path: string, body?: unknown): Promise<{
331
342
  data: T;
332
343
  }>;
333
- delete<T extends unknown>(path: string): Promise<{
334
- data: T;
344
+ delete<T>(path: string): Promise<{
345
+ data: T | undefined;
335
346
  }>;
336
347
  put<T extends unknown>(path: string, body?: unknown): Promise<{
337
348
  data: T;
@@ -341,6 +352,7 @@ declare class ClientImplementation {
341
352
  }>;
342
353
  stream(path: string): Promise<ReadableStream<Uint8Array>>;
343
354
  sse(url: string, signal?: AbortSignal): Promise<ReadableStream<Uint8Array>>;
355
+ private requireData;
344
356
  private makeRequest;
345
357
  private fetchResponse;
346
358
  }
@@ -365,6 +377,9 @@ export interface NotebookData {
365
377
  slug?: string;
366
378
  description?: string | null;
367
379
  policy?: NotebookPolicyData;
380
+ git?: {
381
+ author: GitSyncAuthor;
382
+ };
368
383
  }
369
384
  export declare class NotebookInstance {
370
385
  #private;
@@ -386,6 +401,7 @@ export declare class NotebookInstance {
386
401
  readonly preview: NotebookPreview;
387
402
  readonly mail: NotebookMail;
388
403
  readonly feedback: Feedback;
404
+ readonly publication: NotebookPublication;
389
405
  private readonly socket;
390
406
  private readonly restInvoker;
391
407
  private readonly emitter;
@@ -394,9 +410,9 @@ export declare class NotebookInstance {
394
410
  private readonly disposables;
395
411
  constructor(data: NotebookData, client: Client);
396
412
  ready(): Promise<NotebookInitSuccessResult>;
413
+ /** Update sandbox metadata and Git defaults without restarting it. */
414
+ update(input: UpdateNotebookInput): Promise<NotebookData>;
397
415
  fork(input?: Partial<ForkNotebookInput>): Promise<NotebookInstance>;
398
- publication(): Promise<PublicationInstance | null>;
399
- publish(input?: PublishInput): Promise<PublicationRun>;
400
416
  run(command: string | string[], opts?: SpawnOptions): ShellProcess;
401
417
  run(command: string, args: string[], opts?: SpawnOptions): ShellProcess;
402
418
  exec(command: string | string[], opts?: SpawnOptions): Promise<ProcessResult>;
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
12
  var _NotebookInstance_instances, _NotebookInstance_initPromise, _NotebookInstance_init;
13
+ import { NotebookPublication } from './publication/index.js';
13
14
  import { Filesystem } from './filesystem.js';
14
15
  import Terminals from './terminal.js';
15
16
  import Auth from './auth.js';
@@ -23,17 +24,18 @@ import Config from './config.js';
23
24
  import { Transport } from './socket/index.js';
24
25
  import EventManager from './events/index.js';
25
26
  import Git from './git.js';
26
- import { PHPSandboxError, RemoteError, TransportError, remoteError, } from './errors/index.js';
27
- import { PublicationApi, PublicationInstance, PublicationRun, } from './publications.js';
27
+ import { PHPSandboxError, RemoteError, TransportError, remoteError } from './errors/index.js';
28
+ import { PublicationApi } from './publication/index.js';
28
29
  import { ServerApi } from './servers.js';
29
30
  import { RestRuntimeInvoker } from './runtime/rest.js';
30
31
  import { Feedback } from './feedback.js';
31
32
  import { IntegrationApi, SandboxIntegrationApi } from './integrations/index.js';
32
33
  import { authenticatedRequest } from './http.js';
33
34
  export { LspConnection } from './lsp.js';
34
- export { FileChangeFilter, FileChangeType, FileType, } from './filesystem.js';
35
+ export { FileChangeFilter, FileChangeType, FileType } from './filesystem.js';
35
36
  export { notebookBuiltinServices, notebookKnownServices } from './services.js';
36
- export { PHPSandboxError, RemoteError, TransportError, } from './errors/index.js';
37
+ export { PHPSandboxError, RemoteError, TransportError } from './errors/index.js';
38
+ export * from './publication/index.js';
37
39
  function normalizeNotebookSecretInputs(input) {
38
40
  if (Array.isArray(input)) {
39
41
  return input;
@@ -91,25 +93,25 @@ class ClientImplementation {
91
93
  this.fetch = options.fetch;
92
94
  }
93
95
  this.headers = {
94
- 'Accept': 'application/json',
95
- 'Authorization': `Bearer ${token}`,
96
+ Accept: 'application/json',
97
+ Authorization: `Bearer ${token}`,
96
98
  'Content-Type': 'application/json',
97
99
  };
98
100
  }
99
101
  get(path) {
100
- return this.makeRequest('GET', path);
102
+ return this.requireData(this.makeRequest('GET', path));
101
103
  }
102
104
  post(path, body) {
103
- return this.makeRequest('POST', path, { body: body === undefined ? undefined : JSON.stringify(body) });
105
+ return this.requireData(this.makeRequest('POST', path, { body: body === undefined ? undefined : JSON.stringify(body) }));
104
106
  }
105
107
  delete(path) {
106
108
  return this.makeRequest('DELETE', path);
107
109
  }
108
110
  put(path, body) {
109
- return this.makeRequest('PUT', path, { body: body === undefined ? undefined : JSON.stringify(body) });
111
+ return this.requireData(this.makeRequest('PUT', path, { body: body === undefined ? undefined : JSON.stringify(body) }));
110
112
  }
111
113
  patch(path, body) {
112
- return this.makeRequest('PATCH', path, { body: body === undefined ? undefined : JSON.stringify(body) });
114
+ return this.requireData(this.makeRequest('PATCH', path, { body: body === undefined ? undefined : JSON.stringify(body) }));
113
115
  }
114
116
  async stream(path) {
115
117
  const response = await this.fetchResponse(authenticatedRequest(new URL(path.replace(/^\//, ''), this.baseUrl), {
@@ -129,7 +131,7 @@ class ClientImplementation {
129
131
  method: 'GET',
130
132
  headers: {
131
133
  ...this.headers,
132
- 'Accept': 'text/event-stream',
134
+ Accept: 'text/event-stream',
133
135
  },
134
136
  ...(signal === undefined ? {} : { signal }),
135
137
  }));
@@ -141,6 +143,13 @@ class ClientImplementation {
141
143
  }
142
144
  return response.body;
143
145
  }
146
+ async requireData(request) {
147
+ const response = await request;
148
+ if (response.data === undefined) {
149
+ throw new TransportError('PHPSandbox API returned no data for a data request.', 'InvalidResponse');
150
+ }
151
+ return { ...response, data: response.data };
152
+ }
144
153
  async makeRequest(method, path, init) {
145
154
  const response = await this.fetchResponse(authenticatedRequest(new URL(path.replace(/^\//, ''), this.baseUrl), {
146
155
  method,
@@ -159,7 +168,10 @@ class ClientImplementation {
159
168
  }
160
169
  try {
161
170
  const payload = JSON.parse(body);
162
- if (payload === null || typeof payload !== 'object' || Array.isArray(payload) || !Object.prototype.hasOwnProperty.call(payload, 'data')) {
171
+ if (payload === null ||
172
+ typeof payload !== 'object' ||
173
+ Array.isArray(payload) ||
174
+ !Object.prototype.hasOwnProperty.call(payload, 'data')) {
163
175
  throw new TransportError('PHPSandbox API returned an invalid success response.', 'InvalidResponse', response);
164
176
  }
165
177
  return payload;
@@ -234,9 +246,7 @@ export class NotebookInstance {
234
246
  this.emitter = EventManager.createInstance();
235
247
  if (client.runtimeTransport === 'rest') {
236
248
  this.socket = null;
237
- this.restInvoker = new RestRuntimeInvoker(data, client.options.fetch ?? globalThis.fetch, runtimeUrlProvider === undefined
238
- ? undefined
239
- : () => runtimeUrlProvider(data.id));
249
+ this.restInvoker = new RestRuntimeInvoker(data, client.options.fetch ?? globalThis.fetch, runtimeUrlProvider === undefined ? undefined : () => runtimeUrlProvider(data.id));
240
250
  }
241
251
  else {
242
252
  this.restInvoker = null;
@@ -273,6 +283,7 @@ export class NotebookInstance {
273
283
  this.preview = new NotebookPreview(client, this.data.id);
274
284
  this.mail = new NotebookMail(client, this.data.id);
275
285
  this.feedback = new Feedback(client, this.data.id);
286
+ this.publication = new NotebookPublication(this.data.id, client);
276
287
  }
277
288
  async ready() {
278
289
  if (this.restInvoker !== null) {
@@ -297,28 +308,16 @@ export class NotebookInstance {
297
308
  // Just apply a reasonable timeout for the entire initialization process
298
309
  return ready();
299
310
  }
311
+ /** Update sandbox metadata and Git defaults without restarting it. */
312
+ async update(input) {
313
+ const response = await this.client.put(`/notebook/${this.data.id}`, input);
314
+ Object.assign(this.data, response.data);
315
+ return this.data;
316
+ }
300
317
  async fork(input = {}) {
301
318
  const response = await this.client.post(`/notebook/${this.data.id}/fork`, input);
302
319
  return new NotebookInstance(response.data, this.client);
303
320
  }
304
- async publication() {
305
- try {
306
- const response = await this.client.get(`/notebook/${this.data.id}/publication`);
307
- return new PublicationInstance(response.data, this.client, this.data.id);
308
- }
309
- catch (error) {
310
- if (RemoteError.is(error) && error.status === 404) {
311
- return null;
312
- }
313
- throw error;
314
- }
315
- }
316
- async publish(input) {
317
- const response = await this.client.post(`/notebook/${this.data.id}/publication`, input);
318
- const run = new PublicationRun(new PublicationInstance(response.data, this.client, this.data.id), this.client);
319
- run.start();
320
- return run;
321
- }
322
321
  run(command, argsOrOpts = [], opts) {
323
322
  if (Array.isArray(argsOrOpts)) {
324
323
  return this.shell.run(command, argsOrOpts, opts);
@@ -441,7 +440,11 @@ export class NotebookInstance {
441
440
  return disposable;
442
441
  }
443
442
  onDidBootError(handler) {
444
- const disposable = this.realtimeSocket().listen('okra.boot_error', handler);
443
+ const disposable = this.realtimeSocket().listen('okra.boot_error', (error) => {
444
+ if (RemoteError.is(error, 'NotebookUnavailable')) {
445
+ handler(error);
446
+ }
447
+ });
445
448
  this.disposables.push(disposable);
446
449
  return disposable;
447
450
  }
@@ -0,0 +1,17 @@
1
+ import type { Client } from '../index.js';
2
+ import type { PublicationDomainData } from './types.js';
3
+ export declare class PublicationDomains {
4
+ private readonly client;
5
+ private readonly publicationId;
6
+ constructor(client: Client, publicationId: string);
7
+ list(): Promise<PublicationDomainData[]>;
8
+ create(hostname: string): Promise<PublicationDomainData>;
9
+ refresh(id: string): Promise<PublicationDomainData>;
10
+ /** Sets production APP_URL. Publish again to apply it to the running app. */
11
+ useAsApplicationUrl(id: string): Promise<{
12
+ url: string;
13
+ requiresPublication: boolean;
14
+ }>;
15
+ delete(id: string): Promise<void>;
16
+ private path;
17
+ }
@@ -0,0 +1,25 @@
1
+ export class PublicationDomains {
2
+ constructor(client, publicationId) {
3
+ this.client = client;
4
+ this.publicationId = publicationId;
5
+ }
6
+ async list() {
7
+ return (await this.client.get(this.path())).data;
8
+ }
9
+ async create(hostname) {
10
+ return (await this.client.post(this.path(), { hostname })).data;
11
+ }
12
+ async refresh(id) {
13
+ return (await this.client.post(`${this.path()}/${encodeURIComponent(id)}/refresh`)).data;
14
+ }
15
+ /** Sets production APP_URL. Publish again to apply it to the running app. */
16
+ async useAsApplicationUrl(id) {
17
+ return (await this.client.post(`${this.path()}/${encodeURIComponent(id)}/application-url`)).data;
18
+ }
19
+ async delete(id) {
20
+ await this.client.delete(`${this.path()}/${encodeURIComponent(id)}`);
21
+ }
22
+ path() {
23
+ return `/publications/${encodeURIComponent(this.publicationId)}/domains`;
24
+ }
25
+ }
@@ -0,0 +1,8 @@
1
+ export { NotebookPublication } from './notebook.js';
2
+ export { PublicationApi, PublicationInstance } from './instance.js';
3
+ export { PublicationRun } from './run.js';
4
+ export type * from './types.js';
5
+ export type { BuiltInPublicationProviderName, PublicationStatus, PublicationBuildStatus, PublicationReleaseStatus, PublicationSize, PublicationRegion, PublicationJurisdiction, PublicationProtectionMode, PublicationLogStream, PublicationEventData, PublicationLogChunkData, PublishStreamPhase, PublishStreamLog, PublishStreamResult, PublishStreamEvent, PublishStreamSseEvent, } from '../schemas/publications.js';
6
+ export { laravelCloudSetupForRepublish } from './providers/laravel-cloud.js';
7
+ export { PublicationResources } from './resources.js';
8
+ export { PublicationDomains } from './domains.js';
@@ -0,0 +1,6 @@
1
+ export { NotebookPublication } from './notebook.js';
2
+ export { PublicationApi, PublicationInstance } from './instance.js';
3
+ export { PublicationRun } from './run.js';
4
+ export { laravelCloudSetupForRepublish } from './providers/laravel-cloud.js';
5
+ export { PublicationResources } from './resources.js';
6
+ export { PublicationDomains } from './domains.js';
@@ -0,0 +1,42 @@
1
+ import type { Client } from '../index.js';
2
+ import type { PublicationEventData, PublicationLogChunkData } from '../schemas/publications.js';
3
+ import type { PublicationProtectionData, LaravelCloudSetupInput, PublicationProviderName, PublicationData, UpdatePublicationProtectionInput, PublicationProtectionSessionData, PublicationDestroyOptions } from './types.js';
4
+ import { PublicationRun } from './run.js';
5
+ import { PublicationResources } from './resources.js';
6
+ import { PublicationDomains } from './domains.js';
7
+ export declare class PublicationApi {
8
+ private readonly client;
9
+ readonly resources: PublicationResources;
10
+ constructor(client: Client);
11
+ get<TProvider extends PublicationProviderName = PublicationProviderName>(id: string): Promise<PublicationInstance<TProvider>>;
12
+ }
13
+ export declare class PublicationInstance<TProvider extends PublicationProviderName = PublicationProviderName> {
14
+ readonly data: PublicationData<TProvider>;
15
+ private readonly client;
16
+ private readonly notebookId?;
17
+ readonly domains: PublicationDomains;
18
+ constructor(data: PublicationData<TProvider>, client: Client, notebookId?: string | undefined);
19
+ reconcileLaravelCloudResource(resourceId: string): Promise<PublicationInstance<TProvider>>;
20
+ configureLaravelCloud(setup: LaravelCloudSetupInput): Promise<PublicationInstance<TProvider>>;
21
+ refresh(): Promise<PublicationInstance<TProvider>>;
22
+ /** Start or reattach to the current release stream without queuing another release. */
23
+ follow(): PublicationRun<TProvider>;
24
+ publish(): Promise<PublicationRun<TProvider>>;
25
+ destroy(options?: PublicationDestroyOptions): Promise<void>;
26
+ wait(options?: {
27
+ intervalMs?: number;
28
+ timeoutMs?: number;
29
+ }): Promise<PublicationInstance<TProvider>>;
30
+ events(): Promise<ReadableStream<PublicationEventData>>;
31
+ buildLogs(buildId?: string): Promise<ReadableStream<PublicationLogChunkData>>;
32
+ logSnapshot(stream: 'build' | 'runtime', limit?: number): Promise<{
33
+ stream: 'build' | 'runtime';
34
+ content: string;
35
+ truncated: boolean;
36
+ }>;
37
+ logs(): Promise<ReadableStream<PublicationLogChunkData>>;
38
+ setProtection(input: UpdatePublicationProtectionInput): Promise<PublicationInstance<TProvider>>;
39
+ protection(): Promise<PublicationProtectionData>;
40
+ createProtectionSession(): Promise<PublicationProtectionSessionData>;
41
+ disableProtection(): Promise<PublicationInstance<TProvider>>;
42
+ }
@@ -0,0 +1,133 @@
1
+ import { PublicationEventDataSchema, PublicationLogChunkDataSchema } from '../schemas/publications.js';
2
+ import { PHPSandboxError, TransportError } from '../errors/index.js';
3
+ import { PublicationRun } from './run.js';
4
+ import { PublicationResources } from './resources.js';
5
+ import { PublicationDomains } from './domains.js';
6
+ export class PublicationApi {
7
+ constructor(client) {
8
+ this.client = client;
9
+ this.resources = new PublicationResources(client);
10
+ }
11
+ async get(id) {
12
+ const response = await this.client.get(`/publications/${encodeURIComponent(id)}`);
13
+ return new PublicationInstance(response.data, this.client);
14
+ }
15
+ }
16
+ export class PublicationInstance {
17
+ constructor(data, client, notebookId) {
18
+ this.data = data;
19
+ this.client = client;
20
+ this.notebookId = notebookId;
21
+ this.domains = new PublicationDomains(client, data.id);
22
+ }
23
+ async reconcileLaravelCloudResource(resourceId) {
24
+ const response = await this.client.post(`/publications/${encodeURIComponent(this.data.id)}/laravel-cloud/reconcile`, { resourceId });
25
+ return new PublicationInstance(response.data, this.client, this.notebookId);
26
+ }
27
+ async configureLaravelCloud(setup) {
28
+ const response = await this.client.put(`/publications/${encodeURIComponent(this.data.id)}/laravel-cloud/setup`, { setup });
29
+ return new PublicationInstance(response.data, this.client, this.notebookId);
30
+ }
31
+ async refresh() {
32
+ const response = await this.client.get(`/publications/${encodeURIComponent(this.data.id)}`);
33
+ return new PublicationInstance(response.data, this.client, this.notebookId);
34
+ }
35
+ /** Start or reattach to the current release stream without queuing another release. */
36
+ follow() {
37
+ return new PublicationRun(this, this.client).start();
38
+ }
39
+ async publish() {
40
+ if (!this.notebookId) {
41
+ throw new PHPSandboxError('Publishing requires a notebook-scoped publication instance.');
42
+ }
43
+ const response = await this.client.post(`/notebook/${encodeURIComponent(this.notebookId)}/publication`);
44
+ const run = new PublicationRun(new PublicationInstance(response.data, this.client, this.notebookId), this.client);
45
+ run.start();
46
+ return run;
47
+ }
48
+ async destroy(options = {}) {
49
+ const query = options.deleteResources ? '?deleteResources=1' : '';
50
+ await this.client.delete(`/publications/${encodeURIComponent(this.data.id)}${query}`);
51
+ }
52
+ async wait(options = {}) {
53
+ const intervalMs = options.intervalMs ?? 1000;
54
+ const timeoutMs = options.timeoutMs ?? 300000;
55
+ const startedAt = Date.now();
56
+ let current = this;
57
+ while (!publicationStatusIsTerminal(current.data.status)) {
58
+ if (Date.now() - startedAt > timeoutMs) {
59
+ throw new PHPSandboxError(`Timed out waiting for publication ${this.data.id}`);
60
+ }
61
+ await delay(intervalMs);
62
+ current = await current.refresh();
63
+ }
64
+ return current;
65
+ }
66
+ async events() {
67
+ return parseNdjsonStream(await this.client.stream(`/publications/${encodeURIComponent(this.data.id)}/events/stream`), PublicationEventDataSchema);
68
+ }
69
+ buildLogs(buildId = this.data.latestBuild?.id ?? '') {
70
+ if (buildId === '') {
71
+ return Promise.reject(new PHPSandboxError('Publication has no latest build id'));
72
+ }
73
+ return this.client
74
+ .stream(`/publications/${encodeURIComponent(this.data.id)}/builds/${encodeURIComponent(buildId)}/logs/stream`)
75
+ .then((stream) => parseNdjsonStream(stream, PublicationLogChunkDataSchema));
76
+ }
77
+ async logSnapshot(stream, limit = 50) {
78
+ const query = new URLSearchParams({ stream, limit: String(limit) });
79
+ return (await this.client.get(`/publications/${encodeURIComponent(this.data.id)}/logs?${query}`)).data;
80
+ }
81
+ async logs() {
82
+ return parseNdjsonStream(await this.client.stream(`/publications/${encodeURIComponent(this.data.id)}/logs/stream`), PublicationLogChunkDataSchema);
83
+ }
84
+ async setProtection(input) {
85
+ const response = await this.client.put(`/publications/${encodeURIComponent(this.data.id)}/protection`, input);
86
+ return new PublicationInstance(response.data, this.client, this.notebookId);
87
+ }
88
+ async protection() {
89
+ const response = await this.client.get(`/publications/${encodeURIComponent(this.data.id)}/protection`);
90
+ return response.data;
91
+ }
92
+ async createProtectionSession() {
93
+ const response = await this.client.post(`/publications/${encodeURIComponent(this.data.id)}/protection/session`);
94
+ return response.data;
95
+ }
96
+ async disableProtection() {
97
+ const response = await this.client.delete(`/publications/${encodeURIComponent(this.data.id)}/protection`);
98
+ if (!response.data) {
99
+ throw new TransportError('Protection response has no publication data.', 'InvalidResponse');
100
+ }
101
+ return new PublicationInstance(response.data, this.client, this.notebookId);
102
+ }
103
+ }
104
+ function publicationStatusIsTerminal(status) {
105
+ return ['healthy', 'failed', 'stopped', 'deleted', 'delete_failed'].includes(status);
106
+ }
107
+ function delay(ms) {
108
+ return new Promise((resolve) => setTimeout(resolve, ms));
109
+ }
110
+ function parseNdjsonStream(input, schema) {
111
+ const decoder = new TextDecoder();
112
+ let buffer = '';
113
+ return input.pipeThrough(new TransformStream({
114
+ transform(chunk, controller) {
115
+ buffer += decoder.decode(chunk, { stream: true });
116
+ const lines = buffer.split('\n');
117
+ buffer = lines.pop() ?? '';
118
+ for (const line of lines) {
119
+ const trimmed = line.trim();
120
+ if (trimmed !== '') {
121
+ controller.enqueue(schema.parse(JSON.parse(trimmed)));
122
+ }
123
+ }
124
+ },
125
+ flush(controller) {
126
+ buffer += decoder.decode();
127
+ const trimmed = buffer.trim();
128
+ if (trimmed !== '') {
129
+ controller.enqueue(schema.parse(JSON.parse(trimmed)));
130
+ }
131
+ },
132
+ }));
133
+ }
@@ -0,0 +1,20 @@
1
+ import type { Client } from '../index.js';
2
+ import { PublicationInstance } from './instance.js';
3
+ import { PublicationRun } from './run.js';
4
+ import { PublicationResources } from './resources.js';
5
+ import type { PlannedPublishInput, PublicationPlan, PublicationPlanInput, PublicationProviderName, PublicationReadiness, PublicationResourceCatalog } from './types.js';
6
+ export declare class NotebookPublication {
7
+ private readonly notebookId;
8
+ private readonly client;
9
+ readonly resources: PublicationResources;
10
+ constructor(notebookId: string, client: Client);
11
+ private get path();
12
+ current(): Promise<PublicationInstance | null>;
13
+ readiness(): Promise<PublicationReadiness>;
14
+ /** Review a publishing plan without committing changes or creating resources. */
15
+ prepare<TName extends PublicationProviderName>(input: PublicationPlanInput<TName>): Promise<PublicationPlan<TName>>;
16
+ catalog(provider: 'laravel-cloud' | 'cloudflare-containers'): Promise<PublicationResourceCatalog>;
17
+ catalog(provider: 'ssh-server', serverId: string): Promise<PublicationResourceCatalog>;
18
+ /** Core validates the plan, prepares source and executes publishing. */
19
+ publish(input?: PlannedPublishInput): Promise<PublicationRun>;
20
+ }