@phpsandbox/sdk 0.2.3 → 0.3.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.
@@ -19,6 +19,7 @@ export interface IntegrationData {
19
19
  provider: IntegrationProvider;
20
20
  label: string | null;
21
21
  createdAt: string | null;
22
+ unlinkedAt?: string | null;
22
23
  }
23
24
  export type IntegrationReference = string | Integration;
24
25
  export declare class IntegrationApi {
@@ -21,7 +21,6 @@ export declare class PublicationInstance<TProvider extends PublicationProviderNa
21
21
  refresh(): Promise<PublicationInstance<TProvider>>;
22
22
  /** Start or reattach to the current release stream without queuing another release. */
23
23
  follow(): PublicationRun<TProvider>;
24
- publish(): Promise<PublicationRun<TProvider>>;
25
24
  destroy(options?: PublicationDestroyOptions): Promise<void>;
26
25
  wait(options?: {
27
26
  intervalMs?: number;
@@ -36,15 +36,6 @@ export class PublicationInstance {
36
36
  follow() {
37
37
  return new PublicationRun(this, this.client).start();
38
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
39
  async destroy(options = {}) {
49
40
  const query = options.deleteResources ? '?deleteResources=1' : '';
50
41
  await this.client.delete(`/publications/${encodeURIComponent(this.data.id)}${query}`);
@@ -1,8 +1,7 @@
1
1
  import type { Client } from '../index.js';
2
2
  import { PublicationInstance } from './instance.js';
3
- import { PublicationRun } from './run.js';
4
3
  import { PublicationResources } from './resources.js';
5
- import type { PlannedPublishInput, PublicationApplicationPlan, PublicationApplicationPlanInput, PublicationApplicationPublishInput, PublicationApplicationData, PublicationPlan, PublicationPlanInput, PublicationProviderName, PublicationReadiness, PublicationResourceCatalog } from './types.js';
4
+ import type { PublicationProjectConfiguration, PublicationConfigurationUpdate, PublicationApplicationPlan, PublicationApplicationPlanInput, PublicationApplicationPublishInput, PublicationApplicationPublishResult, PublicationApplicationData, PublicationReadiness, PublicationResourceCatalog } from './types.js';
6
5
  export declare class NotebookPublication {
7
6
  private readonly notebookId;
8
7
  private readonly client;
@@ -11,14 +10,16 @@ export declare class NotebookPublication {
11
10
  private get path();
12
11
  current(): Promise<PublicationInstance | null>;
13
12
  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
- /** Review a multi-component publishing plan without creating resources or starting releases. */
17
- prepareApplication(input: PublicationApplicationPlanInput): Promise<PublicationApplicationPlan>;
18
- /** Publish every component in a reviewed application plan. */
19
- publishApplication(input: PublicationApplicationPublishInput): Promise<PublicationApplicationData>;
13
+ /** Inspect saved configuration and the actual inventory, including removed and legacy apps. */
14
+ configuration(): Promise<PublicationProjectConfiguration>;
15
+ /** Explicit operations preserve omitted apps. Removals never stop deployments or delete resource data. */
16
+ updateConfiguration(input: PublicationConfigurationUpdate): Promise<PublicationProjectConfiguration>;
17
+ /** Review all or selected configured apps at an exact saved revision. */
18
+ prepare(input: PublicationApplicationPlanInput): Promise<PublicationApplicationPlan>;
19
+ /** Publish the exact app selection in a server-owned review. */
20
+ publish(input: PublicationApplicationPublishInput): Promise<PublicationApplicationPublishResult>;
20
21
  application(applicationId: string): Promise<PublicationApplicationData>;
21
- /** Retry only failed components while leaving healthy components untouched. */
22
+ /** Retry failed apps and pending route attachments while preserving running releases. */
22
23
  retryApplication(applicationId: string): Promise<PublicationApplicationData>;
23
24
  destroyApplication(applicationId: string, options?: {
24
25
  deleteResources?: boolean;
@@ -26,6 +27,4 @@ export declare class NotebookPublication {
26
27
  private startApplicationRuns;
27
28
  catalog(provider: 'laravel-cloud' | 'cloudflare-containers'): Promise<PublicationResourceCatalog>;
28
29
  catalog(provider: 'ssh-server', serverId: string): Promise<PublicationResourceCatalog>;
29
- /** Core validates the plan, prepares source and executes publishing. */
30
- publish(input?: PlannedPublishInput): Promise<PublicationRun>;
31
30
  }
@@ -26,24 +26,29 @@ export class NotebookPublication {
26
26
  async readiness() {
27
27
  return (await this.client.get(`${this.path}/publication/readiness`)).data;
28
28
  }
29
- /** Review a publishing plan without committing changes or creating resources. */
29
+ /** Inspect saved configuration and the actual inventory, including removed and legacy apps. */
30
+ async configuration() {
31
+ return (await this.client.get(`${this.path}/publication/configuration`)).data;
32
+ }
33
+ /** Explicit operations preserve omitted apps. Removals never stop deployments or delete resource data. */
34
+ async updateConfiguration(input) {
35
+ return (await this.client.patch(`${this.path}/publication/configuration`, input)).data;
36
+ }
37
+ /** Review all or selected configured apps at an exact saved revision. */
30
38
  async prepare(input) {
31
39
  return (await this.client.post(`${this.path}/publication/plan`, input)).data;
32
40
  }
33
- /** Review a multi-component publishing plan without creating resources or starting releases. */
34
- async prepareApplication(input) {
35
- return (await this.client.post(`${this.path}/publication/application-plan`, input)).data;
36
- }
37
- /** Publish every component in a reviewed application plan. */
38
- async publishApplication(input) {
39
- const application = (await this.client.post(`${this.path}/publication/application`, input)).data;
40
- this.startApplicationRuns(application);
41
+ /** Publish the exact app selection in a server-owned review. */
42
+ async publish(input) {
43
+ const application = (await this.client.post(`${this.path}/publication`, input)).data;
44
+ if (!('confirmationRequired' in application))
45
+ this.startApplicationRuns(application);
41
46
  return application;
42
47
  }
43
48
  async application(applicationId) {
44
49
  return (await this.client.get(`${this.path}/publication/application/${encodeURIComponent(applicationId)}`)).data;
45
50
  }
46
- /** Retry only failed components while leaving healthy components untouched. */
51
+ /** Retry failed apps and pending route attachments while preserving running releases. */
47
52
  async retryApplication(applicationId) {
48
53
  const application = (await this.client.post(`${this.path}/publication/application/${encodeURIComponent(applicationId)}/retry`, {})).data;
49
54
  this.startApplicationRuns(application);
@@ -54,10 +59,12 @@ export class NotebookPublication {
54
59
  await this.client.delete(`${this.path}/publication/application/${encodeURIComponent(applicationId)}${query}`);
55
60
  }
56
61
  startApplicationRuns(application) {
57
- for (const component of application.components) {
58
- if (!['pending', 'queued', 'building', 'deploying'].includes(component.publication.status))
62
+ for (const app of application.apps) {
63
+ if (!(application.selectedAppIds ?? []).includes(app.appId))
64
+ continue;
65
+ if (!['pending', 'queued', 'building', 'deploying'].includes(app.publication.status))
59
66
  continue;
60
- new PublicationRun(new PublicationInstance(component.publication, this.client, this.notebookId), this.client).start();
67
+ new PublicationRun(new PublicationInstance(app.publication, this.client, this.notebookId), this.client).start();
61
68
  }
62
69
  }
63
70
  async catalog(provider, serverId) {
@@ -68,9 +75,4 @@ export class NotebookPublication {
68
75
  return (await this.client.get(`${this.path}/publication/catalog?${query}`))
69
76
  .data;
70
77
  }
71
- /** Core validates the plan, prepares source and executes publishing. */
72
- async publish(input) {
73
- const response = await this.client.post(`${this.path}/publication`, input);
74
- return new PublicationRun(new PublicationInstance(response.data, this.client, this.notebookId), this.client).start();
75
- }
76
78
  }
@@ -69,18 +69,6 @@ export interface PublicationResourceSelection {
69
69
  size?: string;
70
70
  config?: Record<string, string | number | boolean | null>;
71
71
  }
72
- export type PublicationPlanProviderInput<TName extends PublicationProviderName = PublicationProviderName> = {
73
- [Name in TName]: {
74
- name: Name;
75
- } & Partial<PublicationProviderInputs[Name]>;
76
- }[TName];
77
- export interface PublicationPlanInput<TName extends PublicationProviderName = PublicationProviderName> {
78
- slug?: string;
79
- provider: PublicationPlanProviderInput<TName>;
80
- requirements?: PublicationRequirement[];
81
- resources?: Partial<Record<PublicationResourceKind, PublicationResourceSelection>>;
82
- }
83
- export type PlannedPublishInput = PublishInput & Pick<PublicationPlanInput, 'requirements' | 'resources'>;
84
72
  export interface PublicationAccessCapabilities {
85
73
  routing: 'platform' | 'provider';
86
74
  protection: boolean;
@@ -89,8 +77,8 @@ export interface PublicationAccessCapabilities {
89
77
  export interface PublicationCapabilities {
90
78
  source: 'git' | 'workspace';
91
79
  sourceProviders?: string[];
92
- buildModes?: Array<PublicationComponentBuild['mode']>;
93
- componentKinds?: PublicationComponentKind[];
80
+ buildModes?: Array<PublicationAppBuild['mode']>;
81
+ appKinds?: Array<'http'>;
94
82
  resourceCleanup?: boolean;
95
83
  resources: Record<PublicationResourceKind, PublicationResourceMode[]>;
96
84
  access: {
@@ -123,6 +111,10 @@ export interface PublicationCatalogResourceType {
123
111
  configFields: PublicationCatalogConfigField[];
124
112
  }
125
113
  export interface PublicationResourceCatalog {
114
+ accounts?: {
115
+ id: string;
116
+ name: string;
117
+ }[];
126
118
  provider: PublicationProviderName;
127
119
  scope: string | null;
128
120
  capabilities: PublicationCapabilities;
@@ -143,56 +135,14 @@ export interface PublicationResourceCatalog {
143
135
  }[];
144
136
  pricingUrl: string | null;
145
137
  }
146
- export interface PublicationPlan<TName extends PublicationProviderName = PublicationProviderName> {
147
- provider: TName;
148
- capabilities: PublicationCapabilities;
149
- readiness: PublicationReadiness;
150
- source: {
151
- type: 'git' | 'workspace';
152
- repository: string | null;
153
- branch: string | null;
154
- commitAndPush: boolean;
155
- providerAccess: 'verified' | 'confirmed' | 'confirmation_required' | 'not_required';
156
- };
157
- resources: Array<{
158
- kind: PublicationResourceKind;
159
- mode: PublicationResourceMode | 'unconfigured';
160
- engine: string | null;
161
- required: boolean;
162
- recommended: boolean;
163
- supported: boolean;
164
- selection: PublicationResourceSelection | boolean | null;
165
- }>;
166
- blockers: Array<{
167
- code: string;
168
- message: string;
169
- }>;
170
- ready: boolean;
171
- input: PublicationPlanInput<TName>;
172
- cost: {
173
- status: 'unknown';
174
- message: string;
175
- };
176
- }
177
- export type PublicationComponentKind = 'frontend' | 'http-service' | 'static-site' | 'worker' | 'scheduler';
178
138
  export type PublicationDockerfile = {
179
139
  source: 'generated';
180
140
  content: string;
181
- digest?: string;
182
141
  } | {
183
142
  source: 'project';
184
143
  path: string;
185
- digest?: string;
186
144
  };
187
- export type PublicationComponentBuild = {
188
- mode: 'provider-native';
189
- workingDirectory?: string;
190
- context?: string;
191
- installCommand?: string;
192
- buildCommand?: string;
193
- deployCommand?: string;
194
- outputDirectory?: string;
195
- } | {
145
+ export type PublicationContainerBuild = {
196
146
  mode: 'dockerfile';
197
147
  workingDirectory?: string;
198
148
  context?: string;
@@ -200,59 +150,174 @@ export type PublicationComponentBuild = {
200
150
  target?: string;
201
151
  arguments?: Record<string, string>;
202
152
  secrets?: string[];
153
+ };
154
+ export type PublicationNativeBuild = {
155
+ mode: 'provider-native';
156
+ installCommand?: string;
157
+ buildCommand?: string;
158
+ deployCommand?: string;
159
+ };
160
+ export type PublicationAppBuild = PublicationContainerBuild | PublicationNativeBuild;
161
+ export type PublicationRoutingDraft = {
162
+ mode: 'managed';
163
+ routes?: never;
203
164
  } | {
204
- mode: 'image';
205
- reference: string;
165
+ mode: 'custom';
166
+ routes: [{
167
+ hostname: string;
168
+ }, ...Array<{
169
+ hostname: string;
170
+ }>];
206
171
  };
207
- export interface PublicationComponentRuntime {
208
- kind?: 'http' | 'static' | 'process';
209
- command?: string[];
210
- port?: number;
211
- healthCheck?: {
172
+ export interface PublicationManagedAddress {
173
+ slug: string;
174
+ url: string;
175
+ }
176
+ export type PublicationRouting = PublicationRoutingDraft & {
177
+ managedAddress: PublicationManagedAddress;
178
+ };
179
+ export type PublicationContainerRuntime = {
180
+ mode: 'container';
181
+ port: number;
182
+ healthCheck: {
212
183
  path: string;
213
184
  };
214
- }
215
- export type PublicationComponentPlacement<TName extends PublicationProviderName = PublicationProviderName> = {
216
- [Name in TName]: {
217
- provider: Name;
218
- } & Partial<PublicationProviderInputs[Name]>;
219
- }[TName];
220
- export interface PublicationApplicationComponentInput<TName extends PublicationProviderName = PublicationProviderName> {
221
- id: string;
222
- kind: PublicationComponentKind;
185
+ command?: string[];
186
+ };
187
+ export type PublicationNativeRuntime = {
188
+ mode: 'provider-native';
189
+ port?: never;
190
+ command?: never;
191
+ healthCheck?: never;
192
+ };
193
+ interface PublicationAppDraftBase {
194
+ name: string;
195
+ kind: 'http';
223
196
  root: string;
224
- build: PublicationComponentBuild;
225
- sourceDependencies?: string[];
226
- runtime?: PublicationComponentRuntime;
227
- resources?: string[];
228
- placement: PublicationComponentPlacement<TName>;
229
- }
230
- export interface PublicationApplicationResourceInput {
197
+ slug?: string;
198
+ routing: PublicationRoutingDraft;
199
+ resourceIds?: string[];
200
+ }
201
+ /** Only executable HTTP workloads are accepted. Runtime and build follow the destination. */
202
+ export type PublicationAppDraft = PublicationAppDraftBase & ({
203
+ destination: {
204
+ provider: 'ssh-server';
205
+ serverId?: string;
206
+ };
207
+ build: PublicationContainerBuild;
208
+ runtime: PublicationContainerRuntime;
209
+ } | {
210
+ destination: {
211
+ provider: 'cloudflare-containers';
212
+ } & Partial<Omit<CloudflareContainersProviderOptions, 'resources'>>;
213
+ build: Omit<PublicationContainerBuild, 'target' | 'arguments' | 'secrets'>;
214
+ runtime: PublicationContainerRuntime;
215
+ } | {
216
+ destination: {
217
+ provider: 'laravel-cloud';
218
+ region?: LaravelCloudProviderInput['region'];
219
+ setup?: Pick<NonNullable<LaravelCloudProviderInput['setup']>, 'repositoryAccessConfirmed' | 'workerPlacement' | 'workerSize'>;
220
+ };
221
+ build: PublicationNativeBuild;
222
+ runtime: PublicationNativeRuntime;
223
+ });
224
+ /** Server-issued ID and managed address remain stable through names and app-count changes. */
225
+ export type PublicationApp = PublicationAppDraft extends infer Draft ? Draft extends PublicationAppDraft ? Omit<Draft, 'slug' | 'routing' | 'resourceIds'> & {
226
+ id: string;
227
+ routing: PublicationRouting;
228
+ resourceIds: string[];
229
+ } : never : never;
230
+ export type PublicationApplicationResourceInput = {
231
231
  id: string;
232
232
  kind: PublicationResourceKind;
233
- mode: PublicationResourceMode;
234
233
  engine?: string;
235
234
  provider?: PublicationProviderName;
236
- selection?: Omit<PublicationResourceSelection, 'mode'>;
235
+ } & ({
236
+ mode: 'none' | 'external' | 'enable';
237
+ selection?: never;
238
+ } | {
239
+ mode: 'reuse';
240
+ selection: {
241
+ id: string;
242
+ };
243
+ } | {
244
+ mode: 'create';
245
+ provider: 'laravel-cloud';
246
+ kind: 'storage';
247
+ selection?: never;
248
+ } | {
249
+ mode: 'create';
250
+ selection: {
251
+ type: string;
252
+ version?: string;
253
+ size?: string;
254
+ config?: Record<string, string | number | boolean | null>;
255
+ };
256
+ });
257
+ export type PublicationConfigurationOperation = {
258
+ op: 'add-app';
259
+ app: PublicationAppDraft;
260
+ adoptPublicationId?: string;
261
+ } | {
262
+ op: 'update-app';
263
+ appId: string;
264
+ app: PublicationAppDraft;
265
+ } | {
266
+ op: 'remove-app';
267
+ appId: string;
268
+ } | {
269
+ op: 'add-resource';
270
+ resource: PublicationApplicationResourceInput;
271
+ } | {
272
+ op: 'update-resource';
273
+ resourceId: string;
274
+ resource: PublicationApplicationResourceInput;
275
+ } | {
276
+ op: 'remove-resource';
277
+ resourceId: string;
278
+ };
279
+ export interface PublicationConfigurationUpdate {
280
+ expectedRevision: number;
281
+ operations: PublicationConfigurationOperation[];
237
282
  }
238
- export interface PublicationApplicationRouteInput {
239
- hostname: string;
240
- component: string;
283
+ export interface PublicationProjectConfiguration {
284
+ schemaVersion: 1;
285
+ proposal: {
286
+ id: string;
287
+ token: string;
288
+ } | null;
289
+ id: string | null;
290
+ revision: number;
291
+ apps: PublicationApp[];
292
+ resources: PublicationApplicationResourceInput[];
293
+ deployments: Array<{
294
+ appId: string | null;
295
+ configured: boolean;
296
+ publication: PublicationData;
297
+ }>;
241
298
  }
242
299
  export interface PublicationApplicationPlanInput {
243
- schemaVersion: 1;
244
- slug?: string;
245
- source?: {
246
- repository?: string;
247
- revision?: string;
248
- root?: string;
249
- };
250
- components: PublicationApplicationComponentInput[];
251
- resources?: PublicationApplicationResourceInput[];
252
- routes?: PublicationApplicationRouteInput[];
300
+ expectedRevision: number;
301
+ appIds?: string[];
253
302
  }
254
- /** A draft becomes executable only through its server-owned review reference. */
255
- export interface PublicationApplicationPlan extends PublicationApplicationPlanInput {
303
+ export interface PublicationApplicationPlan {
304
+ schemaVersion: 1;
305
+ configurationId: string;
306
+ configurationRevision: number;
307
+ apps: Array<PublicationApp & {
308
+ action: 'create' | 'update' | 'retry';
309
+ publicationId: string | null;
310
+ routeAttachment: 'pending_after_deployment' | 'not_required';
311
+ /** Digest fingerprints the reviewed workspace; revision is the last synced Git commit, before native commit-and-push preparation. */
312
+ source: {
313
+ type: 'workspace' | 'git';
314
+ repository: string | null;
315
+ branch: string | null;
316
+ revision: string | null;
317
+ digest: string | null;
318
+ };
319
+ }>;
320
+ resources: PublicationApplicationResourceInput[];
256
321
  review: {
257
322
  id: string;
258
323
  ready: boolean;
@@ -260,30 +325,56 @@ export interface PublicationApplicationPlan extends PublicationApplicationPlanIn
260
325
  code: string;
261
326
  message: string;
262
327
  }>;
263
- action: 'create' | 'update' | 'retry';
264
- publicationId: string | null;
265
328
  destinations: Record<string, string | null>;
266
329
  productionVariables: string[];
267
330
  configuration: PublicationConfigurationField[];
268
331
  source: {
269
- type: 'workspace' | 'git';
332
+ type: 'workspace' | 'git' | 'mixed';
270
333
  repository: string | null;
271
334
  branch: string | null;
335
+ revision: string | null;
336
+ digest: string | null;
272
337
  };
273
338
  digest: string;
339
+ proposal: string;
274
340
  };
275
341
  }
276
342
  export interface PublicationApplicationPublishInput {
277
343
  reviewId: string;
278
- }
279
- export interface PublicationApplicationComponentData {
280
- componentId: string;
344
+ proposal: string;
345
+ appIds?: string[];
346
+ }
347
+ export interface PublicationApplicationConfirmationRequired {
348
+ confirmationRequired: true;
349
+ plan: PublicationApplicationPlan;
350
+ changes: string[];
351
+ }
352
+ export type PublicationApplicationPublishResult = PublicationApplicationData | PublicationApplicationConfirmationRequired;
353
+ export interface PublicationApplicationAppData {
354
+ appId: string;
355
+ configurationRevision: number | null;
281
356
  publication: PublicationData;
357
+ runtime: {
358
+ status: 'healthy' | 'unknown' | 'stopped';
359
+ state: Record<string, unknown>;
360
+ };
361
+ routing: {
362
+ managedAddress: PublicationManagedAddress;
363
+ status: 'connected' | 'pending' | 'failed';
364
+ routes: Array<{
365
+ hostname: string;
366
+ status: string;
367
+ error: string | null;
368
+ }>;
369
+ errors: Record<string, string>;
370
+ };
282
371
  }
283
372
  export interface PublicationApplicationData {
284
373
  id: string;
285
- status: 'publishing' | 'healthy' | 'failed' | 'deleted';
286
- components: PublicationApplicationComponentData[];
374
+ status: 'publishing' | 'healthy' | 'partial' | 'failed' | 'deleted';
375
+ apps: PublicationApplicationAppData[];
376
+ reviewId?: string;
377
+ selectedAppIds?: string[];
287
378
  }
288
379
  export interface PublicationDnsInstructions {
289
380
  stage: 'ownership' | 'traffic' | 'connected';
@@ -311,14 +402,12 @@ export type PublicationProvider<TName extends PublicationProviderName = Publicat
311
402
  name: Name;
312
403
  } & PublicationProviderData[Name];
313
404
  }[TName];
314
- export interface PublishInput<TName extends PublicationProviderName = PublicationProviderName> {
315
- slug: string;
316
- provider: PublicationProviderInput<TName>;
317
- protection?: PublicationProtectionInput;
318
- metadata?: Record<string, unknown>;
319
- }
320
405
  export interface PublicationBuildData<TProvider extends PublicationProviderName = PublicationProviderName> {
321
406
  id: string;
407
+ appId?: string | null;
408
+ configurationRevision?: number | null;
409
+ reviewId?: string | null;
410
+ sourceRevision?: string | null;
322
411
  status: PublicationBuildStatus;
323
412
  strategy: string;
324
413
  provider: PublicationProvider<TProvider>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phpsandbox/sdk",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "The TypeScript SDK for PHPSandbox cloud development environments",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",