@phpsandbox/sdk 0.3.4 → 0.4.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.
package/README.md CHANGED
@@ -55,6 +55,25 @@ PHPSandbox.rest('token', 'https://api.phpsandbox.io/v1', {
55
55
  });
56
56
  ```
57
57
 
58
+ ## Server invitations
59
+
60
+ Server administrators can invite someone by email and inspect the shared invitation state:
61
+
62
+ ```typescript
63
+ const server = await client.servers.get('SERVER_ID');
64
+ const invitation = await server.share({ email: 'person@example.com', requestId: crypto.randomUUID() });
65
+ const invitations = await server.invitations();
66
+
67
+ // The recipient uses their own workspace credential and the proof from their invitation link.
68
+ await recipientClient.servers.acceptInvitation(invitation.id, invitationProof);
69
+ ```
70
+
71
+ Keep the same `requestId` when retrying creation. Use `client.servers.inspectInvitation()`, `acceptInvitation()` and `declineInvitation()` to respond, and `server.resendInvitation()` or `cancelInvitation()` to manage pending invitations. An `accepting` status means access confirmation is still in progress; retry acceptance with the same recipient workspace.
72
+
73
+ For an immediate grant to an existing workspace, use `server.share({ organizationId: 'WORKSPACE_ID', requestId })`.
74
+
75
+ Invitation operations use the same API base URL and credential as all other operations. No additional endpoint configuration is required.
76
+
58
77
  ## Notebook Lifecycle
59
78
 
60
79
  ```ts
@@ -19676,6 +19676,15 @@ var ServerApi = class {
19676
19676
  constructor(client) {
19677
19677
  this.client = client;
19678
19678
  }
19679
+ async inspectInvitation(id, token) {
19680
+ return (await this.client.post(`/platform/server-invitations/${encodeURIComponent(id)}/inspect`, { token })).data;
19681
+ }
19682
+ async acceptInvitation(id, token) {
19683
+ return (await this.client.post(`/platform/server-invitations/${encodeURIComponent(id)}/accept`, { token })).data;
19684
+ }
19685
+ async declineInvitation(id, token) {
19686
+ return (await this.client.post(`/platform/server-invitations/${encodeURIComponent(id)}/decline`, { token })).data;
19687
+ }
19679
19688
  async list(options = {}) {
19680
19689
  const query = options.page === void 0 ? "" : `?${new URLSearchParams({ page: String(options.page) }).toString()}`;
19681
19690
  return this.client.get(`/servers${query}`);
@@ -19701,17 +19710,40 @@ var ServerInstance = class {
19701
19710
  const query = options.page === void 0 ? "" : `?${new URLSearchParams({ page: String(options.page) }).toString()}`;
19702
19711
  return this.client.get(`/servers/${encodeURIComponent(this.data.id)}/members${query}`);
19703
19712
  }
19704
- async share(email3) {
19705
- return (await this.client.post(`/servers/${encodeURIComponent(this.data.id)}/members`, { email: email3 })).data;
19706
- }
19707
- async shareWithOrganization(organizationId) {
19708
- return (await this.client.post(`/servers/${encodeURIComponent(this.data.id)}/members`, { organizationId })).data;
19713
+ async share(target) {
19714
+ if (target.email !== void 0) {
19715
+ return (await this.client.post(`/platform/servers/${encodeURIComponent(this.data.id)}/invitations`, {
19716
+ email: target.email,
19717
+ requestId: target.requestId ?? crypto.randomUUID()
19718
+ })).data;
19719
+ }
19720
+ const body = { organizationId: target.organizationId, requestId: target.requestId };
19721
+ return (await this.client.post(`/servers/${encodeURIComponent(this.data.id)}/members`, body)).data;
19709
19722
  }
19710
19723
  async revokeAccess(memberId) {
19711
19724
  await this.client.delete(
19712
19725
  `/servers/${encodeURIComponent(this.data.id)}/members/${encodeURIComponent(String(memberId))}`
19713
19726
  );
19714
19727
  }
19728
+ async invitations(options = {}) {
19729
+ const query = options.page === void 0 ? "" : `?${new URLSearchParams({ page: String(options.page) }).toString()}`;
19730
+ return this.client.get(`/platform/servers/${encodeURIComponent(this.data.id)}/invitations${query}`);
19731
+ }
19732
+ async cancelInvitation(id) {
19733
+ const response = await this.client.delete(
19734
+ `/platform/servers/${encodeURIComponent(this.data.id)}/invitations/${encodeURIComponent(id)}`
19735
+ );
19736
+ if (response.data === void 0) {
19737
+ throw new PHPSandboxError("Missing invitation cancellation response.");
19738
+ }
19739
+ return response.data;
19740
+ }
19741
+ async resendInvitation(id) {
19742
+ return (await this.client.post(
19743
+ `/platform/servers/${encodeURIComponent(this.data.id)}/invitations/${encodeURIComponent(id)}/resend`,
19744
+ {}
19745
+ )).data;
19746
+ }
19715
19747
  refresh() {
19716
19748
  return this.client.servers.get(this.data.id);
19717
19749
  }
@@ -20120,6 +20152,280 @@ function sandboxIntegrationId(integration) {
20120
20152
  return typeof integration === "string" ? integration : integration.id;
20121
20153
  }
20122
20154
 
20155
+ // src/payments.ts
20156
+ var DEFAULT_PAYMENT_CALLBACK_PATH = "/api/phpsandbox-payments/events";
20157
+ var paymentModeSchema = external_exports.enum(["test", "live"]);
20158
+ var accountSchema = external_exports.object({
20159
+ id: external_exports.string(),
20160
+ mode: paymentModeSchema,
20161
+ chargesEnabled: external_exports.boolean(),
20162
+ payoutsEnabled: external_exports.boolean(),
20163
+ detailsSubmitted: external_exports.boolean(),
20164
+ requirements: external_exports.array(external_exports.string()),
20165
+ disabledReason: external_exports.string().nullable()
20166
+ });
20167
+ var accessSchema = external_exports.object({
20168
+ subscribed: external_exports.boolean(),
20169
+ liveAllowed: external_exports.boolean(),
20170
+ testConfigured: external_exports.boolean(),
20171
+ liveConfigured: external_exports.boolean(),
20172
+ feeBasisPoints: external_exports.number().int().min(0).max(1e4).nullable(),
20173
+ canRead: external_exports.boolean(),
20174
+ canManage: external_exports.boolean(),
20175
+ canRefund: external_exports.boolean(),
20176
+ recoveryEndsAt: external_exports.string().nullable().default(null)
20177
+ });
20178
+ var applicationsSchema = external_exports.object({
20179
+ notebookId: external_exports.string(),
20180
+ applications: external_exports.array(
20181
+ external_exports.object({
20182
+ id: external_exports.string(),
20183
+ url: external_exports.string().nullable(),
20184
+ healthy: external_exports.boolean(),
20185
+ passwordProtected: external_exports.boolean(),
20186
+ deployedAt: external_exports.string().datetime().nullable()
20187
+ })
20188
+ )
20189
+ });
20190
+ var bindingSchema = external_exports.object({
20191
+ id: external_exports.string(),
20192
+ notebookId: external_exports.string(),
20193
+ applicationId: external_exports.string().nullable(),
20194
+ mode: paymentModeSchema,
20195
+ revision: external_exports.number().int(),
20196
+ accountId: external_exports.string(),
20197
+ enabled: external_exports.boolean(),
20198
+ callbackUrl: external_exports.string().nullable(),
20199
+ originUrl: external_exports.string().nullable(),
20200
+ runtimeConfigured: external_exports.boolean()
20201
+ });
20202
+ var priceSchema = external_exports.object({
20203
+ billingInterval: external_exports.enum(["month", "year"]).nullable().default(null),
20204
+ id: external_exports.string(),
20205
+ name: external_exports.string(),
20206
+ amount: external_exports.number().int(),
20207
+ currency: external_exports.string(),
20208
+ bindingId: external_exports.string()
20209
+ });
20210
+ var transactionSchema = external_exports.object({
20211
+ subscriptionId: external_exports.string().nullable().default(null),
20212
+ periodStart: external_exports.string().datetime({ offset: true }).nullable().default(null),
20213
+ periodEnd: external_exports.string().datetime({ offset: true }).nullable().default(null),
20214
+ id: external_exports.string(),
20215
+ bindingId: external_exports.string(),
20216
+ orderId: external_exports.string(),
20217
+ priceId: external_exports.string(),
20218
+ amount: external_exports.number().int(),
20219
+ currency: external_exports.string(),
20220
+ feeAmount: external_exports.number().int(),
20221
+ revision: external_exports.number().int(),
20222
+ refundedAmount: external_exports.number().int(),
20223
+ status: external_exports.enum(["pending", "paid", "failed", "refunded", "partially_refunded"]),
20224
+ checkoutUrl: external_exports.string().nullable(),
20225
+ deliveredAt: external_exports.string().nullable()
20226
+ });
20227
+ var subscriptionSchema = external_exports.object({
20228
+ id: external_exports.string(),
20229
+ bindingId: external_exports.string(),
20230
+ priceId: external_exports.string(),
20231
+ orderId: external_exports.string(),
20232
+ status: external_exports.enum([
20233
+ "pending",
20234
+ "incomplete",
20235
+ "incomplete_expired",
20236
+ "trialing",
20237
+ "active",
20238
+ "past_due",
20239
+ "canceled",
20240
+ "unpaid",
20241
+ "paused"
20242
+ ]),
20243
+ billingInterval: external_exports.enum(["month", "year"]),
20244
+ paidThrough: external_exports.string().datetime({ offset: true }).nullable(),
20245
+ cancelAtPeriodEnd: external_exports.boolean(),
20246
+ checkoutUrl: external_exports.string().nullable(),
20247
+ revision: external_exports.number().int()
20248
+ });
20249
+ var portalSchema = external_exports.object({ url: external_exports.string().url() });
20250
+ var refundResultSchema = transactionSchema.extend({
20251
+ refund: external_exports.object({
20252
+ id: external_exports.string(),
20253
+ amount: external_exports.number().int().positive(),
20254
+ status: external_exports.enum(["pending", "succeeded", "failed", "canceled", "requires_action"])
20255
+ })
20256
+ });
20257
+ var activationSchema = external_exports.object({
20258
+ revision: external_exports.number().int(),
20259
+ reviewId: external_exports.string(),
20260
+ expiresAt: external_exports.string(),
20261
+ blockers: external_exports.array(external_exports.string()),
20262
+ binding: bindingSchema,
20263
+ account: accountSchema,
20264
+ feeBasisPoints: external_exports.number().int().nullable(),
20265
+ prices: external_exports.array(priceSchema)
20266
+ });
20267
+ var PaymentsApi = class {
20268
+ constructor(client) {
20269
+ this.client = client;
20270
+ this.runtime = new PaymentRuntime(client);
20271
+ }
20272
+ forNotebook(notebookId) {
20273
+ return new NotebookPayments(notebookId, this.client);
20274
+ }
20275
+ async applications(notebookId) {
20276
+ return applicationsSchema.parse(
20277
+ (await this.client.get(`/notebook/${encodeURIComponent(notebookId)}/payments/applications`)).data
20278
+ );
20279
+ }
20280
+ async access() {
20281
+ return accessSchema.parse((await this.client.get("/platform/payments/access")).data);
20282
+ }
20283
+ async accounts() {
20284
+ return external_exports.array(accountSchema).parse((await this.client.get("/platform/payments/accounts")).data);
20285
+ }
20286
+ async createAccount(mode, country) {
20287
+ return accountSchema.parse((await this.client.put(`/platform/payments/accounts/${mode}`, { country })).data);
20288
+ }
20289
+ async onboarding(mode) {
20290
+ return external_exports.object({ url: external_exports.string().url(), expiresAt: external_exports.string() }).parse((await this.client.post(`/platform/payments/accounts/${mode}/onboarding`, {})).data);
20291
+ }
20292
+ };
20293
+ var NotebookPayments = class {
20294
+ constructor(notebookId, client) {
20295
+ this.notebookId = notebookId;
20296
+ this.client = client;
20297
+ }
20298
+ path(mode) {
20299
+ return `/platform/payments/sandboxes/${encodeURIComponent(this.notebookId)}/${mode}`;
20300
+ }
20301
+ async get(mode = "test") {
20302
+ return bindingSchema.nullable().parse((await this.client.get(this.path(mode))).data);
20303
+ }
20304
+ async configure(input, mode = "test") {
20305
+ return bindingSchema.parse((await this.client.put(this.path(mode), input)).data);
20306
+ }
20307
+ async provision(mode = "test") {
20308
+ return bindingSchema.parse((await this.client.post(`${this.path(mode)}/provision`, {})).data);
20309
+ }
20310
+ async prices(mode = "test") {
20311
+ return external_exports.array(priceSchema).parse((await this.client.get(`${this.path(mode)}/prices`)).data);
20312
+ }
20313
+ async createPrice(input, mode = "test") {
20314
+ return priceSchema.parse((await this.client.post(`${this.path(mode)}/prices`, input)).data);
20315
+ }
20316
+ async prepareActivation(mode = "test") {
20317
+ return activationSchema.parse((await this.client.post(`${this.path(mode)}/activation`, {})).data);
20318
+ }
20319
+ async activate(reviewId, mode = "test") {
20320
+ return bindingSchema.parse((await this.client.post(`${this.path(mode)}/activate`, { reviewId })).data);
20321
+ }
20322
+ async disable(mode = "test") {
20323
+ return bindingSchema.parse((await this.client.post(`${this.path(mode)}/disable`, {})).data);
20324
+ }
20325
+ async checkout(input, mode = "test") {
20326
+ return transactionSchema.parse((await this.client.post(`${this.path(mode)}/checkouts`, input)).data);
20327
+ }
20328
+ async subscribe(input, mode = "test") {
20329
+ return subscriptionSchema.parse((await this.client.post(`${this.path(mode)}/subscriptions`, input)).data);
20330
+ }
20331
+ async subscriptions(mode = "test") {
20332
+ return external_exports.array(subscriptionSchema).parse((await this.client.get(`${this.path(mode)}/subscriptions`)).data);
20333
+ }
20334
+ async subscription(id, mode = "test") {
20335
+ return subscriptionSchema.parse(
20336
+ (await this.client.get(`${this.path(mode)}/subscriptions/${encodeURIComponent(id)}`)).data
20337
+ );
20338
+ }
20339
+ async cancelSubscription(id, mode = "test") {
20340
+ return subscriptionSchema.parse(
20341
+ (await this.client.post(`${this.path(mode)}/subscriptions/${encodeURIComponent(id)}/cancel`, {})).data
20342
+ );
20343
+ }
20344
+ async subscriptionPortal(id, input, mode = "test") {
20345
+ return portalSchema.parse(
20346
+ (await this.client.post(`${this.path(mode)}/subscriptions/${encodeURIComponent(id)}/portal`, input)).data
20347
+ );
20348
+ }
20349
+ async transactions(mode = "test") {
20350
+ return external_exports.array(transactionSchema).parse((await this.client.get(`${this.path(mode)}/transactions`)).data);
20351
+ }
20352
+ async refund(id, input, mode = "test") {
20353
+ return refundResultSchema.parse(
20354
+ (await this.client.post(`${this.path(mode)}/transactions/${encodeURIComponent(id)}/refund`, input)).data
20355
+ );
20356
+ }
20357
+ };
20358
+ var paymentEventSchema = external_exports.discriminatedUnion("type", [
20359
+ external_exports.object({
20360
+ id: external_exports.string(),
20361
+ type: external_exports.literal("payment.updated"),
20362
+ mode: paymentModeSchema,
20363
+ transaction: transactionSchema
20364
+ }),
20365
+ external_exports.object({
20366
+ id: external_exports.string(),
20367
+ type: external_exports.literal("subscription.updated"),
20368
+ mode: paymentModeSchema,
20369
+ subscription: subscriptionSchema
20370
+ })
20371
+ ]);
20372
+ async function verifyPaymentEvent(body, signature, token, mode, now = Date.now()) {
20373
+ if (!/^hpay_[a-f0-9]{64}$/.test(token)) {
20374
+ throw new Error("Invalid application payment credential.");
20375
+ }
20376
+ const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(signature);
20377
+ const timestamp = match?.[1];
20378
+ const digest = match?.[2];
20379
+ if (!timestamp || !digest || Math.abs(now - Number(timestamp) * 1e3) > 3e5 || body.length > 262144) {
20380
+ throw new Error("Invalid or expired payment signature.");
20381
+ }
20382
+ const encoder = new TextEncoder();
20383
+ const key = await crypto.subtle.importKey("raw", encoder.encode(token), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
20384
+ const bytes = Uint8Array.from(digest.match(/../g) ?? [], (byte) => Number.parseInt(byte, 16));
20385
+ if (!await crypto.subtle.verify("HMAC", key, bytes, encoder.encode(`${timestamp}.${body}`))) {
20386
+ throw new Error("Invalid payment signature.");
20387
+ }
20388
+ const event = paymentEventSchema.parse(JSON.parse(body));
20389
+ if (event.mode !== mode) {
20390
+ throw new Error("Payment event mode does not match this application.");
20391
+ }
20392
+ return event;
20393
+ }
20394
+ var PaymentRuntime = class {
20395
+ constructor(client) {
20396
+ this.client = client;
20397
+ }
20398
+ async checkout(input) {
20399
+ return transactionSchema.parse((await this.client.post("/platform/payments/runtime/checkouts", input)).data);
20400
+ }
20401
+ async transaction(id) {
20402
+ return transactionSchema.parse(
20403
+ (await this.client.get(`/platform/payments/runtime/transactions/${encodeURIComponent(id)}`)).data
20404
+ );
20405
+ }
20406
+ async subscribe(input) {
20407
+ return subscriptionSchema.parse((await this.client.post("/platform/payments/runtime/subscriptions", input)).data);
20408
+ }
20409
+ async subscription(id) {
20410
+ return subscriptionSchema.parse(
20411
+ (await this.client.get(`/platform/payments/runtime/subscriptions/${encodeURIComponent(id)}`)).data
20412
+ );
20413
+ }
20414
+ async cancelSubscription(id) {
20415
+ return subscriptionSchema.parse(
20416
+ (await this.client.post(`/platform/payments/runtime/subscriptions/${encodeURIComponent(id)}/cancel`, {})).data
20417
+ );
20418
+ }
20419
+ async subscriptionPortal(id, input) {
20420
+ return portalSchema.parse(
20421
+ (await this.client.post(`/platform/payments/runtime/subscriptions/${encodeURIComponent(id)}/portal`, input)).data
20422
+ );
20423
+ }
20424
+ async prices() {
20425
+ return external_exports.array(priceSchema).parse((await this.client.get("/platform/payments/runtime/prices")).data);
20426
+ }
20427
+ };
20428
+
20123
20429
  // src/index.ts
20124
20430
  function normalizeNotebookSecretInputs(input) {
20125
20431
  if (Array.isArray(input)) {
@@ -20170,6 +20476,7 @@ var ClientImplementation = class {
20170
20476
  this.notebook = new NotebookApi(this);
20171
20477
  this.servers = new ServerApi(this);
20172
20478
  this.integrations = new IntegrationApi(this);
20479
+ this.payments = new PaymentsApi(this);
20173
20480
  this.options = { ...options };
20174
20481
  this.runtimeTransportValue = runtimeTransport;
20175
20482
  this.baseUrl = normalizeApiBaseUrl(url2);
@@ -20250,7 +20557,7 @@ var ClientImplementation = class {
20250
20557
  })
20251
20558
  );
20252
20559
  if (!response.ok) {
20253
- throw await responseError(response);
20560
+ throw await responseError(response, path.startsWith("/platform/") ? "platform" : "core");
20254
20561
  }
20255
20562
  if (response.status === 204) {
20256
20563
  return { data: void 0 };
@@ -20283,10 +20590,10 @@ var ClientImplementation = class {
20283
20590
  }
20284
20591
  }
20285
20592
  };
20286
- async function responseError(response) {
20593
+ async function responseError(response, source = "core") {
20287
20594
  const body = await response.text();
20288
20595
  try {
20289
- return remoteError("core", errorPayload(body === "" ? void 0 : JSON.parse(body)), response);
20596
+ return remoteError(source, errorPayload(body === "" ? void 0 : JSON.parse(body)), response);
20290
20597
  } catch {
20291
20598
  return new TransportError(
20292
20599
  `PHPSandbox API returned an invalid error response (HTTP ${response.status}).`,
@@ -20310,6 +20617,7 @@ var PHPSandbox = class _PHPSandbox {
20310
20617
  this.publications = client.publications;
20311
20618
  this.servers = client.servers;
20312
20619
  this.integrations = client.integrations;
20620
+ this.payments = client.payments;
20313
20621
  }
20314
20622
  async whoami() {
20315
20623
  const { data } = await this.client.get("/auth/whoami");
@@ -20376,6 +20684,7 @@ var _NotebookInstance = class _NotebookInstance {
20376
20684
  this.config = new Config(this);
20377
20685
  this.secrets = new NotebookSecrets(client, this.data.id);
20378
20686
  this.integrations = new SandboxIntegrationApi(this.data.id, client);
20687
+ this.payments = new NotebookPayments(this.data.id, client);
20379
20688
  this.preview = new NotebookPreview(client, this.data.id);
20380
20689
  this.mail = new NotebookMail(client, this.data.id);
20381
20690
  this.feedback = new Feedback(client, this.data.id);
@@ -20708,14 +21017,18 @@ function formatQueryString(params) {
20708
21017
  return query === "" ? "" : `?${query}`;
20709
21018
  }
20710
21019
  export {
21020
+ DEFAULT_PAYMENT_CALLBACK_PATH,
20711
21021
  FileChangeFilter,
20712
21022
  FileChangeType,
20713
21023
  FileType,
20714
21024
  LspConnection,
20715
21025
  NotebookInstance,
21026
+ NotebookPayments,
20716
21027
  NotebookPublication,
20717
21028
  PHPSandbox,
20718
21029
  PHPSandboxError,
21030
+ PaymentRuntime,
21031
+ PaymentsApi,
20719
21032
  PublicationApi,
20720
21033
  PublicationDomains,
20721
21034
  PublicationInstance,
@@ -20727,7 +21040,9 @@ export {
20727
21040
  laravelCloudSetupForRepublish,
20728
21041
  notebookBuiltinServices,
20729
21042
  notebookKnownServices,
20730
- readPublicationStream
21043
+ paymentModeSchema,
21044
+ readPublicationStream,
21045
+ verifyPaymentEvent
20731
21046
  };
20732
21047
  /*! Bundled license information:
20733
21048