@q9labsai/chalk-client 4.0.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.
Files changed (253) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/dist/access/base64-url.d.ts +2 -0
  4. package/dist/access/base64-url.d.ts.map +1 -0
  5. package/dist/access/episode-diagnostic-credential.d.ts +11 -0
  6. package/dist/access/episode-diagnostic-credential.d.ts.map +1 -0
  7. package/dist/access/grant.d.ts +53 -0
  8. package/dist/access/grant.d.ts.map +1 -0
  9. package/dist/access/grant.test.helpers.d.ts +10 -0
  10. package/dist/access/grant.test.helpers.d.ts.map +1 -0
  11. package/dist/access/manager.d.ts +36 -0
  12. package/dist/access/manager.d.ts.map +1 -0
  13. package/dist/chat-files/http-transport.d.ts +8 -0
  14. package/dist/chat-files/http-transport.d.ts.map +1 -0
  15. package/dist/chat-files/index.d.ts +3 -0
  16. package/dist/chat-files/index.d.ts.map +1 -0
  17. package/dist/chat-files/types.d.ts +37 -0
  18. package/dist/chat-files/types.d.ts.map +1 -0
  19. package/dist/chunk-5VXGLI3T.js +3385 -0
  20. package/dist/chunk-O22WFBKF.js +5161 -0
  21. package/dist/chunk-PBCGWE2V.js +811 -0
  22. package/dist/chunk-PBSYXPNJ.js +23 -0
  23. package/dist/chunk-QVHR3RMB.js +1229 -0
  24. package/dist/client.d.ts +6785 -0
  25. package/dist/client.d.ts.map +1 -0
  26. package/dist/collaboration/index.d.ts +3 -0
  27. package/dist/collaboration/index.d.ts.map +1 -0
  28. package/dist/collaboration/types.d.ts +94 -0
  29. package/dist/collaboration/types.d.ts.map +1 -0
  30. package/dist/collaboration/wire.d.ts +19 -0
  31. package/dist/collaboration/wire.d.ts.map +1 -0
  32. package/dist/connection/dependencies.d.ts +112 -0
  33. package/dist/connection/dependencies.d.ts.map +1 -0
  34. package/dist/connection/diagnostics.d.ts +52 -0
  35. package/dist/connection/diagnostics.d.ts.map +1 -0
  36. package/dist/connection/index.d.ts +47 -0
  37. package/dist/connection/index.d.ts.map +1 -0
  38. package/dist/connection/lifecycle.d.ts +58 -0
  39. package/dist/connection/lifecycle.d.ts.map +1 -0
  40. package/dist/connection/media-devices.d.ts +6 -0
  41. package/dist/connection/media-devices.d.ts.map +1 -0
  42. package/dist/connection/production.d.ts +13 -0
  43. package/dist/connection/production.d.ts.map +1 -0
  44. package/dist/connection/snapshot.d.ts +23 -0
  45. package/dist/connection/snapshot.d.ts.map +1 -0
  46. package/dist/connection/types.d.ts +128 -0
  47. package/dist/connection/types.d.ts.map +1 -0
  48. package/dist/effect.d.ts +19 -0
  49. package/dist/effect.d.ts.map +1 -0
  50. package/dist/effect.js +7846 -0
  51. package/dist/episode-diagnostic-runtime.test.helpers.d.ts +3 -0
  52. package/dist/episode-diagnostic-runtime.test.helpers.d.ts.map +1 -0
  53. package/dist/generated/http-api.d.ts +6658 -0
  54. package/dist/generated/http-api.d.ts.map +1 -0
  55. package/dist/generated/schemas.d.ts +12076 -0
  56. package/dist/generated/schemas.d.ts.map +1 -0
  57. package/dist/generated/sync.d.ts +3947 -0
  58. package/dist/generated/sync.d.ts.map +1 -0
  59. package/dist/generated/whiteboard-v1.d.ts +334 -0
  60. package/dist/generated/whiteboard-v1.d.ts.map +1 -0
  61. package/dist/index.d.ts +10 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +152 -0
  64. package/dist/media/client.d.ts +18 -0
  65. package/dist/media/client.d.ts.map +1 -0
  66. package/dist/media/cloudflare-sfu.d.ts +5 -0
  67. package/dist/media/cloudflare-sfu.d.ts.map +1 -0
  68. package/dist/media/index.d.ts +3 -0
  69. package/dist/media/index.d.ts.map +1 -0
  70. package/dist/media/index.js +24 -0
  71. package/dist/media/plane.d.ts +24 -0
  72. package/dist/media/plane.d.ts.map +1 -0
  73. package/dist/media/tracks.d.ts +21 -0
  74. package/dist/media/tracks.d.ts.map +1 -0
  75. package/dist/media/transport.d.ts +3 -0
  76. package/dist/media/transport.d.ts.map +1 -0
  77. package/dist/media/types.d.ts +114 -0
  78. package/dist/media/types.d.ts.map +1 -0
  79. package/dist/server/chunk-A6YMG5FM.js +61 -0
  80. package/dist/server/client.d.cts +2 -0
  81. package/dist/server/client.d.ts +3 -0
  82. package/dist/server/client.d.ts.map +1 -0
  83. package/dist/server/errors.d.cts +18 -0
  84. package/dist/server/errors.d.ts +19 -0
  85. package/dist/server/errors.d.ts.map +1 -0
  86. package/dist/server/index.cjs +551 -0
  87. package/dist/server/index.d.cts +3 -0
  88. package/dist/server/index.d.ts +4 -0
  89. package/dist/server/index.d.ts.map +1 -0
  90. package/dist/server/index.js +474 -0
  91. package/dist/server/server-only.cjs +40 -0
  92. package/dist/server/server-only.d.cts +1 -0
  93. package/dist/server/server-only.d.ts +2 -0
  94. package/dist/server/server-only.d.ts.map +1 -0
  95. package/dist/server/server-only.js +9 -0
  96. package/dist/server/transport.d.cts +12 -0
  97. package/dist/server/transport.d.ts +13 -0
  98. package/dist/server/transport.d.ts.map +1 -0
  99. package/dist/server/types.d.cts +201 -0
  100. package/dist/server/types.d.ts +202 -0
  101. package/dist/server/types.d.ts.map +1 -0
  102. package/dist/space-client/chat-controller-helpers.d.ts +15 -0
  103. package/dist/space-client/chat-controller-helpers.d.ts.map +1 -0
  104. package/dist/space-client/chat-controller.d.ts +34 -0
  105. package/dist/space-client/chat-controller.d.ts.map +1 -0
  106. package/dist/space-client/controller-effects.d.ts +45 -0
  107. package/dist/space-client/controller-effects.d.ts.map +1 -0
  108. package/dist/space-client/controller-parity.test.helpers.d.ts +150 -0
  109. package/dist/space-client/controller-parity.test.helpers.d.ts.map +1 -0
  110. package/dist/space-client/core.d.ts +52 -0
  111. package/dist/space-client/core.d.ts.map +1 -0
  112. package/dist/space-client/core.test.helpers.d.ts +14 -0
  113. package/dist/space-client/core.test.helpers.d.ts.map +1 -0
  114. package/dist/space-client/effect.d.ts +44 -0
  115. package/dist/space-client/effect.d.ts.map +1 -0
  116. package/dist/space-client/episode-diagnostic-registry.d.ts +13 -0
  117. package/dist/space-client/episode-diagnostic-registry.d.ts.map +1 -0
  118. package/dist/space-client/episode-diagnostic-render-registry.d.ts +4 -0
  119. package/dist/space-client/episode-diagnostic-render-registry.d.ts.map +1 -0
  120. package/dist/space-client/episode-diagnostic-runtime.d.ts +102 -0
  121. package/dist/space-client/episode-diagnostic-runtime.d.ts.map +1 -0
  122. package/dist/space-client/errors.d.ts +11 -0
  123. package/dist/space-client/errors.d.ts.map +1 -0
  124. package/dist/space-client/index.d.ts +5 -0
  125. package/dist/space-client/index.d.ts.map +1 -0
  126. package/dist/space-client/media-controller.d.ts +30 -0
  127. package/dist/space-client/media-controller.d.ts.map +1 -0
  128. package/dist/space-client/media-controller.test.helpers.d.ts +105 -0
  129. package/dist/space-client/media-controller.test.helpers.d.ts.map +1 -0
  130. package/dist/space-client/media-device-selection.d.ts +13 -0
  131. package/dist/space-client/media-device-selection.d.ts.map +1 -0
  132. package/dist/space-client/participants-controller.d.ts +33 -0
  133. package/dist/space-client/participants-controller.d.ts.map +1 -0
  134. package/dist/space-client/promise-facade.d.ts +12 -0
  135. package/dist/space-client/promise-facade.d.ts.map +1 -0
  136. package/dist/space-client/reactions-controller.d.ts +19 -0
  137. package/dist/space-client/reactions-controller.d.ts.map +1 -0
  138. package/dist/space-client/space-client.d.ts +5 -0
  139. package/dist/space-client/space-client.d.ts.map +1 -0
  140. package/dist/space-client/store.d.ts +29 -0
  141. package/dist/space-client/store.d.ts.map +1 -0
  142. package/dist/space-client/types.d.ts +222 -0
  143. package/dist/space-client/types.d.ts.map +1 -0
  144. package/dist/space-client/whiteboard-controller.d.ts +17 -0
  145. package/dist/space-client/whiteboard-controller.d.ts.map +1 -0
  146. package/dist/sync/browser.d.ts +9 -0
  147. package/dist/sync/browser.d.ts.map +1 -0
  148. package/dist/sync/canonical.d.ts +7 -0
  149. package/dist/sync/canonical.d.ts.map +1 -0
  150. package/dist/sync/errors.d.ts +9 -0
  151. package/dist/sync/errors.d.ts.map +1 -0
  152. package/dist/sync/index.d.ts +13 -0
  153. package/dist/sync/index.d.ts.map +1 -0
  154. package/dist/sync/index.js +51 -0
  155. package/dist/sync/react-native.d.ts +42 -0
  156. package/dist/sync/react-native.d.ts.map +1 -0
  157. package/dist/sync/types.d.ts +28 -0
  158. package/dist/sync/types.d.ts.map +1 -0
  159. package/dist/sync/v1-client.d.ts +59 -0
  160. package/dist/sync/v1-client.d.ts.map +1 -0
  161. package/dist/sync/v1-codec.d.ts +5 -0
  162. package/dist/sync/v1-codec.d.ts.map +1 -0
  163. package/dist/sync/v1-create.d.ts +12 -0
  164. package/dist/sync/v1-create.d.ts.map +1 -0
  165. package/dist/sync/v1-persistence.d.ts +10 -0
  166. package/dist/sync/v1-persistence.d.ts.map +1 -0
  167. package/dist/sync/v1-platform-persistence.d.ts +26 -0
  168. package/dist/sync/v1-platform-persistence.d.ts.map +1 -0
  169. package/dist/sync/v1-reducer.d.ts +17 -0
  170. package/dist/sync/v1-reducer.d.ts.map +1 -0
  171. package/dist/sync/v1-types.d.ts +197 -0
  172. package/dist/sync/v1-types.d.ts.map +1 -0
  173. package/dist/telemetry/attributes.d.ts +7 -0
  174. package/dist/telemetry/attributes.d.ts.map +1 -0
  175. package/dist/telemetry/client.d.ts +44 -0
  176. package/dist/telemetry/client.d.ts.map +1 -0
  177. package/dist/telemetry/delivery.d.ts +107 -0
  178. package/dist/telemetry/delivery.d.ts.map +1 -0
  179. package/dist/telemetry/exporter.d.ts +38 -0
  180. package/dist/telemetry/exporter.d.ts.map +1 -0
  181. package/dist/telemetry/index.d.ts +7 -0
  182. package/dist/telemetry/index.d.ts.map +1 -0
  183. package/dist/telemetry/index.js +48 -0
  184. package/dist/telemetry/journey.d.ts +52 -0
  185. package/dist/telemetry/journey.d.ts.map +1 -0
  186. package/dist/telemetry/random.d.ts +18 -0
  187. package/dist/telemetry/random.d.ts.map +1 -0
  188. package/dist/telemetry/rtc.d.ts +35 -0
  189. package/dist/telemetry/rtc.d.ts.map +1 -0
  190. package/dist/telemetry/storage.d.ts +22 -0
  191. package/dist/telemetry/storage.d.ts.map +1 -0
  192. package/dist/telemetry/sync.d.ts +10 -0
  193. package/dist/telemetry/sync.d.ts.map +1 -0
  194. package/dist/telemetry/test-support.d.ts +51 -0
  195. package/dist/telemetry/test-support.d.ts.map +1 -0
  196. package/dist/telemetry/trace.d.ts +18 -0
  197. package/dist/telemetry/trace.d.ts.map +1 -0
  198. package/dist/telemetry/types.d.ts +109 -0
  199. package/dist/telemetry/types.d.ts.map +1 -0
  200. package/dist/webhooks/chunk-UNMJZNTZ.js +31 -0
  201. package/dist/webhooks/errors.d.cts +8 -0
  202. package/dist/webhooks/errors.d.ts +9 -0
  203. package/dist/webhooks/errors.d.ts.map +1 -0
  204. package/dist/webhooks/generated/event-v1.d.cts +764 -0
  205. package/dist/webhooks/generated/event-v1.d.ts +765 -0
  206. package/dist/webhooks/generated/event-v1.d.ts.map +1 -0
  207. package/dist/webhooks/generated/fixtures-v1.d.cts +46 -0
  208. package/dist/webhooks/generated/fixtures-v1.d.ts +47 -0
  209. package/dist/webhooks/generated/fixtures-v1.d.ts.map +1 -0
  210. package/dist/webhooks/index.cjs +1242 -0
  211. package/dist/webhooks/index.d.cts +5 -0
  212. package/dist/webhooks/index.d.ts +6 -0
  213. package/dist/webhooks/index.d.ts.map +1 -0
  214. package/dist/webhooks/index.js +1197 -0
  215. package/dist/webhooks/processor.d.cts +21 -0
  216. package/dist/webhooks/processor.d.ts +22 -0
  217. package/dist/webhooks/processor.d.ts.map +1 -0
  218. package/dist/webhooks/server-only.cjs +36 -0
  219. package/dist/webhooks/server-only.d.cts +1 -0
  220. package/dist/webhooks/server-only.d.ts +2 -0
  221. package/dist/webhooks/server-only.d.ts.map +1 -0
  222. package/dist/webhooks/server-only.js +9 -0
  223. package/dist/webhooks/test.cjs +160 -0
  224. package/dist/webhooks/test.d.cts +42 -0
  225. package/dist/webhooks/test.d.ts +43 -0
  226. package/dist/webhooks/test.d.ts.map +1 -0
  227. package/dist/webhooks/test.js +137 -0
  228. package/dist/webhooks/types.d.cts +55 -0
  229. package/dist/webhooks/types.d.ts +56 -0
  230. package/dist/webhooks/types.d.ts.map +1 -0
  231. package/dist/webhooks/validate.d.cts +5 -0
  232. package/dist/webhooks/validate.d.ts +6 -0
  233. package/dist/webhooks/validate.d.ts.map +1 -0
  234. package/dist/webhooks/verify.d.cts +10 -0
  235. package/dist/webhooks/verify.d.ts +11 -0
  236. package/dist/webhooks/verify.d.ts.map +1 -0
  237. package/dist/whiteboard/file-http-transport.d.ts +9 -0
  238. package/dist/whiteboard/file-http-transport.d.ts.map +1 -0
  239. package/dist/whiteboard/index.d.ts +7 -0
  240. package/dist/whiteboard/index.d.ts.map +1 -0
  241. package/dist/whiteboard/types.d.ts +139 -0
  242. package/dist/whiteboard/types.d.ts.map +1 -0
  243. package/dist/whiteboard/v1-client.d.ts +18 -0
  244. package/dist/whiteboard/v1-client.d.ts.map +1 -0
  245. package/dist/whiteboard/v1-codec.d.ts +5 -0
  246. package/dist/whiteboard/v1-codec.d.ts.map +1 -0
  247. package/dist/whiteboard/v1-create.d.ts +8 -0
  248. package/dist/whiteboard/v1-create.d.ts.map +1 -0
  249. package/dist/whiteboard/v1-multipart.d.ts +30 -0
  250. package/dist/whiteboard/v1-multipart.d.ts.map +1 -0
  251. package/dist/whiteboard/v1-persistence.d.ts +9 -0
  252. package/dist/whiteboard/v1-persistence.d.ts.map +1 -0
  253. package/package.json +222 -0
@@ -0,0 +1,551 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/server/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ChalkAPIError: () => ChalkAPIError,
24
+ ChalkServerOnlyError: () => ChalkServerOnlyError,
25
+ createChalkServerClient: () => createChalkServerClient
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/access/base64-url.ts
30
+ function decodeBase64Url(value) {
31
+ const base64 = value.replaceAll("-", "+").replaceAll("_", "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
32
+ const bytes = Uint8Array.from(globalThis.atob(base64), (character) => character.charCodeAt(0));
33
+ return new TextDecoder().decode(bytes);
34
+ }
35
+
36
+ // src/access/episode-diagnostic-credential.ts
37
+ var INTAKE_PATH = "/_internal/episode-diagnostic-events";
38
+ var RFC3339_ZONED = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
39
+ function parseEpisodeDiagnosticCredential(value) {
40
+ if (!isRecord(value)) return null;
41
+ const token = value.token;
42
+ const expiresAt = value.expires_at;
43
+ const generation = value.generation;
44
+ const intakePath = value.intake_path;
45
+ if (typeof token !== "string" || !validDiagnosticCredential(token)) return null;
46
+ if (!isRFC3339Zoned(expiresAt)) return null;
47
+ if (!Number.isSafeInteger(generation) || generation < 1) return null;
48
+ if (intakePath !== INTAKE_PATH) return null;
49
+ return Object.freeze({ token, expiresAt, generation, intakePath });
50
+ }
51
+ function validDiagnosticCredential(token) {
52
+ const segments = token.split(".");
53
+ if (segments.length !== 3) return false;
54
+ try {
55
+ const payload = JSON.parse(decodeBase64Url(segments[1] ?? ""));
56
+ return isRecord(payload) && payload.aud === "chalk-diagnostics";
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+ function isRFC3339Zoned(value) {
62
+ if (typeof value !== "string") return false;
63
+ const match = RFC3339_ZONED.exec(value);
64
+ if (!match) return false;
65
+ return validTimestampFields(match) && Number.isFinite(Date.parse(value));
66
+ }
67
+ function validTimestampFields(match) {
68
+ const year = Number(match[1]);
69
+ const month = Number(match[2]);
70
+ const day = Number(match[3]);
71
+ const hour = Number(match[4]);
72
+ const minute = Number(match[5]);
73
+ const second = Number(match[6]);
74
+ const zone = match[7] ?? "";
75
+ return validCalendarDate(year, month, day) && validClockTime(hour, minute, second) && validZoneOffset(zone);
76
+ }
77
+ function inRange(value, min, max) {
78
+ return value >= min && value <= max;
79
+ }
80
+ function validCalendarDate(year, month, day) {
81
+ return inRange(month, 1, 12) && inRange(day, 1, daysInMonth(year, month));
82
+ }
83
+ function validClockTime(hour, minute, second) {
84
+ return inRange(hour, 0, 23) && inRange(minute, 0, 59) && inRange(second, 0, 59);
85
+ }
86
+ function validZoneOffset(zone) {
87
+ if (zone === "Z") return true;
88
+ return inRange(Number(zone.slice(1, 3)), 0, 23) && inRange(Number(zone.slice(4, 6)), 0, 59);
89
+ }
90
+ function daysInMonth(year, month) {
91
+ if (month === 2) return isLeapYear(year) ? 29 : 28;
92
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
93
+ }
94
+ function isLeapYear(year) {
95
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
96
+ }
97
+ function isRecord(value) {
98
+ return typeof value === "object" && value !== null && !Array.isArray(value);
99
+ }
100
+
101
+ // src/access/grant.ts
102
+ var accessGrantBrand = /* @__PURE__ */ Symbol("AccessGrant");
103
+ var AccessGrantError = class extends TypeError {
104
+ code = "access.invalid";
105
+ constructor(message = "Access grant is invalid") {
106
+ super(message);
107
+ this.name = "AccessGrantError";
108
+ }
109
+ };
110
+ function parseParsedAccessGrant(value) {
111
+ if (!isRecord2(value)) throw new AccessGrantError();
112
+ const subject = parseSubject(value.subject);
113
+ const diagnostics = parseEpisodeDiagnosticCredential(value.diagnostics);
114
+ return {
115
+ subject,
116
+ sync: parseSyncAccess(value.sync),
117
+ media: parseMediaAccess(value.media),
118
+ diagnostics: diagnostics?.generation === subject.participantGeneration ? diagnostics : null
119
+ };
120
+ }
121
+ function parseAccessGrant(value) {
122
+ return accessGrantFromParsed(parseParsedAccessGrant(value));
123
+ }
124
+ function accessGrantFromParsed(value) {
125
+ return Object.freeze({
126
+ subject: {
127
+ tenant_id: value.subject.tenantId,
128
+ space_id: value.subject.spaceId,
129
+ episode_id: value.subject.episodeId,
130
+ participant_id: value.subject.participantId,
131
+ participant_generation: value.subject.participantGeneration
132
+ },
133
+ sync: { token: value.sync.token, expires_at: value.sync.expiresAt },
134
+ media: {
135
+ token: value.media.token,
136
+ expires_at: value.media.expiresAt,
137
+ provider: value.media.provider,
138
+ client_payload: { ...value.media.clientPayload }
139
+ },
140
+ ...value.diagnostics ? {
141
+ diagnostics: {
142
+ token: value.diagnostics.token,
143
+ expires_at: value.diagnostics.expiresAt,
144
+ generation: value.diagnostics.generation,
145
+ intake_path: value.diagnostics.intakePath
146
+ }
147
+ } : {},
148
+ [accessGrantBrand]: "AccessGrant"
149
+ });
150
+ }
151
+ function parseSubject(value) {
152
+ if (!isRecord2(value)) throw new AccessGrantError();
153
+ return {
154
+ tenantId: requireNonEmptyString(value.tenant_id),
155
+ spaceId: requireNonEmptyString(value.space_id),
156
+ episodeId: requireNonEmptyString(value.episode_id),
157
+ participantId: requireNonEmptyString(value.participant_id),
158
+ participantGeneration: requirePositiveInteger(value.participant_generation)
159
+ };
160
+ }
161
+ function requirePositiveInteger(value) {
162
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) throw new AccessGrantError();
163
+ return value;
164
+ }
165
+ function parseSyncAccess(value) {
166
+ if (!isRecord2(value)) throw new AccessGrantError();
167
+ return {
168
+ token: requireCredential(value.token, "chalk-sync"),
169
+ expiresAt: requireDateTime(value.expires_at ?? value.expiresAt)
170
+ };
171
+ }
172
+ function parseMediaAccess(value) {
173
+ if (!isRecord2(value) || value.provider !== "cloudflare_sfu" || !isRecord2(value.client_payload ?? value.clientPayload)) throw new AccessGrantError();
174
+ const payload = value.client_payload ?? value.clientPayload;
175
+ return {
176
+ token: requireCredential(value.token, "chalk-media"),
177
+ expiresAt: requireDateTime(value.expires_at ?? value.expiresAt),
178
+ provider: value.provider,
179
+ clientPayload: {
180
+ connectionId: requireNonEmptyString(payload.connectionId),
181
+ stunServer: requireNonEmptyString(payload.stunServer)
182
+ }
183
+ };
184
+ }
185
+ function requireCredential(value, audience) {
186
+ const token = requireNonEmptyString(value);
187
+ const segments = token.split(".");
188
+ if (segments.length !== 3) throw new AccessGrantError();
189
+ let payload;
190
+ try {
191
+ payload = JSON.parse(decodeBase64Url(segments[1] ?? ""));
192
+ } catch {
193
+ throw new AccessGrantError();
194
+ }
195
+ if (!isRecord2(payload) || payload.aud !== audience) throw new AccessGrantError();
196
+ return token;
197
+ }
198
+ function requireDateTime(value) {
199
+ const dateTime = requireNonEmptyString(value);
200
+ if (!Number.isFinite(Date.parse(dateTime))) throw new AccessGrantError();
201
+ return dateTime;
202
+ }
203
+ function requireNonEmptyString(value) {
204
+ if (typeof value !== "string" || value.trim().length === 0) throw new AccessGrantError();
205
+ return value;
206
+ }
207
+ function isRecord2(value) {
208
+ return typeof value === "object" && value !== null && !Array.isArray(value);
209
+ }
210
+
211
+ // src/server/errors.ts
212
+ var safeCodePattern = /^[a-z][a-z0-9_]{0,63}$/u;
213
+ var safeRequestIdPattern = /^[A-Za-z0-9._:-]{1,128}$/u;
214
+ var ChalkAPIError = class extends Error {
215
+ code;
216
+ requestId;
217
+ retryable;
218
+ status;
219
+ constructor(input) {
220
+ super(messageFor(input.code, input.status));
221
+ this.name = "ChalkAPIError";
222
+ this.code = input.code;
223
+ this.status = input.status;
224
+ this.retryable = input.retryable;
225
+ this.requestId = input.requestId;
226
+ }
227
+ };
228
+ var ChalkServerOnlyError = class extends Error {
229
+ constructor() {
230
+ super("The Chalk server client is available only in Node.js runtimes.");
231
+ this.name = "ChalkServerOnlyError";
232
+ }
233
+ };
234
+ function errorCode(value) {
235
+ return safeErrorCode(errorCodeValue(value));
236
+ }
237
+ function requestId(headers) {
238
+ return safeRequestId(headers.get("x-request-id") ?? headers.get("x-chalk-request-id"));
239
+ }
240
+ function errorCodeValue(value) {
241
+ if (!isRecord3(value)) return void 0;
242
+ if (!isRecord3(value.error)) return void 0;
243
+ return value.error.code;
244
+ }
245
+ function safeErrorCode(value) {
246
+ if (typeof value !== "string") return "request_failed";
247
+ if (!safeCodePattern.test(value)) return "request_failed";
248
+ return value;
249
+ }
250
+ function safeRequestId(value) {
251
+ if (value === null) return void 0;
252
+ if (!safeRequestIdPattern.test(value)) return void 0;
253
+ return value;
254
+ }
255
+ function messageFor(code, status) {
256
+ if (code === "network_error") return "The Chalk API could not be reached.";
257
+ if (code === "invalid_response") return "The Chalk API returned an invalid response.";
258
+ return status > 0 ? `The Chalk API request failed with HTTP ${status}.` : "The Chalk API request failed.";
259
+ }
260
+ function isRecord3(value) {
261
+ if (typeof value !== "object") return false;
262
+ if (value === null) return false;
263
+ return !Array.isArray(value);
264
+ }
265
+
266
+ // src/server/transport.ts
267
+ var retryableStatuses = /* @__PURE__ */ new Set([429, 502, 503, 504]);
268
+ var maxRetries = 2;
269
+ function createServerRequester(options, apiKey, apiBaseURL, fetchImplementation) {
270
+ return (input) => {
271
+ const suppliedKey = input.idempotency?.idempotencyKey;
272
+ const attempt = {
273
+ apiBaseURL,
274
+ apiKey,
275
+ fetch: fetchImplementation,
276
+ idempotencyKey: idempotencyKey(input.retry, suppliedKey),
277
+ input,
278
+ options,
279
+ retries: retryCount(input.retry, suppliedKey)
280
+ };
281
+ return performAttempt(attempt, 0);
282
+ };
283
+ }
284
+ async function performAttempt(attempt, index) {
285
+ const response = await fetchAttempt(attempt, index);
286
+ if (response.status === attempt.input.expectedStatus) return decodeSuccess(response);
287
+ if (canRetryResponse(response, attempt.retries, index)) return retryResponse(response, attempt, index);
288
+ throw await responseError(response);
289
+ }
290
+ async function fetchAttempt(attempt, index) {
291
+ try {
292
+ return await attempt.fetch(requestURL(attempt), requestInit(attempt));
293
+ } catch {
294
+ return retryTransport(attempt, index);
295
+ }
296
+ }
297
+ async function retryTransport(attempt, index) {
298
+ if (index >= attempt.retries) throw new ChalkAPIError({ code: "network_error", retryable: true, status: 0 });
299
+ await retryDelay(index);
300
+ return fetchAttempt(attempt, index + 1);
301
+ }
302
+ async function retryResponse(response, attempt, index) {
303
+ await discard(response);
304
+ await retryDelay(index);
305
+ return performAttempt(attempt, index + 1);
306
+ }
307
+ async function decodeSuccess(response) {
308
+ if (response.status === 204) return void 0;
309
+ try {
310
+ return await response.json();
311
+ } catch {
312
+ throw new ChalkAPIError({ code: "invalid_response", requestId: requestId(response.headers), retryable: false, status: response.status });
313
+ }
314
+ }
315
+ function canRetryResponse(response, retries, index) {
316
+ if (index >= retries) return false;
317
+ return retryableStatuses.has(response.status);
318
+ }
319
+ function requestURL(attempt) {
320
+ return new URL(attempt.input.path.replace(/^\/+/u, ""), attempt.apiBaseURL);
321
+ }
322
+ function requestInit(attempt) {
323
+ return {
324
+ method: attempt.input.method,
325
+ headers: requestHeaders(attempt.options, attempt.apiKey, attempt.input.body !== void 0, attempt.idempotencyKey),
326
+ ...serializedBody(attempt.input.body)
327
+ };
328
+ }
329
+ function serializedBody(body) {
330
+ if (body === void 0) return {};
331
+ return { body: JSON.stringify(body) };
332
+ }
333
+ function requestHeaders(options, apiKey, hasBody, idempotencyKeyValue) {
334
+ const headers = new Headers(options.headers);
335
+ setContentType(headers, hasBody);
336
+ setTelemetryHeaders(headers, options);
337
+ setOptionalHeader(headers, "idempotency-key", idempotencyKeyValue);
338
+ headers.set("authorization", `Bearer ${apiKey}`);
339
+ return headers;
340
+ }
341
+ function setContentType(headers, hasBody) {
342
+ if (hasBody) headers.set("content-type", "application/json");
343
+ }
344
+ function setTelemetryHeaders(headers, options) {
345
+ if (!options.telemetry) return;
346
+ headers.set("x-chalk-journey-id", options.telemetry.journeyId);
347
+ headers.set("traceparent", options.telemetry.traceparent);
348
+ setOptionalHeader(headers, "tracestate", options.telemetry.tracestate);
349
+ }
350
+ function setOptionalHeader(headers, name, value) {
351
+ if (value !== void 0) headers.set(name, value);
352
+ }
353
+ async function responseError(response) {
354
+ return new ChalkAPIError({ code: errorCode(await errorBody(response)), requestId: requestId(response.headers), retryable: retryableStatuses.has(response.status), status: response.status });
355
+ }
356
+ async function errorBody(response) {
357
+ try {
358
+ return await response.json();
359
+ } catch {
360
+ return void 0;
361
+ }
362
+ }
363
+ function idempotencyKey(mode, suppliedKey) {
364
+ if (mode !== "caller_idempotency") return void 0;
365
+ return suppliedKey ?? globalThis.crypto.randomUUID();
366
+ }
367
+ function retryCount(mode, suppliedKey) {
368
+ if (mode === "always") return maxRetries;
369
+ if (mode === "caller_idempotency") return suppliedKey === void 0 ? 0 : maxRetries;
370
+ return 0;
371
+ }
372
+ async function discard(response) {
373
+ try {
374
+ await response.arrayBuffer();
375
+ } catch {
376
+ }
377
+ }
378
+ function retryDelay(attempt) {
379
+ const ceiling = Math.min(400, 50 * 2 ** attempt);
380
+ return new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (ceiling + 1))));
381
+ }
382
+
383
+ // src/server/client.ts
384
+ function createChalkServerClient(options) {
385
+ const apiKey = required(options.apiKey, "apiKey");
386
+ const tenantId = required(options.tenantId, "tenantId");
387
+ const apiBaseURL = normalizedBaseURL(options.apiBaseURL);
388
+ const fetchImplementation = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
389
+ if (typeof fetchImplementation !== "function") throw new TypeError("A fetch implementation is required");
390
+ const tenantPath = `/v1/tenants/${segment(tenantId)}`;
391
+ const request = createServerRequester(options, apiKey, apiBaseURL, fetchImplementation);
392
+ return {
393
+ spaces: {
394
+ create: (input) => request({ method: "POST", path: `${tenantPath}/spaces`, body: createSpaceRequest(input), expectedStatus: 201, retry: "never" })
395
+ },
396
+ episodes: {
397
+ create: (spaceId, input, idempotency) => request({ method: "POST", path: `${tenantPath}/spaces/${segment(spaceId)}/episodes`, body: createEpisodeRequest(input), expectedStatus: 201, idempotency, retry: "caller_idempotency" }),
398
+ end: (spaceId, episodeId, idempotency) => request({ method: "POST", path: `${tenantPath}/spaces/${segment(spaceId)}/episodes/${segment(episodeId)}/end`, expectedStatus: 202, idempotency, retry: "caller_idempotency" })
399
+ },
400
+ participants: {
401
+ admit: async (spaceId, episodeId, input, idempotency) => {
402
+ const lifecycle = await request({
403
+ method: "POST",
404
+ path: `${tenantPath}/spaces/${segment(spaceId)}/episodes/${segment(episodeId)}/participants`,
405
+ body: participantAdmissionRequest(input),
406
+ expectedStatus: 201,
407
+ idempotency,
408
+ retry: "caller_idempotency"
409
+ });
410
+ return participantLifecycle(lifecycle);
411
+ },
412
+ issueAccess: async (spaceId, episodeId, participantId, input) => {
413
+ const access = await request({
414
+ method: "POST",
415
+ path: `${tenantPath}/spaces/${segment(spaceId)}/episodes/${segment(episodeId)}/participants/${segment(participantId)}/access-grant`,
416
+ body: accessGrantRequest(input),
417
+ expectedStatus: 201,
418
+ retry: input.replaceMediaConnection === true ? "never" : "always"
419
+ });
420
+ return accessGrant(access);
421
+ },
422
+ remove: (spaceId, episodeId, participantId, input, idempotency) => request({
423
+ method: "POST",
424
+ path: `${tenantPath}/spaces/${segment(spaceId)}/episodes/${segment(episodeId)}/participants/${segment(participantId)}/remove`,
425
+ body: participantRemovalRequest(input),
426
+ expectedStatus: 202,
427
+ idempotency,
428
+ retry: "caller_idempotency"
429
+ })
430
+ },
431
+ apiKeys: {
432
+ create: (input) => request({ method: "POST", path: `${tenantPath}/api-keys`, body: apiKeyCreateRequest(input), expectedStatus: 201, retry: "never" }),
433
+ list: (input) => request({ method: "GET", path: `${tenantPath}/api-keys${apiKeyQuery(input)}`, expectedStatus: 200, retry: "always" }),
434
+ rotate: (apiKeyId, input) => request({ method: "POST", path: `${tenantPath}/api-keys/${segment(apiKeyId)}/rotate`, body: { expires_at: input?.expiresAt }, expectedStatus: 200, retry: "never" }),
435
+ revoke: (apiKeyId) => request({ method: "DELETE", path: `${tenantPath}/api-keys/${segment(apiKeyId)}`, expectedStatus: 204, retry: "always" })
436
+ }
437
+ };
438
+ }
439
+ function createSpaceRequest(input) {
440
+ return {
441
+ default_episode_duration_seconds: input.defaultEpisodeDurationSeconds,
442
+ linger_window_seconds: input.lingerWindowSeconds,
443
+ maximum_episode_duration_seconds: input.maximumEpisodeDurationSeconds,
444
+ media_plane: input.mediaPlane,
445
+ name: input.name,
446
+ slug: input.slug,
447
+ ...input.admissionPolicy === void 0 ? {} : { admission_policy: input.admissionPolicy },
448
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata },
449
+ ...input.recurringPolicy === void 0 ? {} : { recurring_policy: input.recurringPolicy }
450
+ };
451
+ }
452
+ function createEpisodeRequest(input) {
453
+ return {
454
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata },
455
+ ...input.startedAt === void 0 ? {} : { started_at: input.startedAt }
456
+ };
457
+ }
458
+ function participantAdmissionRequest(input) {
459
+ return {
460
+ name: input.name,
461
+ role: input.role,
462
+ ...input.identityId === void 0 ? {} : { identity_id: input.identityId },
463
+ ...input.metadata === void 0 ? {} : { metadata: input.metadata },
464
+ ...input.participantId === void 0 ? {} : { participant_id: input.participantId }
465
+ };
466
+ }
467
+ function accessGrantRequest(input) {
468
+ return {
469
+ participant_generation: input.participantGeneration,
470
+ replace_media_connection: input.replaceMediaConnection ?? false,
471
+ ...input.currentMediaToken ? { current_media_token: input.currentMediaToken } : {}
472
+ };
473
+ }
474
+ function participantRemovalRequest(input) {
475
+ return { participant_generation: input.participantGeneration };
476
+ }
477
+ function apiKeyCreateRequest(input) {
478
+ return { expires_at: input.expiresAt, name: input.name, scopes: [...input.scopes] };
479
+ }
480
+ function apiKeyQuery(input) {
481
+ const query = new URLSearchParams();
482
+ setQueryValue(query, "cursor", input?.cursor);
483
+ setQueryValue(query, "page_size", input?.pageSize);
484
+ return prefixedQuery(query.toString());
485
+ }
486
+ function setQueryValue(query, name, value) {
487
+ if (value !== void 0) query.set(name, String(value));
488
+ }
489
+ function prefixedQuery(value) {
490
+ if (value === "") return "";
491
+ return `?${value}`;
492
+ }
493
+ function required(value, name) {
494
+ const normalized = value.trim();
495
+ if (!normalized) throw new TypeError(`${name} is required`);
496
+ return normalized;
497
+ }
498
+ function normalizedBaseURL(value) {
499
+ const url = new URL(value);
500
+ url.pathname = `${url.pathname.replace(/\/+$/u, "")}/`;
501
+ url.search = "";
502
+ url.hash = "";
503
+ return url.toString();
504
+ }
505
+ function segment(value) {
506
+ return encodeURIComponent(required(value, "identifier"));
507
+ }
508
+ function participantLifecycle(value) {
509
+ const { access, ...lifecycle } = value;
510
+ if (access === void 0) return lifecycle;
511
+ return { ...lifecycle, access: access === null ? null : accessGrant(access) };
512
+ }
513
+ function accessGrant(value) {
514
+ const payload = cloudflareClientPayload(value.media);
515
+ return parseAccessGrant({
516
+ subject: {
517
+ tenant_id: value.subject.tenant_id,
518
+ space_id: requiredResponseID(value.subject.space_id),
519
+ episode_id: requiredResponseID(value.subject.episode_id),
520
+ participant_id: requiredResponseID(value.subject.participant_id),
521
+ participant_generation: value.subject.participant_generation
522
+ },
523
+ sync: value.sync,
524
+ media: {
525
+ token: value.media.token,
526
+ expires_at: value.media.expires_at,
527
+ provider: "cloudflare_sfu",
528
+ client_payload: payload
529
+ }
530
+ });
531
+ }
532
+ function cloudflareClientPayload(media) {
533
+ const { connectionId, stunServer } = media.client_payload;
534
+ if (media.provider !== "cloudflare_sfu" || typeof connectionId !== "string" || typeof stunServer !== "string") {
535
+ throw invalidResponse(201);
536
+ }
537
+ return { connectionId, stunServer };
538
+ }
539
+ function requiredResponseID(value) {
540
+ if (!value) throw invalidResponse(201);
541
+ return value;
542
+ }
543
+ function invalidResponse(status) {
544
+ return new ChalkAPIError({ code: "invalid_response", retryable: false, status });
545
+ }
546
+ // Annotate the CommonJS export names for ESM import in node:
547
+ 0 && (module.exports = {
548
+ ChalkAPIError,
549
+ ChalkServerOnlyError,
550
+ createChalkServerClient
551
+ });
@@ -0,0 +1,3 @@
1
+ export { createChalkServerClient } from "./client.cjs";
2
+ export { ChalkAPIError, ChalkServerOnlyError } from "./errors.cjs";
3
+ export type { APIKey, APIKeyList, APIKeyWithSecret, AdmitParticipantInput, ChalkIdempotencyOptions, ChalkServerClient, ChalkServerClientOptions, ChalkServerHeaders, ChalkServerTelemetry, CreateAPIKeyInput, CreateEpisodeInput, CreateSpaceInput, Episode, EpisodeEnd, IssueAccessGrantInput, ListAPIKeysInput, AccessGrant, ParticipantLifecycle, ParticipantRemoval, RemoveParticipantInput, RotateAPIKeyInput, Space, } from "./types.cjs";
@@ -0,0 +1,4 @@
1
+ export { createChalkServerClient } from "./client.js";
2
+ export { ChalkAPIError, ChalkServerOnlyError } from "./errors.js";
3
+ export type { APIKey, APIKeyList, APIKeyWithSecret, AdmitParticipantInput, ChalkIdempotencyOptions, ChalkServerClient, ChalkServerClientOptions, ChalkServerHeaders, ChalkServerTelemetry, CreateAPIKeyInput, CreateEpisodeInput, CreateSpaceInput, Episode, EpisodeEnd, IssueAccessGrantInput, ListAPIKeysInput, AccessGrant, ParticipantLifecycle, ParticipantRemoval, RemoveParticipantInput, RotateAPIKeyInput, Space, } from "./types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EACV,MAAM,EACN,UAAU,EACV,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,OAAO,EACP,UAAU,EACV,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,GACN,MAAM,YAAY,CAAC"}