@vibes.diy/prompts 14.3.178 → 14.3.184

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/llms/backend.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export const backendConfig = {
2
2
  name: "backend",
3
3
  label: "backend-js",
4
- description: "server-side backend.js file: answer HTTP/webhook requests at the app's /_api URL, react to committed data changes, and run scheduled jobs — its writes go through the app's own access rules; reads a web page, feed or API the person names and hands it to the page through /_api; emails, notifies and reminds people through ctx.notify by handle — a weekly digest, a summary for the owner, a Monday email to the manager — and invites a recipient who is not in the app yet; and sends a record to a tool the person names (Slack, Google Sheets, a CRM, Zapier) through a Zapier Catch Hook the owner connects",
5
- cues: ["email", "notify", "remind", "digest", "zapier", "catch hook", "webhook", "slack", "sheets", "crm"],
4
+ description: "server-side backend.js file: answer HTTP/webhook requests at the app's /_api URL, react to committed data changes, and run scheduled jobs — its writes go through the app's own access rules; reads a web page, feed or API the person names and hands it to the page through /_api; emails, notifies and reminds people through ctx.notify by handle — a weekly digest, a summary for the owner, a Monday email to the manager — and invites a recipient who is not in the app yet; sends a record to a tool the person names (Slack, Google Sheets, a CRM, Zapier) through a Zapier Catch Hook the owner connects; and takes records in from Zapier — a Google Forms response, a new sheet row, an email — at its /_api URL, checked against a shared secret",
5
+ cues: ["email", "notify", "remind", "digest", "zapier", "catch hook", "webhook", "slack", "sheets", "crm", "google forms"],
6
6
  };
7
7
  //# sourceMappingURL=backend.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../jsr/llms/backend.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,YAAY;IACnB,WAAW,EACT,ylBAAylB;IAK3lB,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC;CAC3G,CAAC"}
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../jsr/llms/backend.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,YAAY;IACnB,WAAW,EACT,guBAAguB;IAKluB,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,CAAC;CAC3H,CAAC"}
package/llms/backend.md CHANGED
@@ -21,7 +21,9 @@ create or mention `backend.js`.
21
21
  ## When to use which handler — signal words
22
22
 
23
23
  - **`fetch`** — "webhook", "OAuth", "callback", "API endpoint", "receive",
24
- "form POST", "Stripe/GitHub/Sonos sends us…". Something **external → us**.
24
+ "form POST", "Stripe/GitHub/Sonos sends us…", "from Zapier", "when a form
25
+ is submitted in Google Forms", "every new row in my sheet". Something
26
+ **external → us**.
25
27
  - **`onChange`** — "when X is created", "after a write", "notify", "send an
26
28
  email", "aggregate", "moderate", "sync to…". An **internal write → a side
27
29
  effect**: `ctx.notify` for every person the app emails, notifies or reminds,
@@ -158,8 +160,12 @@ await ctx.notify({ toHandle: "alice", body: "Your turn", dedupeKey: "game-7-turn
158
160
  ctx.log("info", "tick finished", { written: 3 }); // diagnostics — read back with: vibes-diy app logs owner/slug
159
161
  const gh = await ctx.github.fetch("/repos/acme/site/issues", { method: "POST", body: { title: "Filed from the vibe" } }); // connected GitHub repo
160
162
  const routes = await ctx.github.routes(); // [{ repo, accountLogin, binderHandle, installationId }] — who connected which repo; no GitHub call
163
+ const session = await ctx.stripe.fetch("/v1/checkout/sessions", { method: "POST", body: form, mode: "test" }); // the owner's connected Stripe account
164
+ const stripe = await ctx.stripe.status(); // { test: { available, state }, live: { available, state } }
161
165
  ```
162
166
 
167
+ - An app that takes money loads the `stripe` skill (one-time charges) or the `stripe-subscriptions` skill (recurring): the Connect Stripe setup item, `ctx.stripe.fetch` with the order's `mode` on every call, hosted Checkout and a complete example.
168
+
163
169
  - An app that works with somebody's GitHub repo loads the `github` skill: the connect link, `config.github`, `ctx.github.routes()`, deliveries and a complete example.
164
170
  - `ctx.github.fetch(path, init)` calls the GitHub REST API **as the GitHub App
165
171
  installation the owner connected to this vibe**. It is the only GitHub surface
@@ -400,7 +406,9 @@ const routes = await ctx.github.routes(); // [{ repo, accountLogin, binderHandle
400
406
  anything else **throw** — a handler that returns normally has acknowledged
401
407
  the event, so the platform will not call it again for that write, while a
402
408
  thrown `onChange` is re-delivered with backoff (a handful of tries over a few
403
- minutes) and a run that never succeeds lands in the app's logs.
409
+ minutes) and a run that never succeeds lands in the app's logs. Records
410
+ travelling the other way, from a tool into the app, arrive at `fetch`:
411
+ see *Records in from Zapier* below.
404
412
 
405
413
  ```js
406
414
  export async function onChange(event, ctx) {
@@ -843,6 +851,162 @@ signed in.
843
851
  Writes a handler makes with `ctx.db.put` still pass through `access.js` as the
844
852
  caller, so the app's rules hold for API writes too.
845
853
 
854
+ ### Records in from Zapier — a Zap that POSTs to `/_api`
855
+
856
+ A Zap brings records into the app by POSTing to its `/_api` route.
857
+ Reach for it when the ask says the records come *from* another tool — "from
858
+ Zapier", "when a form is submitted in Google Forms", "every new row in my
859
+ sheet", "each new email in my inbox". The owner makes a Zap whose trigger is
860
+ that tool and whose action is **Webhooks by Zapier → POST** to
861
+ `https://vibes.diy/vibe/<owner>/<slug>/_api/zapier/lead`, sending the fields as
862
+ JSON and mapping the tool's own id for the record (the form response id, the
863
+ row id, the message id) to `id`. The route answers the world once the app is
864
+ published.
865
+
866
+ A Zap carries no vibes.diy sign-in, so the route runs as nobody, and the
867
+ recipe has three parts:
868
+
869
+ - **A shared secret proves the sender.** The owner picks a long random value,
870
+ saves it as a secret (`vibes-diy secrets set ZAPIER_INBOUND_SECRET`), and
871
+ pastes the same value into the Zap as an `x-zapier-secret` header. The
872
+ handler compares the two in constant time and answers `401` when they
873
+ differ. The secret rides a header of the app's own naming: `Authorization`
874
+ and `Cookie` are removed before the handler sees the request, and
875
+ `x-vibes-*` belongs to the platform.
876
+ - **The same record lands on the same document.** Zapier can deliver one
877
+ record twice, so its key comes from the record itself — the mapped `id`, or
878
+ a hash of the body — and the record the app shows is written at
879
+ `lead:<key>` with `ifRev: null`, so a second delivery meets a `conflict`
880
+ and changes nothing.
881
+ - **The route writes a sealed arrival, and the tick turns it into the
882
+ record.** The route's write is anonymous, so `access.js` admits it with
883
+ `allowAnonymous: true` — and a type an anonymous route may write, any
884
+ visitor may write directly too. So the route writes an `arrival` carrying a
885
+ seal (an HMAC of its fields under the secret) on a channel no reader is
886
+ granted, and `scheduled`, which reads every channel, makes the seal again,
887
+ writes each matching arrival as the record the app shows (a type only
888
+ `scheduled` writes), and clears the arrival. An arrival with no matching
889
+ seal is cleared without becoming a record. The tick pages through every
890
+ arrival on the `next` cursor, up to ten pages a tick, so a pile of unsealed
891
+ arrivals never stands in front of a real one, and it handles each arrival
892
+ on its own — fields checked as strings, any failure logged — so one odd
893
+ document is cleared and the rest go through. `access.js` keeps every
894
+ arrival at an `arrival:` id, so none can occupy a lead's id.
895
+
896
+ Gate both handlers on the secret, so the app works fully before any Zap
897
+ exists, and say in your reply what the owner sets up: the secret, the Zap's
898
+ POST address and header, and anonymous writes for the app (publishing from
899
+ the editor turns them on when `access.js` declares `allowAnonymous`; after a
900
+ CLI push, `vibes-diy app anon-write <owner>/<slug> on`).
901
+
902
+ ### `backend.js`
903
+ ```js
904
+ export const config = { scheduled: { interval: "1m" } };
905
+
906
+ const enc = new TextEncoder();
907
+
908
+ function json(data, status) {
909
+ return new Response(JSON.stringify(data), { status: status ?? 200, headers: { "content-type": "application/json" } });
910
+ }
911
+
912
+ function hex(buf) {
913
+ return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
914
+ }
915
+
916
+ // The seal: an HMAC of the record's fields under the shared secret.
917
+ async function sealOf(secret, arrival) {
918
+ const key = await crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
919
+ return hex(await crypto.subtle.sign("HMAC", key, enc.encode(JSON.stringify([arrival.key, arrival.name, arrival.email]))));
920
+ }
921
+
922
+ // Constant time: both sides become fixed-length digests, compared byte by byte with no early exit.
923
+ async function sameText(a, b) {
924
+ const [x, y] = await Promise.all([a, b].map(async (s) => new Uint8Array(await crypto.subtle.digest("SHA-256", enc.encode(s)))));
925
+ let diff = 0;
926
+ for (let i = 0; i < x.length; i++) diff |= x[i] ^ y[i];
927
+ return diff === 0;
928
+ }
929
+
930
+ export async function fetch(request, ctx) {
931
+ const url = new URL(request.url);
932
+ if (url.pathname !== "/zapier/lead" || request.method !== "POST") return json({ ok: false, reason: "not found" }, 404);
933
+ const secret = ctx.secrets.ZAPIER_INBOUND_SECRET;
934
+ if (!secret) return json({ ok: false, reason: "this app is still being set up" }, 503);
935
+ const given = request.headers.get("x-zapier-secret") ?? "";
936
+ if (!(await sameText(given, secret))) return json({ ok: false, reason: "unrecognized sender" }, 401);
937
+ const raw = await request.text();
938
+ let body;
939
+ try {
940
+ body = JSON.parse(raw);
941
+ } catch {
942
+ return json({ ok: false, reason: "send the fields as JSON" }, 400);
943
+ }
944
+ if (typeof body?.name !== "string" || typeof body?.email !== "string") {
945
+ return json({ ok: false, reason: "name and email are needed" }, 400);
946
+ }
947
+ // The key comes from the record itself, so a second delivery of it lands on the same lead.
948
+ const source = typeof body.id === "string" && body.id !== "" ? body.id : raw;
949
+ const key = hex(await crypto.subtle.digest("SHA-256", enc.encode(source)));
950
+ const arrival = { type: "arrival", key, name: body.name.slice(0, 200), email: body.email.slice(0, 200) };
951
+ arrival.seal = await sealOf(secret, arrival);
952
+ await ctx.db.put(arrival, { db: "leads", id: "arrival:" + arrival.seal });
953
+ return json({ ok: true, key });
954
+ }
955
+
956
+ export async function scheduled(event, ctx) {
957
+ const secret = ctx.secrets.ZAPIER_INBOUND_SECRET;
958
+ let added = 0;
959
+ let cleared = 0;
960
+ let after;
961
+ let pages = 0;
962
+ // Page through every arrival (up to 10 pages a tick), so a pile of unsealed ones never hides a real one.
963
+ do {
964
+ const page = secret ? await ctx.db.query({ db: "leads", field: "type", key: "arrival", limit: 200, after }) : [];
965
+ for (const arrival of page) {
966
+ // Each arrival is handled on its own: whatever one holds, the tick clears it and moves on.
967
+ try {
968
+ const wellFormed = ["key", "name", "email", "seal"].every((f) => typeof arrival[f] === "string");
969
+ if (wellFormed && (await sameText(arrival.seal, await sealOf(secret, arrival)))) {
970
+ const lead = { type: "lead", name: arrival.name, email: arrival.email, addedAt: event.scheduledTime };
971
+ await ctx.db.put(lead, { db: "leads", id: "lead:" + arrival.key, ifRev: null });
972
+ added += 1;
973
+ } else {
974
+ cleared += 1;
975
+ }
976
+ } catch (err) {
977
+ // A conflict means this lead is already here; anything else is logged and the arrival cleared.
978
+ if (err?.code !== "conflict") ctx.log("warn", "arrival set aside", { id: arrival._id, reason: String(err?.message ?? err) });
979
+ }
980
+ await ctx.db.delete(arrival._id, { db: "leads" }).catch((err) => ctx.log("warn", "arrival kept", { id: arrival._id, reason: String(err?.message ?? err) }));
981
+ }
982
+ after = page.next;
983
+ pages += 1;
984
+ } while (after && pages < 10);
985
+ ctx.log("info", "zapier arrivals", { added, cleared });
986
+ }
987
+ ```
988
+
989
+ ### `access.js`
990
+ ```js
991
+ export function leads(doc, oldDoc, user, ctx) {
992
+ if (oldDoc && oldDoc.type !== doc.type) throw { forbidden: "a record keeps its type" }; // a delete arrives as the stored record, type and all
993
+ if (doc.type === "arrival") {
994
+ // An arrival lives at `arrival:<seal>`, so no arrival can sit at a lead's id.
995
+ if (typeof doc._id !== "string" || !doc._id.startsWith("arrival:")) throw { forbidden: "an arrival lives at arrival:<seal>" };
996
+ if (doc._deleted) ctx.requireBackend("scheduled"); // the tick clears arrivals
997
+ return { channels: ["arrivals"], allowAnonymous: true }; // no reader is granted this channel
998
+ }
999
+ if (doc.type === "lead") {
1000
+ ctx.requireBackend("scheduled"); // a lead is written only after its seal matched
1001
+ return { channels: ["leads"], grant: { users: { [user.userHandle]: ["leads"] } } };
1002
+ }
1003
+ throw { forbidden: "unknown type" };
1004
+ }
1005
+ ```
1006
+
1007
+ The page lists `lead` records with `useLiveQuery("type", { key: "lead" })`,
1008
+ and the owner sees each one within a minute of the Zap firing.
1009
+
846
1010
  ### Bulk transformations run as a route
847
1011
 
848
1012
  A change that touches many stored records is written once, as a named `/_api` route, and run
@@ -1,20 +1,25 @@
1
1
  export const connectionsConfig = {
2
2
  name: "connections",
3
3
  label: "Connections",
4
- description: "Connect a visitor's own GitHub profile, Threads or Instagram account with one click and read it from backend.js — config.connections, useConnection, @vibes.diy/social",
4
+ description: "Connect a visitor's own GitHub profile, Threads, Instagram or LinkedIn account with one click, read it from backend.js, and post to their Threads, Instagram or LinkedIn — config.connections, useConnection, @vibes.diy/social",
5
5
  cues: [
6
6
  "connect my",
7
7
  "sign in with github",
8
8
  "sign in with instagram",
9
9
  "sign in with threads",
10
+ "sign in with linkedin",
10
11
  "my instagram",
11
12
  "my threads",
12
13
  "my github",
14
+ "my linkedin",
15
+ "post to linkedin",
16
+ "share on linkedin",
13
17
  "my posts",
14
18
  "my followers",
15
19
  "social media login",
16
20
  "instagram",
17
21
  "threads",
22
+ "linkedin",
18
23
  "github account",
19
24
  ],
20
25
  importModule: "use-vibes",
@@ -1 +1 @@
1
- {"version":3,"file":"connections.js","sourceRoot":"","sources":["../../jsr/llms/connections.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iBAAiB,GAAc;IAC1C,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,WAAW,EACT,wKAAwK;IAC1K,IAAI,EAAE;QACJ,YAAY;QACZ,qBAAqB;QACrB,wBAAwB;QACxB,sBAAsB;QACtB,cAAc;QACd,YAAY;QACZ,WAAW;QACX,UAAU;QACV,cAAc;QACd,oBAAoB;QACpB,WAAW;QACX,SAAS;QACT,gBAAgB;KACjB;IACD,YAAY,EAAE,WAAW;IACzB,UAAU,EAAE,eAAe;CAC5B,CAAC"}
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../../jsr/llms/connections.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iBAAiB,GAAc;IAC1C,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,WAAW,EACT,iOAAiO;IACnO,IAAI,EAAE;QACJ,YAAY;QACZ,qBAAqB;QACrB,wBAAwB;QACxB,sBAAsB;QACtB,uBAAuB;QACvB,cAAc;QACd,YAAY;QACZ,WAAW;QACX,aAAa;QACb,kBAAkB;QAClB,mBAAmB;QACnB,UAAU;QACV,cAAc;QACd,oBAAoB;QACpB,WAAW;QACX,SAAS;QACT,UAAU;QACV,gBAAgB;KACjB;IACD,YAAY,EAAE,WAAW;IACzB,UAAU,EAAE,eAAe;CAC5B,CAAC"}
@@ -1,7 +1,7 @@
1
1
  # Connections — a visitor's own account, spent by your app
2
2
 
3
3
  A **connection** lets a visitor link their own third-party account (GitHub,
4
- Threads, Instagram) to your app in one click, and lets your `backend.js` read
4
+ Threads, Instagram, LinkedIn) to your app in one click, and lets your `backend.js` read
5
5
  from that account — and, when they approve it, post to it — as them. The platform shows the consent screen, runs the
6
6
  sign-in, holds the credential, refreshes it, and warns the person before it
7
7
  expires. Your app contains **no** client id, secret, redirect route, token
@@ -17,7 +17,7 @@ to replace.
17
17
 
18
18
  ## When to use it — signal words
19
19
 
20
- "connect my Instagram", "sign in with GitHub", "my Threads posts", "show my
20
+ "connect my Instagram", "sign in with GitHub", "my Threads posts", "post this to my LinkedIn", "show my
21
21
  profile from …", "which of my videos got the most likes", "my followers on …".
22
22
  A request that names one of the providers below and wants **their** data is a
23
23
  connection. A request for public data nobody has to log in for (a public feed,
@@ -62,6 +62,8 @@ GitHub profile next to your entries."), never as a note to yourself.
62
62
  | `threads` | `threads_content_publish` | **post to their Threads feed** (`threads(ctx).publish()`) |
63
63
  | `instagram` | `instagram_business_basic` | read the account's profile and its own media with like/comment counts |
64
64
  | `instagram` | `instagram_business_content_publish` | **post a photo or video to their Instagram** (`instagram(ctx).publish()`) |
65
+ | `linkedin` | `profile` | read the member's name and profile photo (`linkedin(ctx).me()`) |
66
+ | `linkedin` | `w_member_social` | **post to their LinkedIn feed** (`linkedin(ctx).post()`); declare `profile` beside it |
65
67
 
66
68
  **A scope that posts is a separate scope, and asking for it is the whole
67
69
  authorization.** Declare it only when the app really posts, and say so in `why`
@@ -158,8 +160,18 @@ provider call as a typed request with a typed response, so there is no
158
160
  - `instagram(ctx).publish({ imageUrl, caption? })` → `{ id }`. Needs
159
161
  `instagram_business_content_publish`. Instagram has no text-only post, so
160
162
  `imageUrl` is required.
161
-
162
- **Video posting is not available on either provider yet** — the provider hands
163
+ - `linkedin(ctx).me()` → `{ id, name, givenName, familyName, picture, personUrn }`.
164
+ Needs `profile`.
165
+ - `linkedin(ctx).post({ text, visibility?, link? })` → `{ id }`. Needs
166
+ `w_member_social` and `profile` (a post is authored as the member, and
167
+ `profile` is where their id comes from). `text` is plain prose: the library
168
+ escapes LinkedIn's formatting characters and keeps `#hashtags` live.
169
+ `visibility` is `"PUBLIC"` (default) or `"CONNECTIONS"`. `link` is
170
+ `{ url, title?, description? }` and becomes the post's card exactly as given,
171
+ since LinkedIn shows what the request carries. LinkedIn posts carry text and
172
+ a link card.
173
+
174
+ **Video posting is not available on any provider yet** — the provider hands
163
175
  back a container while the video is still processing, and the platform cannot
164
176
  yet read that container's status, so a video post would intermittently fail
165
177
  rather than land. Build on text and images; do not work around it with a raw
@@ -1,7 +1,7 @@
1
1
  export const stripeSubscriptionsConfig = {
2
2
  name: "stripe-subscriptions",
3
3
  label: "Stripe subscriptions",
4
- description: "A paid membership or subscription through Stripe Checkout in subscription mode, ONLY when the person asks for recurring money — monthly, yearly, subscribe, membership fee, paid tier, paywall — on the owner's own STRIPE_SECRET_KEY: the setup steps the app shows when the key or the Customer Portal is missing, the key kept in backend.js with no Stripe.js in the page, one pending membership then one Checkout Session opened in a new tab, entitlement computed from Stripe's status plus one GRACE_DAYS knob for a past_due card, members-only content as a channel the membership record grants and the backend withdraws, cancellation and card changes in Stripe's Customer Portal, and a scheduled sweep that re-reads every live subscription. A one-time charge (dues once, a ticket, a deposit) is the stripe skill; an app with members-only content that charges nothing does not need this — offer it as the [pay] option instead",
4
+ description: "A paid membership or subscription through Stripe Checkout in subscription mode, ONLY when the person asks for recurring money — monthly, yearly, subscribe, membership fee, paid tier, paywall — into the owner's own Stripe account through ctx.stripe, connected once with Connect Stripe: the Connect Stripe and Customer Portal setup items, test mode first with the membership's mode passed on every call and live once the owner takes this app live, every Stripe call in backend.js with no Stripe.js in the page, one pending membership then one Checkout Session opened in a new tab, entitlement computed from Stripe's status plus one GRACE_DAYS knob for a past_due card, members-only content as a channel the membership record grants and the backend withdraws, cancellation and card changes in Stripe's Customer Portal, and a scheduled sweep that re-reads every live subscription. A one-time charge (dues once, a ticket, a deposit) is the stripe skill; an app with members-only content that charges nothing does not need this — offer it as the [pay] option instead",
5
5
  cues: [
6
6
  "subscription",
7
7
  "subscribe",
@@ -1 +1 @@
1
- {"version":3,"file":"stripe-subscriptions.js","sourceRoot":"","sources":["../../jsr/llms/stripe-subscriptions.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,yBAAyB,GAAc;IAClD,IAAI,EAAE,sBAAsB;IAC5B,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EACT,w5BAAw5B;IAC15B,IAAI,EAAE;QACJ,cAAc;QACd,WAAW;QACX,SAAS;QACT,WAAW;QACX,SAAS;QACT,QAAQ;QACR,gBAAgB;QAChB,iBAAiB;QACjB,WAAW;QACX,SAAS;QACT,WAAW;QACX,cAAc;QACd,iBAAiB;KAClB;CACF,CAAC"}
1
+ {"version":3,"file":"stripe-subscriptions.js","sourceRoot":"","sources":["../../jsr/llms/stripe-subscriptions.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,yBAAyB,GAAc;IAClD,IAAI,EAAE,sBAAsB;IAC5B,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EACT,siCAAsiC;IACxiC,IAAI,EAAE;QACJ,cAAc;QACd,WAAW;QACX,SAAS;QACT,WAAW;QACX,SAAS;QACT,QAAQ;QACR,gBAAgB;QAChB,iBAAiB;QACjB,WAAW;QACX,SAAS;QACT,WAAW;QACX,cAAc;QACd,iBAAiB;KAClB;CACF,CAAC"}