@omg-dev/sdk 0.4.29 → 0.4.31

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.
@@ -0,0 +1,222 @@
1
+ import { useId, useState } from "react";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/brand/OmgBadge.tsx
4
+ const BRAND = "#FF5530";
5
+ const Z_BADGE = 2147482e3;
6
+ const Z_DIALOG = 2147483100;
7
+ function OmgBadge({ href = "https://omg.dev", label = "What is omg?" }) {
8
+ const [open, setOpen] = useState(false);
9
+ const titleId = useId();
10
+ const descriptionId = useId();
11
+ const markId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
12
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("button", {
13
+ type: "button",
14
+ "aria-label": label,
15
+ onClick: () => setOpen(true),
16
+ style: badgeStyle,
17
+ children: [/* @__PURE__ */ jsx(OmgMark, {
18
+ size: 18,
19
+ id: `badge-${markId}`
20
+ }), /* @__PURE__ */ jsx("span", {
21
+ style: badgeTextStyle,
22
+ children: "omg"
23
+ })]
24
+ }), open && /* @__PURE__ */ jsx("div", {
25
+ role: "presentation",
26
+ style: backdropStyle,
27
+ onMouseDown: (event) => {
28
+ if (event.target === event.currentTarget) setOpen(false);
29
+ },
30
+ children: /* @__PURE__ */ jsxs("div", {
31
+ role: "dialog",
32
+ "aria-modal": "true",
33
+ "aria-labelledby": titleId,
34
+ "aria-describedby": descriptionId,
35
+ style: dialogStyle,
36
+ children: [
37
+ /* @__PURE__ */ jsx("button", {
38
+ type: "button",
39
+ "aria-label": "Close",
40
+ onClick: () => setOpen(false),
41
+ style: closeButtonStyle,
42
+ children: /* @__PURE__ */ jsx("svg", {
43
+ "aria-hidden": "true",
44
+ width: "18",
45
+ height: "18",
46
+ viewBox: "0 0 24 24",
47
+ fill: "none",
48
+ children: /* @__PURE__ */ jsx("path", {
49
+ d: "M6 6l12 12M18 6 6 18",
50
+ stroke: "currentColor",
51
+ strokeWidth: "2",
52
+ strokeLinecap: "round"
53
+ })
54
+ })
55
+ }),
56
+ /* @__PURE__ */ jsx("div", {
57
+ style: markWrapStyle,
58
+ children: /* @__PURE__ */ jsx(OmgMark, {
59
+ size: 34,
60
+ id: `dialog-${markId}`
61
+ })
62
+ }),
63
+ /* @__PURE__ */ jsx("h2", {
64
+ id: titleId,
65
+ style: titleStyle,
66
+ children: "Built with omg"
67
+ }),
68
+ /* @__PURE__ */ jsx("p", {
69
+ id: descriptionId,
70
+ style: bodyStyle,
71
+ children: "omg turns a plain-language idea into a real app, then hosts it with sign-in, data storage, and publishing already handled."
72
+ }),
73
+ /* @__PURE__ */ jsx("a", {
74
+ href,
75
+ target: "_blank",
76
+ rel: "noopener noreferrer",
77
+ style: ctaStyle,
78
+ children: "Learn about omg"
79
+ })
80
+ ]
81
+ })
82
+ })] });
83
+ }
84
+ function OmgMark({ size, id }) {
85
+ const maskId = `omg-mark-cut-${id}`;
86
+ return /* @__PURE__ */ jsxs("svg", {
87
+ "aria-hidden": "true",
88
+ width: size,
89
+ height: size,
90
+ viewBox: "0 0 100 100",
91
+ fill: "none",
92
+ style: {
93
+ display: "block",
94
+ flex: "0 0 auto"
95
+ },
96
+ children: [/* @__PURE__ */ jsxs("mask", {
97
+ id: maskId,
98
+ children: [/* @__PURE__ */ jsx("rect", {
99
+ width: "100",
100
+ height: "100",
101
+ fill: "white"
102
+ }), /* @__PURE__ */ jsx("circle", {
103
+ cx: "71",
104
+ cy: "29",
105
+ r: size <= 16 ? 16.5 : size <= 20 ? 15.2 : size <= 32 ? 14 : 13.2,
106
+ fill: "black"
107
+ })]
108
+ }), /* @__PURE__ */ jsx("circle", {
109
+ cx: "50",
110
+ cy: "50",
111
+ r: "44",
112
+ fill: BRAND,
113
+ mask: `url(#${maskId})`
114
+ })]
115
+ });
116
+ }
117
+ const font = "system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif";
118
+ const badgeStyle = {
119
+ position: "fixed",
120
+ right: "max(14px, env(safe-area-inset-right))",
121
+ bottom: "calc(16px + env(safe-area-inset-bottom))",
122
+ zIndex: Z_BADGE,
123
+ height: 38,
124
+ padding: "0 8px 0 10px",
125
+ border: "1px solid rgba(20, 16, 12, 0.12)",
126
+ borderRadius: 999,
127
+ background: "rgba(255, 255, 255, 0.94)",
128
+ color: "#15110d",
129
+ boxShadow: "0 8px 28px rgba(0, 0, 0, 0.16)",
130
+ display: "inline-flex",
131
+ alignItems: "center",
132
+ gap: 7,
133
+ fontFamily: font,
134
+ cursor: "pointer",
135
+ textDecoration: "none",
136
+ WebkitTapHighlightColor: "transparent",
137
+ backdropFilter: "blur(10px)"
138
+ };
139
+ const badgeTextStyle = {
140
+ fontSize: 13,
141
+ lineHeight: "18px",
142
+ fontWeight: 700,
143
+ letterSpacing: 0,
144
+ padding: "0 3px"
145
+ };
146
+ const backdropStyle = {
147
+ position: "fixed",
148
+ inset: 0,
149
+ zIndex: Z_DIALOG,
150
+ display: "grid",
151
+ placeItems: "center",
152
+ padding: "24px 16px calc(24px + env(safe-area-inset-bottom))",
153
+ background: "rgba(15, 13, 11, 0.34)",
154
+ boxSizing: "border-box"
155
+ };
156
+ const dialogStyle = {
157
+ position: "relative",
158
+ width: "min(100%, 360px)",
159
+ borderRadius: 18,
160
+ padding: "26px 22px 20px",
161
+ background: "#fffaf4",
162
+ color: "#15110d",
163
+ boxShadow: "0 24px 70px rgba(0, 0, 0, 0.28)",
164
+ border: "1px solid rgba(21, 17, 13, 0.08)",
165
+ fontFamily: font,
166
+ boxSizing: "border-box"
167
+ };
168
+ const closeButtonStyle = {
169
+ position: "absolute",
170
+ top: 10,
171
+ right: 10,
172
+ width: 34,
173
+ height: 34,
174
+ borderRadius: 999,
175
+ border: "none",
176
+ background: "transparent",
177
+ color: "#6b6459",
178
+ display: "grid",
179
+ placeItems: "center",
180
+ cursor: "pointer"
181
+ };
182
+ const markWrapStyle = {
183
+ width: 52,
184
+ height: 52,
185
+ borderRadius: 16,
186
+ display: "grid",
187
+ placeItems: "center",
188
+ background: "#fff",
189
+ boxShadow: "inset 0 0 0 1px rgba(21, 17, 13, 0.08)",
190
+ marginBottom: 16
191
+ };
192
+ const titleStyle = {
193
+ margin: 0,
194
+ color: "#15110d",
195
+ fontSize: 23,
196
+ lineHeight: "28px",
197
+ fontWeight: 750,
198
+ letterSpacing: 0
199
+ };
200
+ const bodyStyle = {
201
+ margin: "10px 0 18px",
202
+ color: "#5f574d",
203
+ fontSize: 15,
204
+ lineHeight: "22px",
205
+ letterSpacing: 0
206
+ };
207
+ const ctaStyle = {
208
+ display: "inline-flex",
209
+ alignItems: "center",
210
+ justifyContent: "center",
211
+ width: "100%",
212
+ height: 44,
213
+ borderRadius: 12,
214
+ background: "#15110d",
215
+ color: "#fffaf4",
216
+ fontSize: 15,
217
+ fontWeight: 700,
218
+ textDecoration: "none",
219
+ letterSpacing: 0
220
+ };
221
+ //#endregion
222
+ export { OmgBadge as t };
@@ -1,4 +1,4 @@
1
- import { t as OmgBadge } from "../OmgBadge-CDnTIlnn.mjs";
1
+ import { t as OmgBadge } from "../OmgBadge-C559Dp1f.mjs";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { createRoot } from "react-dom/client";
4
4
  //#region src/brand/auto.tsx
@@ -8,7 +8,6 @@ function mount() {
8
8
  if (document.getElementById(HOST_ID)) return;
9
9
  const host = document.createElement("div");
10
10
  host.id = HOST_ID;
11
- host.setAttribute("data-vibes-feedback", "");
12
11
  document.body.appendChild(host);
13
12
  createRoot(host).render(/* @__PURE__ */ jsx(OmgBadge, {}));
14
13
  }
package/dist/index.mjs CHANGED
@@ -1,11 +1,46 @@
1
- import { a as installTrace, c as requestMotionPermission, d as getAuthContext, f as notifyAuthRequired, h as subscribeAuthRequired, i as getTrace, l as useFeedbackGesture, m as subscribeAuthChange, n as captureScreenshot, o as attachGestureListeners, p as setAuthContext, r as clearTrace, s as motionPermissionState, t as VibesFeedback, u as useUpload } from "./VibesFeedback-BF2Vf6FK.mjs";
2
- import { t as OmgBadge } from "./OmgBadge-CDnTIlnn.mjs";
1
+ import { t as OmgBadge } from "./OmgBadge-C559Dp1f.mjs";
3
2
  import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
3
  import { fetchEventSource } from "@microsoft/fetch-event-source";
5
4
  import { createAuthClient } from "better-auth/react";
6
5
  import { passkeyClient } from "@better-auth/passkey/client";
7
6
  import { emailOTPClient, magicLinkClient } from "better-auth/client/plugins";
8
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/auth/bridge.ts
9
+ let current = {
10
+ user: null,
11
+ token: null,
12
+ authReady: false
13
+ };
14
+ const authChangeListeners = /* @__PURE__ */ new Set();
15
+ function setAuthContext(snap) {
16
+ current = snap;
17
+ for (const cb of authChangeListeners) cb(snap);
18
+ }
19
+ function getAuthContext() {
20
+ return current;
21
+ }
22
+ /**
23
+ * Subscribe to auth snapshot changes (user / token / authReady). Returns an
24
+ * unsubscribe fn. Data hooks use this to re-establish a subscription when the
25
+ * JWT finishes loading (null→token) or on sign-out (token→null) — without it
26
+ * the WS connects once, often before getToken() resolves, and never retries
27
+ * with a bearer.
28
+ */
29
+ function subscribeAuthChange(cb) {
30
+ authChangeListeners.add(cb);
31
+ return () => authChangeListeners.delete(cb);
32
+ }
33
+ const authRequiredListeners = /* @__PURE__ */ new Set();
34
+ /** Subscribe to auth-required signals. Returns an unsubscribe fn. */
35
+ function subscribeAuthRequired(cb) {
36
+ authRequiredListeners.add(cb);
37
+ return () => authRequiredListeners.delete(cb);
38
+ }
39
+ /** Notify all subscribers that a request hit the server's auth-required signal. */
40
+ function notifyAuthRequired() {
41
+ for (const cb of authRequiredListeners) cb();
42
+ }
43
+ //#endregion
9
44
  //#region src/auth/client.ts
10
45
  /**
11
46
  * Recover the user carried by a slug-scoped JWT.
@@ -650,6 +685,143 @@ function VibesAuthGuard({ children, fallback, loadingFallback }) {
650
685
  return /* @__PURE__ */ jsx(Fragment, { children });
651
686
  }
652
687
  //#endregion
688
+ //#region src/storage/useUpload.ts
689
+ function bearerHeaders() {
690
+ const { token } = getAuthContext();
691
+ return token ? { Authorization: `Bearer ${token}` } : {};
692
+ }
693
+ /**
694
+ * Headless upload hook. Returns the upload function plus state.
695
+ *
696
+ * Usage:
697
+ * const { upload, uploading, progress, error } = useUpload()
698
+ * await upload(file, { key: `avatars/${user.id}.png` })
699
+ */
700
+ function useUpload() {
701
+ const [state, setState] = useState({
702
+ uploading: false,
703
+ progress: 0,
704
+ error: null,
705
+ result: null
706
+ });
707
+ const reset = useCallback(() => {
708
+ setState({
709
+ uploading: false,
710
+ progress: 0,
711
+ error: null,
712
+ result: null
713
+ });
714
+ }, []);
715
+ const upload = useCallback(async (file, opts) => {
716
+ setState({
717
+ uploading: true,
718
+ progress: 0,
719
+ error: null,
720
+ result: null
721
+ });
722
+ const presignUrl = opts.presignUrl ?? "/api/_storage/presign";
723
+ const contentType = opts.contentType ?? file.type ?? "application/octet-stream";
724
+ try {
725
+ const presignRes = await fetch(presignUrl, {
726
+ method: "POST",
727
+ headers: {
728
+ "Content-Type": "application/json",
729
+ ...bearerHeaders()
730
+ },
731
+ body: JSON.stringify({
732
+ action: "put",
733
+ key: opts.key,
734
+ contentType,
735
+ scope: opts.scope ?? "user"
736
+ })
737
+ });
738
+ if (!presignRes.ok) {
739
+ if (presignRes.status === 401 && getAuthContext().authReady) notifyAuthRequired();
740
+ const text = await presignRes.text().catch(() => "");
741
+ throw new Error(`presign ${presignRes.status}: ${text.slice(0, 200)}`);
742
+ }
743
+ const { url, key: storedKey, maxBytes } = await presignRes.json();
744
+ if (typeof maxBytes === "number" && file.size > maxBytes) throw new Error(`file too large (${file.size} > ${maxBytes} bytes)`);
745
+ await new Promise((resolve, reject) => {
746
+ const xhr = new XMLHttpRequest();
747
+ xhr.open("PUT", url);
748
+ xhr.setRequestHeader("Content-Type", contentType);
749
+ xhr.upload.onprogress = (e) => {
750
+ if (e.lengthComputable) {
751
+ const frac = e.loaded / e.total;
752
+ setState((s) => ({
753
+ ...s,
754
+ progress: frac
755
+ }));
756
+ opts.onProgress?.(frac);
757
+ }
758
+ };
759
+ xhr.onload = () => {
760
+ if (xhr.status >= 200 && xhr.status < 300) resolve();
761
+ else reject(/* @__PURE__ */ new Error(`PUT ${xhr.status}: ${xhr.responseText.slice(0, 200)}`));
762
+ };
763
+ xhr.onerror = () => reject(/* @__PURE__ */ new Error("network error during upload"));
764
+ xhr.send(file);
765
+ });
766
+ const dlRes = await fetch(presignUrl, {
767
+ method: "POST",
768
+ headers: {
769
+ "Content-Type": "application/json",
770
+ ...bearerHeaders()
771
+ },
772
+ body: JSON.stringify({
773
+ action: "get",
774
+ key: opts.key,
775
+ scope: opts.scope ?? "user"
776
+ })
777
+ });
778
+ let downloadUrl = "";
779
+ if (dlRes.ok) downloadUrl = (await dlRes.json()).url ?? "";
780
+ fetch(presignUrl.replace(/\/presign$/, "/notify"), {
781
+ method: "POST",
782
+ headers: {
783
+ "Content-Type": "application/json",
784
+ ...bearerHeaders()
785
+ },
786
+ body: JSON.stringify({
787
+ action: "upload",
788
+ key: opts.key,
789
+ size: file.size,
790
+ contentType,
791
+ scope: opts.scope ?? "user"
792
+ })
793
+ }).catch(() => {});
794
+ const result = {
795
+ key: storedKey,
796
+ downloadUrl,
797
+ size: file.size,
798
+ contentType
799
+ };
800
+ setState({
801
+ uploading: false,
802
+ progress: 1,
803
+ error: null,
804
+ result
805
+ });
806
+ return result;
807
+ } catch (err) {
808
+ const msg = err.message ?? String(err);
809
+ setState({
810
+ uploading: false,
811
+ progress: 0,
812
+ error: msg,
813
+ result: null
814
+ });
815
+ throw err;
816
+ }
817
+ }, []);
818
+ return {
819
+ ...state,
820
+ upload,
821
+ reset
822
+ };
823
+ }
824
+ //#endregion
653
825
  //#region src/storage/VibesUpload.tsx
654
826
  function defaultKeyFor(file) {
655
827
  const safe = file.name.replace(/[^a-zA-Z0-9._-]/g, "-");
@@ -889,6 +1061,7 @@ async function createNotification(input) {
889
1061
  const runtimeClaims = {
890
1062
  llmInvoke: "llm.invoke",
891
1063
  mediaInvoke: "media.invoke",
1064
+ speechTranscribe: "speech.transcribe",
892
1065
  browserUse: "browser.use",
893
1066
  sandboxCreate: "sandbox.create",
894
1067
  sandboxDelegate: "sandbox.delegate"
@@ -1185,17 +1358,19 @@ function orderedRows(rows) {
1185
1358
  return Array.from(rows).sort((a, b) => compareRows(a, b));
1186
1359
  }
1187
1360
  var SharedCollectionSocket = class {
1361
+ url;
1362
+ token;
1363
+ ws = null;
1364
+ connecting = false;
1365
+ closedByManager = false;
1366
+ consecutiveErrors = 0;
1367
+ reconnectTimer = null;
1368
+ flushQueued = false;
1369
+ subs = /* @__PURE__ */ new Map();
1370
+ pendingSubIds = /* @__PURE__ */ new Set();
1188
1371
  constructor(url, token) {
1189
1372
  this.url = url;
1190
1373
  this.token = token;
1191
- this.ws = null;
1192
- this.connecting = false;
1193
- this.closedByManager = false;
1194
- this.consecutiveErrors = 0;
1195
- this.reconnectTimer = null;
1196
- this.flushQueued = false;
1197
- this.subs = /* @__PURE__ */ new Map();
1198
- this.pendingSubIds = /* @__PURE__ */ new Set();
1199
1374
  }
1200
1375
  add(sub) {
1201
1376
  this.subs.set(sub.subId, sub);
@@ -1421,24 +1596,29 @@ function makeInitialQuerySnapshot() {
1421
1596
  };
1422
1597
  }
1423
1598
  var CollectionQueryStore = class {
1599
+ key;
1600
+ url;
1601
+ collection;
1602
+ where;
1603
+ onIdle;
1604
+ listeners = /* @__PURE__ */ new Set();
1605
+ rowSubscribers = /* @__PURE__ */ new Set();
1606
+ socketHandle = null;
1607
+ fallbackHandle = null;
1608
+ authUnsub = null;
1609
+ authToken = getAuthContext().token;
1610
+ authReady = getAuthContext().authReady;
1611
+ lastSeq = null;
1612
+ pendingAuthRequired = null;
1613
+ notifyQueued = false;
1614
+ rowsDirty = false;
1615
+ snapshot = makeInitialQuerySnapshot();
1424
1616
  constructor(key, url, collection, where, onIdle) {
1425
1617
  this.key = key;
1426
1618
  this.url = url;
1427
1619
  this.collection = collection;
1428
1620
  this.where = where;
1429
1621
  this.onIdle = onIdle;
1430
- this.listeners = /* @__PURE__ */ new Set();
1431
- this.rowSubscribers = /* @__PURE__ */ new Set();
1432
- this.socketHandle = null;
1433
- this.fallbackHandle = null;
1434
- this.authUnsub = null;
1435
- this.authToken = getAuthContext().token;
1436
- this.authReady = getAuthContext().authReady;
1437
- this.lastSeq = null;
1438
- this.pendingAuthRequired = null;
1439
- this.notifyQueued = false;
1440
- this.rowsDirty = false;
1441
- this.snapshot = makeInitialQuerySnapshot();
1442
1622
  }
1443
1623
  subscribe(listener) {
1444
1624
  this.listeners.add(listener);
@@ -1637,4 +1817,4 @@ function _canonicalPredicateForTests(predicate) {
1637
1817
  return canonicalPredicateString(predicate);
1638
1818
  }
1639
1819
  //#endregion
1640
- export { OmgBadge, VibesAuthAutoPrompt, VibesAuthGuard, VibesAuthProvider, VibesAuthRequiredError, VibesFeedback, VibesLogin, VibesUpload, _canonicalPredicateForTests, _resetSharedCollectionSocketsForTests, attachGestureListeners, captureScreenshot, clearTrace, createNotification, createSandbox, createVibesAuth, deriveAppId, deriveTokenUrl, forkSandbox, getAuthContext, getTrace, installTrace, mailAppForEmail, motionPermissionState, notificationSupport, notifyAuthRequired, requestMotionPermission, runtimeClaims, subscribeAuthRequired, subscribeCollection, useAuth, useCollection, useFeedbackGesture, useNotificationPermission, useNotifications, useQuery, useUpload, useUser, useVibesAuth, useVibesToken, vibesFetch };
1820
+ export { OmgBadge, VibesAuthAutoPrompt, VibesAuthGuard, VibesAuthProvider, VibesAuthRequiredError, VibesLogin, VibesUpload, _canonicalPredicateForTests, _resetSharedCollectionSocketsForTests, createNotification, createSandbox, createVibesAuth, deriveAppId, deriveTokenUrl, forkSandbox, getAuthContext, mailAppForEmail, notificationSupport, notifyAuthRequired, runtimeClaims, subscribeAuthRequired, subscribeCollection, useAuth, useCollection, useNotificationPermission, useNotifications, useQuery, useUpload, useUser, useVibesAuth, useVibesToken, vibesFetch };
package/package.json CHANGED
@@ -1,16 +1,12 @@
1
1
  {
2
2
  "name": "@omg-dev/sdk",
3
- "version": "0.4.29",
3
+ "version": "0.4.31",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./src/index.ts",
8
8
  "default": "./dist/index.mjs"
9
9
  },
10
- "./feedback/auto": {
11
- "types": "./src/feedback/auto.tsx",
12
- "default": "./dist/feedback/auto.mjs"
13
- },
14
10
  "./brand/auto": {
15
11
  "types": "./src/brand/auto.tsx",
16
12
  "default": "./dist/brand/auto.mjs"
@@ -19,8 +15,7 @@
19
15
  "dependencies": {
20
16
  "better-auth": "^1.2.0",
21
17
  "@better-auth/passkey": "^1.2.0",
22
- "@microsoft/fetch-event-source": "^2.0.1",
23
- "modern-screenshot": "^4.0.0"
18
+ "@microsoft/fetch-event-source": "^2.0.1"
24
19
  },
25
20
  "peerDependencies": {
26
21
  "react": ">=18",