@omen.dog/sdk 1.0.0 → 1.1.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/dist/index.mjs CHANGED
@@ -1,3 +1,13 @@
1
+ import {
2
+ ASK_GROWN_UP_STATES,
3
+ CHILD_LOGIN_STATES,
4
+ ChildLogin,
5
+ ENTRY_STATES,
6
+ deriveInitialState,
7
+ displayGroup,
8
+ reduce
9
+ } from "./chunk-7L3ANE2V.mjs";
10
+
1
11
  // src/errors.ts
2
12
  var OmenError = class extends Error {
3
13
  /** HTTP status code from the API. */
@@ -254,6 +264,27 @@ var ItemsNamespace = class {
254
264
  body: { reason }
255
265
  });
256
266
  }
267
+ /**
268
+ * Award a catalog item (open-shop cosmetic or avatar-editor trait) to a user,
269
+ * paid for from your Sparks pool at the item's list price. Custom items use
270
+ * `issue()` and are free; catalog items draw the pool down. Re-awarding an item
271
+ * the user already owns is a no-op (no charge).
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * await omen.items.awardCatalog({
276
+ * userId, catalogType: 'store', catalogItemId: 'theme_lava',
277
+ * });
278
+ * ```
279
+ */
280
+ async awardCatalog(options) {
281
+ const { idempotencyKey, ...body } = options;
282
+ return this.http.request(`/api/v1/apps/${this.appId}/items/award-catalog`, {
283
+ method: "POST",
284
+ body,
285
+ headers: idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0
286
+ });
287
+ }
257
288
  };
258
289
 
259
290
  // src/namespaces/collections.ts
@@ -476,6 +507,316 @@ function timingSafeEqual(a, b) {
476
507
  return result === 0;
477
508
  }
478
509
 
510
+ // src/namespaces/products.ts
511
+ var ProductsNamespace = class {
512
+ constructor(http, appId) {
513
+ this.http = http;
514
+ this.appId = appId;
515
+ }
516
+ /**
517
+ * Create a new product.
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * const product = await omen.products.create({
522
+ * name: 'Premium Sword',
523
+ * type: 'one_time',
524
+ * priceCents: 499,
525
+ * });
526
+ * ```
527
+ */
528
+ async create(options) {
529
+ return this.http.request(`/api/v1/apps/${this.appId}/products`, {
530
+ method: "POST",
531
+ body: options
532
+ });
533
+ }
534
+ /**
535
+ * List all products for this app.
536
+ *
537
+ * @param activeOnly - If true, only return active products. Defaults to false.
538
+ */
539
+ async list(activeOnly) {
540
+ return this.http.request(`/api/v1/apps/${this.appId}/products`, {
541
+ query: activeOnly ? { active: "true" } : void 0
542
+ });
543
+ }
544
+ /**
545
+ * Update an existing product.
546
+ *
547
+ * @param productId - The product ID to update.
548
+ * @param options - Fields to update.
549
+ */
550
+ async update(productId, options) {
551
+ return this.http.request(`/api/v1/apps/${this.appId}/products`, {
552
+ method: "PATCH",
553
+ query: { productId },
554
+ body: options
555
+ });
556
+ }
557
+ /**
558
+ * Deactivate a product. Deactivated products cannot be purchased but existing
559
+ * purchases remain valid.
560
+ *
561
+ * @param productId - The product ID to deactivate.
562
+ */
563
+ async deactivate(productId) {
564
+ await this.http.request(`/api/v1/apps/${this.appId}/products`, {
565
+ method: "DELETE",
566
+ query: { productId }
567
+ });
568
+ }
569
+ };
570
+
571
+ // src/namespaces/multiplayer.ts
572
+ var MultiplayerNamespace = class {
573
+ constructor(http, appId) {
574
+ this.http = http;
575
+ this.appId = appId;
576
+ }
577
+ /**
578
+ * Get the current multiplayer configuration for this app.
579
+ */
580
+ async getConfig() {
581
+ const res = await this.http.request(
582
+ `/api/v1/apps/${this.appId}/multiplayer-config`
583
+ );
584
+ return res.multiplayerConfig;
585
+ }
586
+ /**
587
+ * Enable or update managed multiplayer configuration.
588
+ *
589
+ * @example
590
+ * ```ts
591
+ * await omen.multiplayer.updateConfig({
592
+ * managed: true,
593
+ * maxPlayers: 8,
594
+ * voiceEnabled: true,
595
+ * teamMode: 'manual',
596
+ * });
597
+ * ```
598
+ */
599
+ async updateConfig(options) {
600
+ const res = await this.http.request(
601
+ `/api/v1/apps/${this.appId}/multiplayer-config`,
602
+ { method: "PUT", body: options }
603
+ );
604
+ return res.multiplayerConfig;
605
+ }
606
+ /**
607
+ * Deploy authoritative room logic (Pro tier required).
608
+ * The bundle is a JavaScript file that exports room lifecycle hooks.
609
+ *
610
+ * @param bundle - The JavaScript source code as a string.
611
+ */
612
+ async deployLogic(bundle) {
613
+ return this.http.request(`/api/v1/apps/${this.appId}/room-logic`, {
614
+ method: "PUT",
615
+ body: { bundle }
616
+ });
617
+ }
618
+ /**
619
+ * List deployed room logic versions.
620
+ */
621
+ async listLogicVersions() {
622
+ const res = await this.http.request(
623
+ `/api/v1/apps/${this.appId}/room-logic/versions`
624
+ );
625
+ return res.versions;
626
+ }
627
+ /**
628
+ * Roll back to a previous room logic version.
629
+ *
630
+ * @param version - The version number to roll back to.
631
+ */
632
+ async rollbackLogic(version) {
633
+ return this.http.request(`/api/v1/apps/${this.appId}/room-logic/rollback`, {
634
+ method: "POST",
635
+ body: { version }
636
+ });
637
+ }
638
+ };
639
+
640
+ // src/namespaces/notifications.ts
641
+ var NotificationsNamespace = class {
642
+ constructor(http, appId) {
643
+ this.http = http;
644
+ this.appId = appId;
645
+ }
646
+ /**
647
+ * Send a notification to a user.
648
+ *
649
+ * @example
650
+ * ```ts
651
+ * await omen.notifications.send({
652
+ * userId: 'cmm0tzco8...',
653
+ * title: 'New high score!',
654
+ * body: 'Someone beat your record on Pixel Duel.',
655
+ * category: 'app',
656
+ * actionUrl: '/creations/pixel-duel',
657
+ * });
658
+ * ```
659
+ */
660
+ async send(options) {
661
+ return this.http.request(`/api/v1/apps/${this.appId}/notifications/send`, {
662
+ method: "POST",
663
+ body: options
664
+ });
665
+ }
666
+ };
667
+
668
+ // src/namespaces/sparks.ts
669
+ import { createHmac } from "crypto";
670
+ var SparksNamespace = class {
671
+ constructor(http, appId) {
672
+ this.http = http;
673
+ this.appId = appId;
674
+ }
675
+ /**
676
+ * Award Sparks to a single user. Pass `idempotencyKey` to make retries safe.
677
+ *
678
+ * @example
679
+ * ```ts
680
+ * await omen.sparks.award({
681
+ * userId: 'cmm0tzco8...',
682
+ * amount: 250,
683
+ * reason: 'Beat level 10',
684
+ * idempotencyKey: `level10:${userId}`,
685
+ * });
686
+ * ```
687
+ */
688
+ async award(options) {
689
+ const { idempotencyKey, ...body } = options;
690
+ return this.http.request(`/api/v1/apps/${this.appId}/sparks/award`, {
691
+ method: "POST",
692
+ body,
693
+ headers: idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0
694
+ });
695
+ }
696
+ /**
697
+ * Award Sparks to many users in one request (max 100). Best-effort per item:
698
+ * a single bad/underfunded entry does not fail the rest. Inspect `results`.
699
+ */
700
+ async awardBatch(awards) {
701
+ return this.http.request(`/api/v1/apps/${this.appId}/sparks/award-batch`, {
702
+ method: "POST",
703
+ body: { awards }
704
+ });
705
+ }
706
+ /** Current pool status: balance, lifetime stats, and per-app budgets. */
707
+ async pool() {
708
+ return this.http.request(`/api/v1/apps/${this.appId}/sparks/pool`);
709
+ }
710
+ /**
711
+ * Mint a short-lived display token for the UI kit, scoped to one user. Sign
712
+ * with your app's OAuth client SECRET (from the Developer Portal) — never ship
713
+ * the secret to the browser; call this on your backend and pass the token to
714
+ * `<omen-sparks-balance token="...">` / `<omen-inventory token="...">`.
715
+ *
716
+ * Synchronous (no network). Default TTL 10 min, max 1 hour.
717
+ *
718
+ * @example
719
+ * ```ts
720
+ * const token = omen.sparks.displayToken({ userId, secret: process.env.OMEN_CLIENT_SECRET! });
721
+ * ```
722
+ */
723
+ displayToken(options) {
724
+ if (!options.userId || !options.secret) throw new Error("userId and secret are required");
725
+ const ttl = Math.min(Math.max(1, Math.floor(options.ttlSeconds ?? 600)), 3600);
726
+ const exp = Math.floor(Date.now() / 1e3) + ttl;
727
+ const payloadB64 = Buffer.from(JSON.stringify({ a: this.appId, u: options.userId, e: exp })).toString("base64url");
728
+ const sig = createHmac("sha256", options.secret).update(payloadB64).digest("base64url");
729
+ return `${payloadB64}.${sig}`;
730
+ }
731
+ };
732
+
733
+ // src/namespaces/emails.ts
734
+ var EmailsNamespace = class {
735
+ constructor(http, appId) {
736
+ this.http = http;
737
+ this.appId = appId;
738
+ }
739
+ /**
740
+ * Send one email (up to 10 recipients). No attachments; 256 KB body max.
741
+ *
742
+ * @example
743
+ * ```ts
744
+ * await omen.emails.send({
745
+ * from: 'hello@yourdomain.com',
746
+ * fromName: 'My App',
747
+ * to: player.email,
748
+ * subject: 'Your weekly recap',
749
+ * html: '<h1>Nice run!</h1>',
750
+ * });
751
+ * ```
752
+ */
753
+ async send(options) {
754
+ return this.http.request(`/api/v1/apps/${this.appId}/emails/send`, {
755
+ method: "POST",
756
+ body: options
757
+ });
758
+ }
759
+ /** Recent sends for this app plus the rolling 24h quota. */
760
+ async log(limit = 50) {
761
+ return this.http.request(`/api/v1/apps/${this.appId}/emails/log?limit=${limit}`);
762
+ }
763
+ };
764
+
765
+ // src/namespaces/avatar.ts
766
+ import { createHmac as createHmac2 } from "crypto";
767
+ var AvatarNamespace = class {
768
+ constructor(appId, baseUrl) {
769
+ this.appId = appId;
770
+ this.baseUrl = baseUrl;
771
+ }
772
+ /**
773
+ * Mint a short-lived WRITE-scoped editor token for one user (backend only —
774
+ * never expose your client secret to the browser). Pass it to
775
+ * `<omen-avatar-editor token="...">`.
776
+ *
777
+ * @example
778
+ * ```ts
779
+ * const token = omen.avatar.editorToken({ userId, secret: process.env.OMEN_CLIENT_SECRET! });
780
+ * ```
781
+ */
782
+ editorToken(options) {
783
+ if (!options.userId || !options.secret) throw new Error("userId and secret are required");
784
+ const ttl = Math.min(Math.max(1, Math.floor(options.ttlSeconds ?? 600)), 3600);
785
+ const exp = Math.floor(Date.now() / 1e3) + ttl;
786
+ const payloadB64 = Buffer.from(
787
+ JSON.stringify({ a: this.appId, u: options.userId, e: exp, t: "editor" })
788
+ ).toString("base64url");
789
+ const sig = createHmac2("sha256", options.secret).update(payloadB64).digest("base64url");
790
+ return `${payloadB64}.${sig}`;
791
+ }
792
+ /**
793
+ * Public render URL for a user's shared avatar (SVG; append `&format=png`
794
+ * where supported). Pass `version` (from the `avatar:saved` event or the
795
+ * `user.avatar_updated` webhook) to bust caches after an edit.
796
+ */
797
+ renderUrl(userId, options) {
798
+ const v = options?.version ? `&v=${encodeURIComponent(options.version)}` : "";
799
+ return `${this.baseUrl}/api/v1/avatar/render?userId=${encodeURIComponent(userId)}${v}`;
800
+ }
801
+ /**
802
+ * Enumerate the shared avatar catalog — every trait id, name, rarity and
803
+ * Sparks list price. Public, no auth. Trait ids (`category/file.svg` paths)
804
+ * are also public SVGs at `{baseUrl}/avatar/{id}` for previews, and feed
805
+ * `omen.items.awardCatalog({ catalogType: 'avatar_trait', catalogItemId })`
806
+ * for pool-funded gifts.
807
+ *
808
+ * Pass a user's editor token and each trait's `locked` flag reflects that
809
+ * user's ownership instead of the anonymous default.
810
+ */
811
+ async catalog(options) {
812
+ const res = await fetch(`${this.baseUrl}/api/v1/avatar/catalog`, {
813
+ headers: options?.editorToken ? { Authorization: `Bearer ${options.editorToken}` } : void 0
814
+ });
815
+ if (!res.ok) throw new Error(`Failed to load avatar catalog (HTTP ${res.status})`);
816
+ return await res.json();
817
+ }
818
+ };
819
+
479
820
  // src/index.ts
480
821
  var OmenClient = class {
481
822
  /** User profiles and friend lists. */
@@ -488,6 +829,18 @@ var OmenClient = class {
488
829
  collections;
489
830
  /** Webhook endpoint management and signature verification. */
490
831
  webhooks;
832
+ /** In-app products (one-time purchases and subscriptions). */
833
+ products;
834
+ /** Managed multiplayer configuration and room logic deployment. */
835
+ multiplayer;
836
+ /** Send notifications to app users. */
837
+ notifications;
838
+ /** Award Sparks from your prepaid pool + mint UI-kit display tokens. */
839
+ sparks;
840
+ /** Send transactional email from your verified domains (Partner Apps). */
841
+ emails;
842
+ /** Shared Omen avatar in your app: editor tokens + render URLs (F262). */
843
+ avatar;
491
844
  constructor(options) {
492
845
  if (!options.token) throw new Error("OmenClient: token is required");
493
846
  if (!options.appId) throw new Error("OmenClient: appId is required");
@@ -498,13 +851,26 @@ var OmenClient = class {
498
851
  this.items = new ItemsNamespace(http, options.appId);
499
852
  this.collections = new CollectionsNamespace(http, options.appId);
500
853
  this.webhooks = new WebhooksNamespace(http, options.appId);
854
+ this.products = new ProductsNamespace(http, options.appId);
855
+ this.multiplayer = new MultiplayerNamespace(http, options.appId);
856
+ this.notifications = new NotificationsNamespace(http, options.appId);
857
+ this.sparks = new SparksNamespace(http, options.appId);
858
+ this.emails = new EmailsNamespace(http, options.appId);
859
+ this.avatar = new AvatarNamespace(options.appId, baseUrl);
501
860
  }
502
861
  };
503
862
  export {
863
+ ASK_GROWN_UP_STATES,
864
+ CHILD_LOGIN_STATES,
865
+ ChildLogin,
866
+ ENTRY_STATES,
504
867
  OmenAuthError,
505
868
  OmenClient,
506
869
  OmenError,
507
870
  OmenNotFoundError,
508
871
  OmenRateLimitError,
509
- OmenValidationError
872
+ OmenValidationError,
873
+ deriveInitialState,
874
+ displayGroup,
875
+ reduce
510
876
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omen.dog/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Official server-side TypeScript SDK for the Omen platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -12,6 +12,11 @@
12
12
  "import": "./dist/index.mjs",
13
13
  "require": "./dist/index.js"
14
14
  },
15
+ "./child-login": {
16
+ "types": "./dist/child-login.d.ts",
17
+ "import": "./dist/child-login.mjs",
18
+ "require": "./dist/child-login.js"
19
+ },
15
20
  "./creation": {
16
21
  "types": "./dist/creation.d.ts"
17
22
  }
@@ -23,7 +28,7 @@
23
28
  "node": ">=18.0.0"
24
29
  },
25
30
  "scripts": {
26
- "build": "tsup src/index.ts --format cjs,esm --dts --clean && cp src/creation.d.ts dist/creation.d.ts",
31
+ "build": "tsup src/index.ts src/child-login.ts --format cjs,esm --dts --clean && cp src/creation.d.ts dist/creation.d.ts",
27
32
  "prepublishOnly": "npm run build"
28
33
  },
29
34
  "devDependencies": {