@capturly/capture-sdk 2.11.3 → 2.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,70 @@
1
1
  # @capturly/capture-sdk
2
2
 
3
+ ## 2.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#488](https://github.com/Corey-Alan-Consulting/capturly.app/pull/488) [`b4963e6`](https://github.com/Corey-Alan-Consulting/capturly.app/commit/b4963e699dd48f7d95057043dff86e0c036e510f) Thanks [@NX211](https://github.com/NX211)! - Stop pairing a studio's cover art with a capture request's own brand mark, and
8
+ move the two campaign notification toggles into Settings where they belong.
9
+
10
+ A capture request can set its own logo and colours; cover art has no per-request
11
+ override and is always the studio's. So a request branded for someone else — an
12
+ agency collecting for one client out of a shared studio — opened its page and
13
+ its invite under the studio's banner with the client's logo underneath: two
14
+ brands, only one of which is asking. When the mark belongs to the request, the
15
+ studio cover is no longer shown on either surface, and the mark leads.
16
+
17
+ Deciding that needs to know which level the mark came from, so `logoScope`
18
+ joins the respondent branding contract. Adding it was a compile error in the
19
+ mobile app, the capture SDK and the email decision table, which is what that
20
+ contract is for — each now records what it does with the key. The SDK surfaces
21
+ it on `CaptureRequestBranding` so an embedding host can make the same call about
22
+ its own layout; the mobile app declares it unused, having no cover art to pair.
23
+
24
+ Auto-reminders and new-video email were buttons in the header bar, which made two
25
+ pieces of persistent state look like two actions. They already PATCHed the same
26
+ endpoint the settings form uses, so they are now checkboxes in Settings and save
27
+ with everything else.
28
+
29
+ ## 2.11.4
30
+
31
+ ### Patch Changes
32
+
33
+ - [#486](https://github.com/Corey-Alan-Consulting/capturly.app/pull/486) [`52f5252`](https://github.com/Corey-Alan-Consulting/capturly.app/commit/52f5252a783bdc4c5725779ac5190ce3dd760ee9) Thanks [@NX211](https://github.com/NX211)! - Show the brand logo to respondents on iOS and in the capture SDK, and pin the
34
+ respondent-branding payload so the next field cannot reach one client only.
35
+
36
+ Brand logos shipped to the web respondent page and stopped there. The server was
37
+ already serving them — `/api/campaigns/respond/view` returns the same view the
38
+ /c page renders — but the mobile app and the SDK each re-declare the branding
39
+ shape by hand to parse an untrusted body defensively, and neither picked the
40
+ field up. On iOS that left the studio name rendering in exactly the slot the mark
41
+ is meant to replace; an SDK host got a brand-coloured surface with no mark at
42
+ all.
43
+
44
+ Both now read `logoPath`. The mobile respond screen renders it where the wordmark
45
+ was, with the studio name as its accessibility label — the same substitution the
46
+ web page makes, for the same reason: the mark says who is asking, and a reader
47
+ who cannot see it still needs to know.
48
+
49
+ Nothing could have caught this. The web page is type-checked against the
50
+ server's own view type, so adding a branding field turns every web surface into a
51
+ compile error — and leaves the two hand-written parsers compiling cleanly while
52
+ dropping it. `RESPONDENT_BRANDING_KEYS` in @capturly/studios-client is now the
53
+ agreed list of what that endpoint serves, and all three sides own a test against
54
+ it: the server proves it serves exactly those keys, and each client proves it
55
+ accounts for every one. Deliberately ignoring a key is still fine — `studioSlug`
56
+ is ignored by both clients — but a new key now fails three tests until each
57
+ surface has decided.
58
+
59
+ No desktop change: branding is edited on the web by design, and the desktop has
60
+ no respondent surface.
61
+
62
+ `@capturly/capture-sdk` takes a patch rather than the minor its new `logoPath`
63
+ would suggest: it shares the repo's `fixed` version group with the desktop and
64
+ mobile apps, so a minor there moves the versions the store pipelines submit
65
+ against. `@capturly/studios-client` is outside that group and takes the minor on
66
+ its own.
67
+
3
68
  ## 2.11.3
4
69
 
5
70
  ## 2.11.2
@@ -101,6 +101,23 @@ function isRecord(value) {
101
101
  return value !== null && typeof value === "object" && !Array.isArray(value);
102
102
  }
103
103
  var stringOrNull = (v) => typeof v === "string" ? v : null;
104
+ var LOGO_SCOPES = { org: true, studio: true, campaign: true };
105
+ function brandLogoScopeOrNull(v) {
106
+ return typeof v === "string" && v in LOGO_SCOPES ? v : null;
107
+ }
108
+ var HANDLED_KEYS = {
109
+ brandAccentColor: true,
110
+ brandColor: true,
111
+ brandSecondaryColor: true,
112
+ coverPath: true,
113
+ logoPath: true,
114
+ logoScope: true,
115
+ studioName: true,
116
+ // Served and deliberately dropped: it addresses a Capturly studio page, which
117
+ // an embedding host has no reason to link to.
118
+ studioSlug: false
119
+ };
120
+ var BRANDING_KEYS_HANDLED = Object.keys(HANDLED_KEYS);
104
121
  async function fetchCaptureView(args) {
105
122
  const json = await postJson(
106
123
  args.baseUrl,
@@ -117,13 +134,17 @@ async function fetchCaptureView(args) {
117
134
  brandColor: stringOrNull(view.branding.brandColor),
118
135
  brandSecondaryColor: stringOrNull(view.branding.brandSecondaryColor),
119
136
  brandAccentColor: stringOrNull(view.branding.brandAccentColor),
120
- coverPath: stringOrNull(view.branding.coverPath)
137
+ coverPath: stringOrNull(view.branding.coverPath),
138
+ logoPath: stringOrNull(view.branding.logoPath),
139
+ logoScope: brandLogoScopeOrNull(view.branding.logoScope)
121
140
  } : {
122
141
  studioName: "",
123
142
  brandColor: null,
124
143
  brandSecondaryColor: null,
125
144
  brandAccentColor: null,
126
- coverPath: null
145
+ coverPath: null,
146
+ logoPath: null,
147
+ logoScope: null
127
148
  };
128
149
  if (typeof view.maxDurationSec !== "number" || view.maxDurationSec <= 0) {
129
150
  throw new CaptureError("unknown", "Capturly returned no recording time limit.");
@@ -271,6 +292,7 @@ async function finalizeSubmission(args) {
271
292
  export {
272
293
  isSecureTransportUrl,
273
294
  CaptureError,
295
+ BRANDING_KEYS_HANDLED,
274
296
  fetchCaptureView,
275
297
  requestUpload,
276
298
  uploadBlob,
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  isSecureTransportUrl,
6
6
  requestUpload,
7
7
  uploadBlob
8
- } from "./chunk-CZPDQKMD.js";
8
+ } from "./chunk-EH7DDMYO.js";
9
9
 
10
10
  var MP4_MIME = "video/mp4;codecs=avc1.42E01E,mp4a.40.2";
11
11
  function pickMuxStrategy() {
@@ -1,6 +1,7 @@
1
1
  import { CaptureError, type CaptureAccess, type CaptureErrorCode, type CaptureRequestBranding, type CaptureRequestView } from './types.js';
2
2
  export { CaptureError };
3
3
  export type { CaptureAccess, CaptureErrorCode, CaptureRequestBranding, CaptureRequestView };
4
+ export declare const BRANDING_KEYS_HANDLED: readonly string[];
4
5
  /**
5
6
  * Fetch what the person is being asked — title, prompt, branding, release
6
7
  * text, limits, and any submission they already made. Same access, same
package/dist/transport.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import {
2
+ BRANDING_KEYS_HANDLED,
2
3
  CaptureError,
3
4
  fetchCaptureView,
4
5
  finalizeSubmission,
5
6
  requestUpload,
6
7
  uploadBlob
7
- } from "./chunk-CZPDQKMD.js";
8
+ } from "./chunk-EH7DDMYO.js";
8
9
  export {
10
+ BRANDING_KEYS_HANDLED,
9
11
  CaptureError,
10
12
  fetchCaptureView,
11
13
  finalizeSubmission,
package/dist/types.d.ts CHANGED
@@ -31,6 +31,28 @@ export interface CaptureRequestBranding {
31
31
  brandAccentColor: string | null;
32
32
  /** Cover-art path under the Capturly origin, or null when unset. */
33
33
  coverPath: string | null;
34
+ /**
35
+ * Brand-mark path under the Capturly origin, already resolved through
36
+ * request → studio → organization, or null when no level has one.
37
+ *
38
+ * Render it in place of `studioName` and keep the name as the image's alt
39
+ * text: the mark says who is asking, and a reader with images blocked still
40
+ * needs to know. Served by an unauthenticated proxy, so a host page can load
41
+ * it without credentials.
42
+ */
43
+ logoPath: string | null;
44
+ /**
45
+ * Which level the mark came from: `'org'`, `'studio'`, or `'campaign'`, or
46
+ * null when there is none.
47
+ *
48
+ * `'campaign'` means this request set its own mark, which may belong to a
49
+ * different brand than the studio hosting it — an agency collecting for one
50
+ * client out of a shared studio. `coverPath` has no such override; it is
51
+ * always the studio's, so Capturly serves `coverPath: null` alongside a
52
+ * campaign-scoped mark rather than hand every host two unrelated brands.
53
+ * Layout information only — there is no rule for a host to re-apply.
54
+ */
55
+ logoScope: 'org' | 'studio' | 'campaign' | null;
34
56
  }
35
57
  /**
36
58
  * What the person is being asked, fetched with the same access the recording
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capturly/capture-sdk",
3
- "version": "2.11.3",
3
+ "version": "2.12.0",
4
4
  "description": "Record full-quality audio and video on your user's own device, from your own product.",
5
5
  "keywords": [
6
6
  "recording",
@@ -55,9 +55,10 @@
55
55
  "esbuild": "^0.25.0",
56
56
  "typescript": "^5.4.0",
57
57
  "vitest": "^4.1.7",
58
- "@capturly/eslint-config": "2.11.3",
59
- "@capturly/tsconfig": "2.11.3",
60
- "@capturly/recording-engine": "2.11.3"
58
+ "@capturly/eslint-config": "2.12.0",
59
+ "@capturly/recording-engine": "2.12.0",
60
+ "@capturly/studios-client": "0.4.0",
61
+ "@capturly/tsconfig": "2.12.0"
61
62
  },
62
63
  "scripts": {
63
64
  "build": "pnpm clean && node build.mjs && tsc -p tsconfig.build.json --emitDeclarationOnly",