@scalemule/nextjs 0.1.30 → 0.1.32

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
@@ -95,6 +95,44 @@ For zero-config auth (reads from env vars):
95
95
  export { GET, POST, DELETE, PATCH } from '@scalemule/nextjs/server/auth'
96
96
  ```
97
97
 
98
+ ## Proxy Mode (keep API keys server-side)
99
+
100
+ If you don't want a real ScaleMule API key in browser bundles, run the SDK in **proxy mode**: pass the literal sentinel string `'proxy-mode'` as `apiKey` and route all server-bound traffic through Next.js routes that hold the real `SCALEMULE_API_KEY` server-side. **Both** the auth route and the analytics route are required — without the analytics route, every `trackEvent` / `trackPageView` silently 401s against the gateway and the dashboard reads empty.
101
+
102
+ ```ts
103
+ // app/api/auth/[...scalemule]/route.ts
104
+ export { GET, POST, DELETE, PATCH } from '@scalemule/nextjs/server/auth'
105
+ ```
106
+
107
+ ```ts
108
+ // app/api/analytics/event/route.ts
109
+ import { NextRequest } from 'next/server'
110
+ import { createAnalyticsRoutes } from '@scalemule/nextjs/server'
111
+
112
+ const { POST: handler } = createAnalyticsRoutes({ simpleProxy: true })
113
+
114
+ export const dynamic = 'force-dynamic'
115
+ export function POST(req: NextRequest, ctx: { params: Promise<Record<string, string | string[]>> }) {
116
+ return handler(req, ctx)
117
+ }
118
+ ```
119
+
120
+ ```tsx
121
+ // app/layout.tsx
122
+ <ScaleMuleProvider
123
+ apiKey="proxy-mode"
124
+ applicationId={process.env.SCALEMULE_APP_ID}
125
+ environment="prod"
126
+ gatewayUrl="https://api.scalemule.com"
127
+ authProxyUrl="/api/auth"
128
+ analyticsProxyUrl="/api/analytics/event"
129
+ >
130
+ {children}
131
+ </ScaleMuleProvider>
132
+ ```
133
+
134
+ If `apiKey="proxy-mode"` is set without `analyticsProxyUrl` (and no `publishableKey`), `useAnalytics` emits a one-time dev console warning — that exact misconfig caused a silent-empty dashboard in production for one customer (Gistyo, app ID `019dda2a-c7d9-75a1-9258-12e4c48f1499`).
135
+
98
136
  ## Client-Side Provider
99
137
 
100
138
  ```tsx
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ApiError, RealtimeService, StorageService, PhotoService, VideoService, AudioService, TtsService, TtsJobStatus, TtsAudioInfo, FileStatus } from '@scalemule/sdk';
2
+ import { ApiError, RealtimeService, StorageService, PhotoService, VideoService, AudioService, TtsService, SocialService, SocialPolicyService, TtsJobStatus, TtsAudioInfo, FileStatus } from '@scalemule/sdk';
3
3
  import * as React from 'react';
4
4
  import { ReactNode, ReactElement } from 'react';
5
5
  import { MoneyClient } from '@scalemule/money';
@@ -223,6 +223,10 @@ interface ScaleMuleContextValue {
223
223
  media: unknown;
224
224
  /** Base SDK TTS service — exposed for `useTtsJob()` and direct narration requests */
225
225
  tts: TtsService;
226
+ /** Base SDK social graph service — follow graph, posts, feed, likes, and activity */
227
+ social: SocialService;
228
+ /** Base SDK social policy service — privacy decisions, requests, blocks, reports */
229
+ socialPolicy: SocialPolicyService;
226
230
  /**
227
231
  * Default media policy for `useMedia()` calls. Set via
228
232
  * `<ScaleMuleProvider mediaPolicy="…">`; per-call overrides win.
@@ -245,6 +249,14 @@ interface ScaleMuleContextValue {
245
249
  authProxyUrl?: string;
246
250
  /** Publishable key for browser-safe operations (analytics) */
247
251
  publishableKey?: string;
252
+ /**
253
+ * The configured `apiKey` value. Exposed so hooks like `useAnalytics`
254
+ * can detect the proxy-mode sentinel (`'proxy-mode'`) and warn when
255
+ * the corresponding proxy route is missing — silently 401'ing on the
256
+ * fallback is the worst possible failure mode for an analytics path.
257
+ * Treat as read-only diagnostic; consumers should not pass it onward.
258
+ */
259
+ apiKey?: string;
248
260
  /** Gateway URL for direct API calls */
249
261
  gatewayUrl?: string;
250
262
  /** Configured environment ('dev' or 'prod') */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ApiError, RealtimeService, StorageService, PhotoService, VideoService, AudioService, TtsService, TtsJobStatus, TtsAudioInfo, FileStatus } from '@scalemule/sdk';
2
+ import { ApiError, RealtimeService, StorageService, PhotoService, VideoService, AudioService, TtsService, SocialService, SocialPolicyService, TtsJobStatus, TtsAudioInfo, FileStatus } from '@scalemule/sdk';
3
3
  import * as React from 'react';
4
4
  import { ReactNode, ReactElement } from 'react';
5
5
  import { MoneyClient } from '@scalemule/money';
@@ -223,6 +223,10 @@ interface ScaleMuleContextValue {
223
223
  media: unknown;
224
224
  /** Base SDK TTS service — exposed for `useTtsJob()` and direct narration requests */
225
225
  tts: TtsService;
226
+ /** Base SDK social graph service — follow graph, posts, feed, likes, and activity */
227
+ social: SocialService;
228
+ /** Base SDK social policy service — privacy decisions, requests, blocks, reports */
229
+ socialPolicy: SocialPolicyService;
226
230
  /**
227
231
  * Default media policy for `useMedia()` calls. Set via
228
232
  * `<ScaleMuleProvider mediaPolicy="…">`; per-call overrides win.
@@ -245,6 +249,14 @@ interface ScaleMuleContextValue {
245
249
  authProxyUrl?: string;
246
250
  /** Publishable key for browser-safe operations (analytics) */
247
251
  publishableKey?: string;
252
+ /**
253
+ * The configured `apiKey` value. Exposed so hooks like `useAnalytics`
254
+ * can detect the proxy-mode sentinel (`'proxy-mode'`) and warn when
255
+ * the corresponding proxy route is missing — silently 401'ing on the
256
+ * fallback is the worst possible failure mode for an analytics path.
257
+ * Treat as read-only diagnostic; consumers should not pass it onward.
258
+ */
259
+ apiKey?: string;
248
260
  /** Gateway URL for direct API calls */
249
261
  gatewayUrl?: string;
250
262
  /** Configured environment ('dev' or 'prod') */
package/dist/index.js CHANGED
@@ -4496,6 +4496,116 @@ var SocialService = class extends ServiceModule {
4496
4496
  return this.comment(postId, data);
4497
4497
  }
4498
4498
  };
4499
+ var SocialPolicyService = class extends ServiceModule {
4500
+ constructor() {
4501
+ super(...arguments);
4502
+ this.basePath = "/v1/social-policy";
4503
+ }
4504
+ decide(request, options) {
4505
+ return this.post("/decision", withDefaultContext(request), options);
4506
+ }
4507
+ batchDecide(request, options) {
4508
+ return this.post("/decisions/batch", request, options);
4509
+ }
4510
+ decideAction(action, actor, target, options) {
4511
+ return this.decide(
4512
+ {
4513
+ app_key: options?.appKey,
4514
+ account_id: options?.accountId,
4515
+ policy_pack: options?.policyPack,
4516
+ actor,
4517
+ target,
4518
+ action,
4519
+ context: options?.context,
4520
+ metadata: options?.metadata
4521
+ },
4522
+ options?.requestOptions
4523
+ );
4524
+ }
4525
+ decideFollow(actor, target, options) {
4526
+ return this.decideAction("follow", actor, target, options);
4527
+ }
4528
+ decideDirectMessage(actor, target, options) {
4529
+ return this.decideAction("message_direct", actor, target, options);
4530
+ }
4531
+ decideContactRequest(actor, target, options) {
4532
+ return this.decideAction("connect_request", actor, target, options);
4533
+ }
4534
+ getSettings(identityId, options) {
4535
+ return this._get(`/settings/${identityId}`, options);
4536
+ }
4537
+ updateSettings(identityId, request, options) {
4538
+ return this.patch(`/settings/${identityId}`, request, options);
4539
+ }
4540
+ createRelationship(request, options) {
4541
+ return this.post("/relationships", request, options);
4542
+ }
4543
+ listRelationships(params, options) {
4544
+ return this._get(this.withQuery("/relationships", toQueryParams(params)), options);
4545
+ }
4546
+ deleteRelationship(relationshipId, options) {
4547
+ return this.del(`/relationships/${relationshipId}`, options);
4548
+ }
4549
+ createContactRequest(request, options) {
4550
+ return this.post("/contact-requests", request, options);
4551
+ }
4552
+ listContactRequestInbox(params, options) {
4553
+ return this._get(
4554
+ this.withQuery("/contact-requests/inbox", toQueryParams(params)),
4555
+ options
4556
+ );
4557
+ }
4558
+ listContactRequestSent(params, options) {
4559
+ return this._get(
4560
+ this.withQuery("/contact-requests/sent", toQueryParams(params)),
4561
+ options
4562
+ );
4563
+ }
4564
+ acceptContactRequest(requestId, options) {
4565
+ return this.post(`/contact-requests/${requestId}/accept`, void 0, options);
4566
+ }
4567
+ ignoreContactRequest(requestId, options) {
4568
+ return this.post(`/contact-requests/${requestId}/ignore`, void 0, options);
4569
+ }
4570
+ declineContactRequest(requestId, options) {
4571
+ return this.post(`/contact-requests/${requestId}/decline`, void 0, options);
4572
+ }
4573
+ reportContactRequest(requestId, options) {
4574
+ return this.post(`/contact-requests/${requestId}/report`, void 0, options);
4575
+ }
4576
+ block(request, options) {
4577
+ return this.post("/block", request, options);
4578
+ }
4579
+ unblock(request, options) {
4580
+ return this.post("/unblock", request, options);
4581
+ }
4582
+ mute(request, options) {
4583
+ return this.post("/mute", request, options);
4584
+ }
4585
+ unmute(request, options) {
4586
+ return this.post("/unmute", request, options);
4587
+ }
4588
+ report(request, options) {
4589
+ return this.post("/report", request, options);
4590
+ }
4591
+ listPolicyPacks(options) {
4592
+ return this._get("/policy-packs", options);
4593
+ }
4594
+ };
4595
+ function withDefaultContext(request) {
4596
+ return {
4597
+ ...request,
4598
+ context: request.context ?? {}
4599
+ };
4600
+ }
4601
+ function toQueryParams(params) {
4602
+ if (!params) return void 0;
4603
+ const query = {};
4604
+ for (const [key, value] of Object.entries(params)) {
4605
+ query[key] = value;
4606
+ }
4607
+ return query;
4608
+ }
4499
4609
  var ReferralsService = class extends ServiceModule {
4500
4610
  constructor() {
4501
4611
  super(...arguments);
@@ -7543,6 +7653,7 @@ var ScaleMule = class {
7543
7653
  this.chat = new ChatService(this._client);
7544
7654
  this.conference = new ConferenceService(this._client);
7545
7655
  this.social = new SocialService(this._client);
7656
+ this.socialPolicy = new SocialPolicyService(this._client);
7546
7657
  this.referrals = new ReferralsService(this._client);
7547
7658
  this.billing = new BillingService(this._client);
7548
7659
  this.analytics = new AnalyticsService(this._client);
@@ -8915,6 +9026,8 @@ function ScaleMuleProvider({
8915
9026
  audio: baseClient.audio,
8916
9027
  media: baseClient.media,
8917
9028
  tts: baseClient.tts,
9029
+ social: baseClient.social,
9030
+ socialPolicy: baseClient.socialPolicy,
8918
9031
  mediaPolicy: effectiveMediaPolicy,
8919
9032
  user,
8920
9033
  setUser: handleSetUser,
@@ -8924,13 +9037,14 @@ function ScaleMuleProvider({
8924
9037
  analyticsProxyUrl,
8925
9038
  authProxyUrl,
8926
9039
  publishableKey,
9040
+ apiKey,
8927
9041
  gatewayUrl: resolvedGatewayUrl,
8928
9042
  environment: environment || void 0,
8929
9043
  enableAccountSwitcher,
8930
9044
  accountSwitcherPrivacy,
8931
9045
  bootstrapFlags
8932
9046
  }),
8933
- [client, money$1, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags, effectiveMediaPolicy]
9047
+ [client, money$1, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, apiKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags, effectiveMediaPolicy]
8934
9048
  );
8935
9049
  return /* @__PURE__ */ jsxRuntime.jsx(ScaleMuleContext.Provider, { value, children });
8936
9050
  }
@@ -11645,7 +11759,18 @@ function useAnalytics(options = {}) {
11645
11759
  eventDedupMs = DEFAULT_EVENT_DEDUP_MS
11646
11760
  } = options;
11647
11761
  const shouldAutoCaptureUtmParams = autoCaptureUtmParams ?? autoCapturUtmParams ?? true;
11648
- const { client, user, analyticsProxyUrl, publishableKey, gatewayUrl } = useScaleMule();
11762
+ const { client, user, analyticsProxyUrl, publishableKey, apiKey, gatewayUrl } = useScaleMule();
11763
+ const proxyModeMisconfigWarnedRef = React.useRef(false);
11764
+ React.useEffect(() => {
11765
+ if (process.env.NODE_ENV === "production") return;
11766
+ if (proxyModeMisconfigWarnedRef.current) return;
11767
+ if (apiKey === "proxy-mode" && !analyticsProxyUrl && !publishableKey) {
11768
+ proxyModeMisconfigWarnedRef.current = true;
11769
+ console.warn(
11770
+ '[ScaleMule] useAnalytics: apiKey="proxy-mode" but neither `analyticsProxyUrl` nor `publishableKey` is configured. Every trackEvent / trackPageView will silently 401 against the gateway. Add an analytics proxy route (createAnalyticsRoutes({ simpleProxy: true })) and pass `analyticsProxyUrl` to <ScaleMuleProvider>. See @scalemule/nextjs README \u2192 Proxy Mode.'
11771
+ );
11772
+ }
11773
+ }, [apiKey, analyticsProxyUrl, publishableKey]);
11649
11774
  const [loading, setLoading] = React.useState(false);
11650
11775
  const [error, setError] = React.useState(null);
11651
11776
  const [utmParams, setUtmParams] = React.useState(null);
package/dist/index.mjs CHANGED
@@ -4476,6 +4476,116 @@ var SocialService = class extends ServiceModule {
4476
4476
  return this.comment(postId, data);
4477
4477
  }
4478
4478
  };
4479
+ var SocialPolicyService = class extends ServiceModule {
4480
+ constructor() {
4481
+ super(...arguments);
4482
+ this.basePath = "/v1/social-policy";
4483
+ }
4484
+ decide(request, options) {
4485
+ return this.post("/decision", withDefaultContext(request), options);
4486
+ }
4487
+ batchDecide(request, options) {
4488
+ return this.post("/decisions/batch", request, options);
4489
+ }
4490
+ decideAction(action, actor, target, options) {
4491
+ return this.decide(
4492
+ {
4493
+ app_key: options?.appKey,
4494
+ account_id: options?.accountId,
4495
+ policy_pack: options?.policyPack,
4496
+ actor,
4497
+ target,
4498
+ action,
4499
+ context: options?.context,
4500
+ metadata: options?.metadata
4501
+ },
4502
+ options?.requestOptions
4503
+ );
4504
+ }
4505
+ decideFollow(actor, target, options) {
4506
+ return this.decideAction("follow", actor, target, options);
4507
+ }
4508
+ decideDirectMessage(actor, target, options) {
4509
+ return this.decideAction("message_direct", actor, target, options);
4510
+ }
4511
+ decideContactRequest(actor, target, options) {
4512
+ return this.decideAction("connect_request", actor, target, options);
4513
+ }
4514
+ getSettings(identityId, options) {
4515
+ return this._get(`/settings/${identityId}`, options);
4516
+ }
4517
+ updateSettings(identityId, request, options) {
4518
+ return this.patch(`/settings/${identityId}`, request, options);
4519
+ }
4520
+ createRelationship(request, options) {
4521
+ return this.post("/relationships", request, options);
4522
+ }
4523
+ listRelationships(params, options) {
4524
+ return this._get(this.withQuery("/relationships", toQueryParams(params)), options);
4525
+ }
4526
+ deleteRelationship(relationshipId, options) {
4527
+ return this.del(`/relationships/${relationshipId}`, options);
4528
+ }
4529
+ createContactRequest(request, options) {
4530
+ return this.post("/contact-requests", request, options);
4531
+ }
4532
+ listContactRequestInbox(params, options) {
4533
+ return this._get(
4534
+ this.withQuery("/contact-requests/inbox", toQueryParams(params)),
4535
+ options
4536
+ );
4537
+ }
4538
+ listContactRequestSent(params, options) {
4539
+ return this._get(
4540
+ this.withQuery("/contact-requests/sent", toQueryParams(params)),
4541
+ options
4542
+ );
4543
+ }
4544
+ acceptContactRequest(requestId, options) {
4545
+ return this.post(`/contact-requests/${requestId}/accept`, void 0, options);
4546
+ }
4547
+ ignoreContactRequest(requestId, options) {
4548
+ return this.post(`/contact-requests/${requestId}/ignore`, void 0, options);
4549
+ }
4550
+ declineContactRequest(requestId, options) {
4551
+ return this.post(`/contact-requests/${requestId}/decline`, void 0, options);
4552
+ }
4553
+ reportContactRequest(requestId, options) {
4554
+ return this.post(`/contact-requests/${requestId}/report`, void 0, options);
4555
+ }
4556
+ block(request, options) {
4557
+ return this.post("/block", request, options);
4558
+ }
4559
+ unblock(request, options) {
4560
+ return this.post("/unblock", request, options);
4561
+ }
4562
+ mute(request, options) {
4563
+ return this.post("/mute", request, options);
4564
+ }
4565
+ unmute(request, options) {
4566
+ return this.post("/unmute", request, options);
4567
+ }
4568
+ report(request, options) {
4569
+ return this.post("/report", request, options);
4570
+ }
4571
+ listPolicyPacks(options) {
4572
+ return this._get("/policy-packs", options);
4573
+ }
4574
+ };
4575
+ function withDefaultContext(request) {
4576
+ return {
4577
+ ...request,
4578
+ context: request.context ?? {}
4579
+ };
4580
+ }
4581
+ function toQueryParams(params) {
4582
+ if (!params) return void 0;
4583
+ const query = {};
4584
+ for (const [key, value] of Object.entries(params)) {
4585
+ query[key] = value;
4586
+ }
4587
+ return query;
4588
+ }
4479
4589
  var ReferralsService = class extends ServiceModule {
4480
4590
  constructor() {
4481
4591
  super(...arguments);
@@ -7523,6 +7633,7 @@ var ScaleMule = class {
7523
7633
  this.chat = new ChatService(this._client);
7524
7634
  this.conference = new ConferenceService(this._client);
7525
7635
  this.social = new SocialService(this._client);
7636
+ this.socialPolicy = new SocialPolicyService(this._client);
7526
7637
  this.referrals = new ReferralsService(this._client);
7527
7638
  this.billing = new BillingService(this._client);
7528
7639
  this.analytics = new AnalyticsService(this._client);
@@ -8895,6 +9006,8 @@ function ScaleMuleProvider({
8895
9006
  audio: baseClient.audio,
8896
9007
  media: baseClient.media,
8897
9008
  tts: baseClient.tts,
9009
+ social: baseClient.social,
9010
+ socialPolicy: baseClient.socialPolicy,
8898
9011
  mediaPolicy: effectiveMediaPolicy,
8899
9012
  user,
8900
9013
  setUser: handleSetUser,
@@ -8904,13 +9017,14 @@ function ScaleMuleProvider({
8904
9017
  analyticsProxyUrl,
8905
9018
  authProxyUrl,
8906
9019
  publishableKey,
9020
+ apiKey,
8907
9021
  gatewayUrl: resolvedGatewayUrl,
8908
9022
  environment: environment || void 0,
8909
9023
  enableAccountSwitcher,
8910
9024
  accountSwitcherPrivacy,
8911
9025
  bootstrapFlags
8912
9026
  }),
8913
- [client, money, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags, effectiveMediaPolicy]
9027
+ [client, money, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, apiKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags, effectiveMediaPolicy]
8914
9028
  );
8915
9029
  return /* @__PURE__ */ jsx(ScaleMuleContext.Provider, { value, children });
8916
9030
  }
@@ -11625,7 +11739,18 @@ function useAnalytics(options = {}) {
11625
11739
  eventDedupMs = DEFAULT_EVENT_DEDUP_MS
11626
11740
  } = options;
11627
11741
  const shouldAutoCaptureUtmParams = autoCaptureUtmParams ?? autoCapturUtmParams ?? true;
11628
- const { client, user, analyticsProxyUrl, publishableKey, gatewayUrl } = useScaleMule();
11742
+ const { client, user, analyticsProxyUrl, publishableKey, apiKey, gatewayUrl } = useScaleMule();
11743
+ const proxyModeMisconfigWarnedRef = useRef(false);
11744
+ useEffect(() => {
11745
+ if (process.env.NODE_ENV === "production") return;
11746
+ if (proxyModeMisconfigWarnedRef.current) return;
11747
+ if (apiKey === "proxy-mode" && !analyticsProxyUrl && !publishableKey) {
11748
+ proxyModeMisconfigWarnedRef.current = true;
11749
+ console.warn(
11750
+ '[ScaleMule] useAnalytics: apiKey="proxy-mode" but neither `analyticsProxyUrl` nor `publishableKey` is configured. Every trackEvent / trackPageView will silently 401 against the gateway. Add an analytics proxy route (createAnalyticsRoutes({ simpleProxy: true })) and pass `analyticsProxyUrl` to <ScaleMuleProvider>. See @scalemule/nextjs README \u2192 Proxy Mode.'
11751
+ );
11752
+ }
11753
+ }, [apiKey, analyticsProxyUrl, publishableKey]);
11629
11754
  const [loading, setLoading] = useState(false);
11630
11755
  const [error, setError] = useState(null);
11631
11756
  const [utmParams, setUtmParams] = useState(null);