@comfyorg/account-core 1.0.0-alpha.2 → 1.0.0-alpha.3

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 (69) hide show
  1. package/dist/core/billing/index.d.ts +4 -3
  2. package/dist/core/billing/index.js +2 -1
  3. package/dist/core/billing/operationLifecycle.d.ts +26 -4
  4. package/dist/core/billing/operationLifecycle.js +113 -14
  5. package/dist/core/billing/operationPointer.d.ts +24 -0
  6. package/dist/core/billing/operationPointer.js +14 -1
  7. package/dist/core/billing/operationState.d.ts +18 -6
  8. package/dist/core/billing/operationState.js +13 -15
  9. package/dist/core/billing/paymentFriction.d.ts +17 -0
  10. package/dist/core/billing/paymentFriction.js +41 -0
  11. package/dist/core/billing/subscriptionCommands.d.ts +38 -2
  12. package/dist/core/billing/subscriptionCommands.js +27 -7
  13. package/dist/core/billing/subscriptionDiscount.d.ts +26 -0
  14. package/dist/core/billing/subscriptionDiscount.js +6 -0
  15. package/dist/core/billing/telemetry/billingTelemetryEvent.d.ts +25 -0
  16. package/dist/core/billing/telemetry/billingTelemetryEvent.js +3 -0
  17. package/dist/core/billing/telemetry/cancelEvent.d.ts +19 -0
  18. package/dist/core/billing/telemetry/cancelEvent.js +1 -0
  19. package/dist/core/billing/telemetry/capabilityReadEvent.d.ts +4 -0
  20. package/dist/core/billing/telemetry/capabilityReadEvent.js +1 -0
  21. package/dist/core/billing/telemetry/checkoutChallengeEvent.d.ts +25 -0
  22. package/dist/core/billing/telemetry/checkoutChallengeEvent.js +1 -0
  23. package/dist/core/billing/telemetry/checkoutJourney.d.ts +176 -0
  24. package/dist/core/billing/telemetry/checkoutJourney.js +115 -0
  25. package/dist/core/billing/telemetry/checkoutRedirectEvent.d.ts +30 -0
  26. package/dist/core/billing/telemetry/checkoutRedirectEvent.js +1 -0
  27. package/dist/core/billing/telemetry/downgradeToPersonalEvent.d.ts +14 -0
  28. package/dist/core/billing/telemetry/downgradeToPersonalEvent.js +1 -0
  29. package/dist/core/billing/telemetry/entryEvent.d.ts +16 -0
  30. package/dist/core/billing/telemetry/entryEvent.js +1 -0
  31. package/dist/core/billing/telemetry/eventNames.d.ts +36 -0
  32. package/dist/core/billing/telemetry/eventNames.js +36 -0
  33. package/dist/core/billing/telemetry/index.d.ts +17 -0
  34. package/dist/core/billing/telemetry/index.js +6 -0
  35. package/dist/core/billing/telemetry/operationEvent.d.ts +25 -0
  36. package/dist/core/billing/telemetry/operationEvent.js +1 -0
  37. package/dist/core/billing/telemetry/operationLifecycleEvent.d.ts +9 -0
  38. package/dist/core/billing/telemetry/operationLifecycleEvent.js +79 -0
  39. package/dist/core/billing/telemetry/payload.d.ts +55 -0
  40. package/dist/core/billing/telemetry/payload.js +75 -0
  41. package/dist/core/billing/telemetry/portalEvent.d.ts +23 -0
  42. package/dist/core/billing/telemetry/portalEvent.js +1 -0
  43. package/dist/core/billing/telemetry/resubscribeEvent.d.ts +13 -0
  44. package/dist/core/billing/telemetry/resubscribeEvent.js +1 -0
  45. package/dist/core/billing/telemetry/stages.d.ts +45 -0
  46. package/dist/core/billing/telemetry/stages.js +1 -0
  47. package/dist/core/billing/telemetry/subscriptionCheckoutEvent.d.ts +23 -0
  48. package/dist/core/billing/telemetry/subscriptionCheckoutEvent.js +1 -0
  49. package/dist/core/billing/telemetry/topupEvent.d.ts +30 -0
  50. package/dist/core/billing/telemetry/topupEvent.js +6 -0
  51. package/dist/core/billing/telemetry/vocabulary.d.ts +9 -0
  52. package/dist/core/billing/telemetry/vocabulary.js +1 -0
  53. package/dist/core/billing/telemetry/webEntryEvent.d.ts +27 -0
  54. package/dist/core/billing/telemetry/webEntryEvent.js +1 -0
  55. package/dist/core/billing/telemetry/webHandoffEvent.d.ts +11 -0
  56. package/dist/core/billing/telemetry/webHandoffEvent.js +1 -0
  57. package/dist/core/billing/telemetry/webReturnEvent.d.ts +8 -0
  58. package/dist/core/billing/telemetry/webReturnEvent.js +1 -0
  59. package/dist/core/billing/telemetry/webSessionEvent.d.ts +17 -0
  60. package/dist/core/billing/telemetry/webSessionEvent.js +1 -0
  61. package/dist/core/webSession.d.ts +4 -3
  62. package/dist/core/webSession.js +6 -5
  63. package/dist/core/webSessionIdentity.d.ts +6 -6
  64. package/dist/signInSchemas.d.ts +1 -1
  65. package/dist/telemetry.d.ts +26 -0
  66. package/dist/telemetry.js +28 -7
  67. package/dist/testing.d.ts +2 -1
  68. package/dist/testing.js +5 -4
  69. package/package.json +3 -1
@@ -0,0 +1,11 @@
1
+ import type { HostedBillingIntent, PaymentIntentSource } from './vocabulary.js';
2
+ export type WebHandoffBillingEvent = {
3
+ operation: 'web_handoff';
4
+ stage: 'opened';
5
+ outcome: 'pending';
6
+ intent: HostedBillingIntent;
7
+ result: 'opened' | 'blocked';
8
+ payment_intent_source?: PaymentIntentSource;
9
+ /** The cloud journey id the entry link carries as `correlation_id`. */
10
+ correlation_id: string;
11
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ /** Back from a checkout step, the frame's close, the finished checkout's close, or the surface's link to the host. */
2
+ export type WebReturnControl = 'back' | 'close' | 'success_close' | 'host_link';
3
+ export type WebReturnBillingEvent = {
4
+ operation: 'web_return';
5
+ stage: 'clicked';
6
+ outcome: 'pending';
7
+ control: WebReturnControl;
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { SessionErrorCode } from '../../sessionContracts.js';
2
+ export type WebSessionMode = 'session-client' | 'web-session';
3
+ export type WebSessionBillingEvent = {
4
+ operation: 'web_session';
5
+ outcome: 'pending';
6
+ } & ({
7
+ stage: 'signin_required';
8
+ reason: 'no_session' | 'refused';
9
+ } | {
10
+ stage: 'established';
11
+ /** `interactive` once the customer signed in on billing web itself; `restored` when a session was already there. */
12
+ origin: 'restored' | 'interactive';
13
+ mode: WebSessionMode;
14
+ } | {
15
+ stage: 'failed';
16
+ error_code: SessionErrorCode;
17
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -21,7 +21,8 @@ export declare function createWebSession(options: WebSessionOptions, getIdentity
21
21
  /** Sends no CSRF token: sign-out must work against a dead session. */
22
22
  export declare function deleteWebSession(options: WebSessionOptions): Promise<WebSessionCommandResult>;
23
23
  /**
24
- * Signs the user out of every device. Only a body the contract recognises is
25
- * ok: the caller tells the user every device is signed out on that answer.
24
+ * Signs the user out of every device with the session cookie alone. Only a
25
+ * body the contract recognises is ok: the caller tells the user every device
26
+ * is signed out on that answer.
26
27
  */
27
- export declare function revokeAllWebSessions(options: WebSessionOptions, csrfToken: string, getIdentityProof: () => Promise<string>): Promise<WebSessionCommandResult>;
28
+ export declare function revokeAllWebSessions(options: WebSessionOptions, csrfToken: string): Promise<WebSessionCommandResult>;
@@ -4,6 +4,7 @@
4
4
  * Identity proof is an opaque string from the caller, so no provider leaks in.
5
5
  */
6
6
  import { zCreateSessionResponse, zDeleteSessionResponse, zErrorResponse, zGetSessionResponse, zRevokeAllSessionsResponse } from '@comfyorg/ingest-types/zod';
7
+ import { COMFY_CLIENT } from './requestAuth.js';
7
8
  import { timedSignal } from './requestTimeout.js';
8
9
  const UNAUTHORIZED_CODES = {
9
10
  no_session: 'NO_SESSION',
@@ -125,14 +126,14 @@ export async function deleteWebSession(options) {
125
126
  return { status: 'ok' };
126
127
  }
127
128
  /**
128
- * Signs the user out of every device. Only a body the contract recognises is
129
- * ok: the caller tells the user every device is signed out on that answer.
129
+ * Signs the user out of every device with the session cookie alone. Only a
130
+ * body the contract recognises is ok: the caller tells the user every device
131
+ * is signed out on that answer.
130
132
  */
131
- export async function revokeAllWebSessions(options, csrfToken, getIdentityProof) {
132
- const proof = await getIdentityProof();
133
+ export async function revokeAllWebSessions(options, csrfToken) {
133
134
  const sent = await send(options, '/auth/sessions/revoke-all', {
134
135
  method: 'POST',
135
- headers: { Authorization: `Bearer ${proof}`, 'X-CSRF-Token': csrfToken }
136
+ headers: { 'X-Comfy-Client': COMFY_CLIENT, 'X-CSRF-Token': csrfToken }
136
137
  });
137
138
  if ('code' in sent)
138
139
  return sent;
@@ -187,8 +187,8 @@ declare const zSharedMessage: z.ZodObject<{
187
187
  name?: string | undefined;
188
188
  signInProvider?: string | undefined;
189
189
  };
190
- expiresAt: number;
191
190
  csrfToken: string;
191
+ expiresAt: number;
192
192
  absoluteExpiresAt: number;
193
193
  }, {
194
194
  user: {
@@ -198,8 +198,8 @@ declare const zSharedMessage: z.ZodObject<{
198
198
  name?: string | undefined;
199
199
  signInProvider?: string | undefined;
200
200
  };
201
- expiresAt: number;
202
201
  csrfToken: string;
202
+ expiresAt: number;
203
203
  absoluteExpiresAt: number;
204
204
  }>;
205
205
  }, "strip", z.ZodTypeAny, {
@@ -212,8 +212,8 @@ declare const zSharedMessage: z.ZodObject<{
212
212
  name?: string | undefined;
213
213
  signInProvider?: string | undefined;
214
214
  };
215
- expiresAt: number;
216
215
  csrfToken: string;
216
+ expiresAt: number;
217
217
  absoluteExpiresAt: number;
218
218
  };
219
219
  }, {
@@ -226,8 +226,8 @@ declare const zSharedMessage: z.ZodObject<{
226
226
  name?: string | undefined;
227
227
  signInProvider?: string | undefined;
228
228
  };
229
- expiresAt: number;
230
229
  csrfToken: string;
230
+ expiresAt: number;
231
231
  absoluteExpiresAt: number;
232
232
  };
233
233
  }>, z.ZodObject<{
@@ -254,8 +254,8 @@ declare const zSharedMessage: z.ZodObject<{
254
254
  name?: string | undefined;
255
255
  signInProvider?: string | undefined;
256
256
  };
257
- expiresAt: number;
258
257
  csrfToken: string;
258
+ expiresAt: number;
259
259
  absoluteExpiresAt: number;
260
260
  };
261
261
  } | {
@@ -275,8 +275,8 @@ declare const zSharedMessage: z.ZodObject<{
275
275
  name?: string | undefined;
276
276
  signInProvider?: string | undefined;
277
277
  };
278
- expiresAt: number;
279
278
  csrfToken: string;
279
+ expiresAt: number;
280
280
  absoluteExpiresAt: number;
281
281
  };
282
282
  } | {
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  /**
3
3
  * The exact message keys the schemas emit, so a host wires every one of them
4
4
  * or the compiler says which is missing. Both hosts already carry these keys:
5
- * the app in `src/locales/en/main.json`, the website in its translations map.
5
+ * the app in `src/locales/en/main.json`, the website in `apps/website/src/locales/en/main.json`.
6
6
  */
7
7
  export type AuthSchemaMessageKey = 'validation.prefix' | 'validation.length' | 'validation.invalidEmail' | 'validation.required' | 'validation.minLength' | 'validation.maxLength' | 'validation.password.uppercase' | 'validation.password.lowercase' | 'validation.password.number' | 'validation.password.special' | 'validation.password.match';
8
8
  export type AuthSchemaTranslate = (key: AuthSchemaMessageKey, params?: Record<string, string | number>) => string;
@@ -4,10 +4,24 @@
4
4
  * every host. The package never calls a telemetry API itself; call sites
5
5
  * stay host-specific.
6
6
  */
7
+ import type { WebSessionBootstrapEvent, WebSessionIdentityOptions } from './core/webSessionIdentity.js';
7
8
  export declare const SESSION_TELEMETRY_EVENT: {
8
9
  readonly refreshSucceeded: "auth.unified.refresh.succeeded";
9
10
  readonly refreshFailed: "auth.unified.refresh.failed";
11
+ readonly bootstrap: "session_bootstrap";
12
+ readonly signedOutRemotely: "session_signed_out_remotely";
10
13
  };
14
+ export type WebSessionTelemetryEvent = {
15
+ readonly name: typeof SESSION_TELEMETRY_EVENT.bootstrap;
16
+ readonly properties: WebSessionBootstrapEvent;
17
+ } | {
18
+ readonly name: typeof SESSION_TELEMETRY_EVENT.signedOutRemotely;
19
+ readonly properties: {
20
+ readonly origin: string;
21
+ };
22
+ };
23
+ /** The web-session identity's telemetry hooks, routed to one host sink. */
24
+ export declare function webSessionTelemetryHooks(track: (event: WebSessionTelemetryEvent) => void): Pick<WebSessionIdentityOptions, 'onBootstrap' | 'onSignedOutRemotely'>;
11
25
  /**
12
26
  * The cloud app's `billing.operation.*` events, emitted by the billing
13
27
  * operation lifecycle for every observed `billing_op_id` regardless of
@@ -19,6 +33,18 @@ export declare const BILLING_OPERATION_TELEMETRY_EVENT: {
19
33
  readonly failed: "billing.operation.failed";
20
34
  readonly timeout: "billing.operation.timeout";
21
35
  };
36
+ /**
37
+ * Payment friction the lifecycle observes inside one operation: a bank
38
+ * challenge and its verdict, each retryable decline, and a hosted step the
39
+ * customer was sent to and came back from.
40
+ */
41
+ export declare const BILLING_CHECKOUT_FRICTION_TELEMETRY_EVENT: {
42
+ readonly challengeRequired: "billing.checkout.challenge_required";
43
+ readonly challengeCompleted: "billing.checkout.challenge_completed";
44
+ readonly challengeFailed: "billing.checkout.challenge_failed";
45
+ readonly redirectStarted: "billing.checkout.redirect_started";
46
+ readonly returned: "billing.checkout.returned";
47
+ };
22
48
  export declare const AUTH_TELEMETRY_EVENT: {
23
49
  readonly signUpOpened: "app:user_sign_up_opened";
24
50
  readonly authCompleted: "app:user_auth_completed";
package/dist/telemetry.js CHANGED
@@ -1,13 +1,22 @@
1
- /**
2
- * Shared telemetry vocabulary. The event names match the cloud app's
3
- * TelemetryEvents verbatim so an auth outcome is one queryable event across
4
- * every host. The package never calls a telemetry API itself; call sites
5
- * stay host-specific.
6
- */
7
1
  export const SESSION_TELEMETRY_EVENT = {
8
2
  refreshSucceeded: 'auth.unified.refresh.succeeded',
9
- refreshFailed: 'auth.unified.refresh.failed'
3
+ refreshFailed: 'auth.unified.refresh.failed',
4
+ bootstrap: 'session_bootstrap',
5
+ signedOutRemotely: 'session_signed_out_remotely'
10
6
  };
7
+ /** The web-session identity's telemetry hooks, routed to one host sink. */
8
+ export function webSessionTelemetryHooks(track) {
9
+ return {
10
+ onBootstrap: ({ outcome, origin }) => track({
11
+ name: SESSION_TELEMETRY_EVENT.bootstrap,
12
+ properties: { outcome, origin }
13
+ }),
14
+ onSignedOutRemotely: ({ origin }) => track({
15
+ name: SESSION_TELEMETRY_EVENT.signedOutRemotely,
16
+ properties: { origin }
17
+ })
18
+ };
19
+ }
11
20
  /**
12
21
  * The cloud app's `billing.operation.*` events, emitted by the billing
13
22
  * operation lifecycle for every observed `billing_op_id` regardless of
@@ -19,6 +28,18 @@ export const BILLING_OPERATION_TELEMETRY_EVENT = {
19
28
  failed: 'billing.operation.failed',
20
29
  timeout: 'billing.operation.timeout'
21
30
  };
31
+ /**
32
+ * Payment friction the lifecycle observes inside one operation: a bank
33
+ * challenge and its verdict, each retryable decline, and a hosted step the
34
+ * customer was sent to and came back from.
35
+ */
36
+ export const BILLING_CHECKOUT_FRICTION_TELEMETRY_EVENT = {
37
+ challengeRequired: 'billing.checkout.challenge_required',
38
+ challengeCompleted: 'billing.checkout.challenge_completed',
39
+ challengeFailed: 'billing.checkout.challenge_failed',
40
+ redirectStarted: 'billing.checkout.redirect_started',
41
+ returned: 'billing.checkout.returned'
42
+ };
22
43
  export const AUTH_TELEMETRY_EVENT = {
23
44
  signUpOpened: 'app:user_sign_up_opened',
24
45
  authCompleted: 'app:user_auth_completed',
package/dist/testing.d.ts CHANGED
@@ -36,7 +36,8 @@ export interface FakeWebSessionEndpoint {
36
36
  /**
37
37
  * A `fetch` serving ingest's session routes under `/api/auth`. POST with a
38
38
  * bearer proof signs `signInUser` in; DELETE and revoke-all leave the cookie
39
- * revoked, as the real endpoint does.
39
+ * revoked, as the real endpoint does. Revoke-all serves only the cookie path,
40
+ * which needs the CSRF token and `X-Comfy-Client`.
40
41
  */
41
42
  export declare function createFakeWebSessionEndpoint({ state, signInUser, now }: {
42
43
  state: FakeWebSessionState;
package/dist/testing.js CHANGED
@@ -67,15 +67,15 @@ function routesFor(signInUser, now) {
67
67
  [
68
68
  'POST /api/auth/sessions/revoke-all',
69
69
  (endpoint, headers) => {
70
- if (!headers.get('authorization')?.startsWith('Bearer ')) {
71
- return errorResponse(401, 'no_session');
72
- }
73
70
  if (endpoint.state.kind === 'dead') {
74
71
  return errorResponse(401, endpoint.state.code);
75
72
  }
76
73
  if (headers.get('x-csrf-token') !== FAKE_CSRF_TOKEN) {
77
74
  return errorResponse(403, 'csrf_invalid');
78
75
  }
76
+ if (!headers.has('x-comfy-client')) {
77
+ return errorResponse(403, 'origin_not_allowed');
78
+ }
79
79
  revoke(endpoint);
80
80
  return jsonResponse(200, { revoked: 1 });
81
81
  }
@@ -85,7 +85,8 @@ function routesFor(signInUser, now) {
85
85
  /**
86
86
  * A `fetch` serving ingest's session routes under `/api/auth`. POST with a
87
87
  * bearer proof signs `signInUser` in; DELETE and revoke-all leave the cookie
88
- * revoked, as the real endpoint does.
88
+ * revoked, as the real endpoint does. Revoke-all serves only the cookie path,
89
+ * which needs the CSRF token and `X-Comfy-Client`.
89
90
  */
90
91
  export function createFakeWebSessionEndpoint({ state, signInUser = fakeWebSessionUser(), now = Date.now }) {
91
92
  const requests = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfyorg/account-core",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.3",
4
4
  "description": "Shared account layer: a framework-free session core that exchanges a signed-in identity for the workspace-scoped Cloud JWT with valid-on-read freshness, plus the package-owned Firebase identity entry. It has no view layer and no Vue dependency; the composables and unstyled components ship from @comfyorg/account-ui",
5
5
  "keywords": [],
6
6
  "homepage": "https://comfy.org",
@@ -127,6 +127,8 @@
127
127
  "zod": "^3.23.8"
128
128
  },
129
129
  "scripts": {
130
+ "format": "oxfmt --write",
131
+ "format:check": "oxfmt --check",
130
132
  "build": "pnpm run clean && tsc -p tsconfig.build.json",
131
133
  "check:pack": "tsx scripts/checkPublishable.ts",
132
134
  "clean": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true })\"",