@voltro/plugin-licensing 0.86.0 → 0.88.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.
@@ -0,0 +1,31 @@
1
+ import { Effect } from 'effect';
2
+ import { VoidIfEmpty } from 'effect/Types';
3
+ import { YieldableError } from 'effect/Cause';
4
+
5
+ /** Authenticated JSON retrieval from the existing Cloud /licenses route.
6
+ * HTTPS, or literal local loopback HTTP for private development/fixtures.
7
+ * Redirects refuse before credential forwarding. No issuer or verification key. */
8
+ export declare const cloudLicenseSnapshot: (options: CloudLicenseSnapshotOptions) => LicenseSnapshotSource;
9
+
10
+ export declare interface CloudLicenseSnapshotOptions {
11
+ readonly url: string;
12
+ /** Read the actual license:read credential at refresh time. Never a signing key. */
13
+ readonly bearerToken: () => Effect.Effect<string, LicenseSnapshotFetchError>;
14
+ readonly timeoutMs?: number;
15
+ }
16
+
17
+ /** Redacted transport refusal. No response body, URL, token or credential cause. */
18
+ export declare class LicenseSnapshotFetchError extends LicenseSnapshotFetchError_base<{
19
+ readonly reason: 'credentials' | 'transport' | 'status' | 'response';
20
+ readonly status?: number;
21
+ }> {
22
+ }
23
+
24
+ declare const LicenseSnapshotFetchError_base: new <A extends Record<string, any> = {}>(args: VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
25
+ readonly _tag: "LicenseSnapshotFetchError";
26
+ } & Readonly<A>;
27
+
28
+ /** Swappable server transport; verification and installation remain with Licensing. */
29
+ export declare type LicenseSnapshotSource = () => Effect.Effect<string, LicenseSnapshotFetchError>;
30
+
31
+ export { }
package/dist/cloud.js ADDED
@@ -0,0 +1,60 @@
1
+ import { Data as e, Effect as t } from "effect";
2
+ // src/cloud.ts
3
+ var n = class extends e.TaggedError("LicenseSnapshotFetchError") {}, r = 131072, i = 65536, a = (e) => {
4
+ let a = new URL(e.url), o = a.hostname === "127.0.0.1" || a.hostname === "[::1]";
5
+ if (a.username !== "" || a.password !== "" || a.hash !== "" || a.protocol !== "https:" && !(a.protocol === "http:" && o)) throw Error("Cloud license retrieval requires HTTPS or literal local loopback HTTP, without URL credentials or fragments");
6
+ let s = a.href, c = e.bearerToken, l = e.timeoutMs ?? 3e4;
7
+ if (typeof c != "function" || !Number.isSafeInteger(l) || l <= 0 || l > 3e5) throw Error("Cloud license retrieval requires an actual credential port and bounded positive timeout");
8
+ return () => t.gen(function* () {
9
+ let e = yield* c();
10
+ return typeof e != "string" || e.length === 0 || e.length > i || !/^[\x21-\x7e]+$/.test(e) ? yield* t.fail(new n({ reason: "credentials" })) : yield* t.tryPromise({
11
+ try: async (t) => {
12
+ let a = new AbortController(), o = () => {
13
+ a.abort();
14
+ };
15
+ t.addEventListener("abort", o, { once: !0 }), t.aborted && a.abort();
16
+ let c = setTimeout(o, l);
17
+ try {
18
+ let t = await fetch(s, {
19
+ headers: { authorization: `Bearer ${e}` },
20
+ redirect: "error",
21
+ signal: a.signal
22
+ });
23
+ if (!t.ok) throw await t.body?.cancel(), new n({
24
+ reason: "status",
25
+ status: t.status
26
+ });
27
+ if (!/^application\/json(?:\s*;|\s*$)/i.test(t.headers.get("content-type") ?? "") || t.body === null) throw await t.body?.cancel(), new n({ reason: "response" });
28
+ let o = t.headers.get("content-length");
29
+ if (o !== null && (!/^\d+$/.test(o) || Number(o) > r)) throw await t.body.cancel(), new n({ reason: "response" });
30
+ let c = t.body.getReader(), l = [], u = 0;
31
+ try {
32
+ for (;;) {
33
+ let e = await c.read();
34
+ if (e.done) break;
35
+ if (u += e.value.byteLength, u > r) throw await c.cancel(), new n({ reason: "response" });
36
+ l.push(e.value);
37
+ }
38
+ } finally {
39
+ c.releaseLock();
40
+ }
41
+ let d = new Uint8Array(u), f = 0;
42
+ for (let e of l) d.set(e, f), f += e.byteLength;
43
+ let p;
44
+ try {
45
+ p = JSON.parse(new TextDecoder("utf-8", { fatal: !0 }).decode(d));
46
+ } catch {
47
+ throw new n({ reason: "response" });
48
+ }
49
+ let m = typeof p == "object" && p && !Array.isArray(p) && "token" in p ? p.token : void 0;
50
+ if (typeof m != "string" || m.length > i || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(m)) throw new n({ reason: "response" });
51
+ return m;
52
+ } finally {
53
+ clearTimeout(c), t.removeEventListener("abort", o);
54
+ }
55
+ },
56
+ catch: (e) => e instanceof n ? e : new n({ reason: "transport" })
57
+ });
58
+ });
59
+ };
60
+ export { n as LicenseSnapshotFetchError, a as cloudLicenseSnapshot };
@@ -0,0 +1,39 @@
1
+ import { Schema } from 'effect';
2
+
3
+ /** A numeric cap, or `'unlimited'` for no cap. */
4
+ declare type EntitlementLimit = number | 'unlimited';
5
+
6
+ /** License names, not package installation or accepted capability claims. */
7
+ export declare const GAMES_PLUGIN_GRANTS: readonly ["rooms", "matchmaking", "leaderboards", "social", "chat", "guilds", "inventory", "economy", "progression", "liveops", "players", "saves", "sanctions", "gm"];
8
+
9
+ export declare const GamesLicenseEdition: Schema.Literals<readonly ["games", "games-enterprise"]>;
10
+
11
+ export declare type GamesLicenseEdition = typeof GamesLicenseEdition.Type;
12
+
13
+ export declare interface GamesSignLicenseInput extends Omit<SignLicenseInput, 'edition'> {
14
+ readonly edition: GamesLicenseEdition;
15
+ }
16
+
17
+ declare interface SignLicenseInput {
18
+ /** Scope the license to one tenant id; omit for a deployment-wide license. */
19
+ readonly tenant?: string;
20
+ /** Edition label (e.g. `'pro'`, `'enterprise'`). */
21
+ readonly edition: string;
22
+ /** Per-key numeric limits (`'unlimited'` for no cap). */
23
+ readonly entitlements: Readonly<Record<string, EntitlementLimit>>;
24
+ /** Boolean feature flags to grant. */
25
+ readonly features?: ReadonlyArray<string>;
26
+ /** Plugins licensed for production use (names without the
27
+ * `@voltro/plugin-` prefix, e.g. `['storage']`). */
28
+ readonly plugins?: ReadonlyArray<string>;
29
+ /** Seconds from `now` until the license expires. */
30
+ readonly ttlSeconds: number;
31
+ /** Key id stamped into the JWS header (public-key rotation). */
32
+ readonly kid?: string;
33
+ }
34
+
35
+ /** Issuer-side composition. Retains actual caller-owned base grants, limits,
36
+ * seats and TTL. It signs nothing and invents no price, key or entitlement. */
37
+ export declare const withGamesPluginGrants: (input: GamesSignLicenseInput) => SignLicenseInput;
38
+
39
+ export { }
package/dist/games.js ADDED
@@ -0,0 +1,64 @@
1
+ import { Schema as e } from "effect";
2
+ // src/games.ts
3
+ var t = e.Literals(["games", "games-enterprise"]), n = Object.freeze([
4
+ "rooms",
5
+ "matchmaking",
6
+ "leaderboards",
7
+ "social",
8
+ "chat",
9
+ "guilds",
10
+ "inventory",
11
+ "economy",
12
+ "progression",
13
+ "liveops",
14
+ "players",
15
+ "saves",
16
+ "sanctions",
17
+ "gm"
18
+ ]), r = (r) => {
19
+ if (!r || Object.getPrototypeOf(r) !== Object.prototype || Object.getOwnPropertySymbols(r).length) throw TypeError("Invalid Games license input");
20
+ let i = Object.getOwnPropertyDescriptors(r);
21
+ if (Object.entries(i).some(([e, t]) => ![
22
+ "tenant",
23
+ "edition",
24
+ "entitlements",
25
+ "features",
26
+ "plugins",
27
+ "ttlSeconds",
28
+ "kid"
29
+ ].includes(e) || !t.enumerable || !("value" in t))) throw TypeError("Invalid Games license input");
30
+ let a = Object.fromEntries(Object.entries(i).map(([e, t]) => [e, t.value]));
31
+ e.decodeUnknownSync(e.toType(t))(a.edition);
32
+ let o = (e) => typeof e == "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,119}$/.test(e);
33
+ if (a.tenant !== void 0 && !o(a.tenant) || a.kid !== void 0 && !o(a.kid) || !Number.isSafeInteger(a.ttlSeconds) || a.ttlSeconds <= 0) throw TypeError("Invalid Games license input");
34
+ let s = (e) => {
35
+ if (e === void 0) return Object.freeze([]);
36
+ if (!Array.isArray(e) || e.length > 256 || Reflect.ownKeys(e).length !== e.length + 1) throw TypeError("Invalid Games license grants");
37
+ let t = [];
38
+ for (let n = 0; n < e.length; n++) {
39
+ let r = Object.getOwnPropertyDescriptor(e, String(n));
40
+ if (!r?.enumerable || !("value" in r) || !o(r.value)) throw TypeError("Invalid Games license grants");
41
+ t.push(r.value);
42
+ }
43
+ return Object.freeze([...new Set(t)]);
44
+ }, c = a.entitlements;
45
+ if (!c || Object.getPrototypeOf(c) !== Object.prototype || Reflect.ownKeys(c).length > 256) throw TypeError("Invalid Games license limits");
46
+ let l = {};
47
+ for (let e of Reflect.ownKeys(c)) {
48
+ let t = Object.getOwnPropertyDescriptor(c, e);
49
+ if (!o(e) || !t?.enumerable || !("value" in t)) throw TypeError("Invalid Games license limits");
50
+ let n = t.value;
51
+ if (n !== "unlimited" && (typeof n != "number" || !Number.isFinite(n) || n < 0)) throw TypeError("Invalid Games license limits");
52
+ Object.defineProperty(l, e, {
53
+ value: n,
54
+ enumerable: !0
55
+ });
56
+ }
57
+ return Object.freeze({
58
+ ...a,
59
+ entitlements: Object.freeze(l),
60
+ features: s(a.features),
61
+ plugins: Object.freeze([.../* @__PURE__ */ new Set([...s(a.plugins), ...n])])
62
+ });
63
+ };
64
+ export { n as GAMES_PLUGIN_GRANTS, t as GamesLicenseEdition, r as withGamesPluginGrants };
package/dist/gate.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { VoltroPlugin } from '@voltro/protocol';
2
+
3
+ /** Server-only factory admission. The ordinary lifecycle still owns order,
4
+ * hook execution and cleanup; no descriptor/transport/store/tick contribution. */
5
+ export declare const defineLicensedGamePlugin: (descriptor: VoltroPlugin) => VoltroPlugin;
6
+
7
+ export { }
package/dist/gate.js ADDED
@@ -0,0 +1,27 @@
1
+ import { GAMES_PLUGIN_GRANTS as e } from "./games.js";
2
+ import { PluginNotLicensed as t, licenseStatus as n, requireLicensedPlugin as r } from "./index.js";
3
+ import { Effect as i } from "effect";
4
+ import { definePlugin as a } from "@voltro/protocol";
5
+ // src/gate.ts
6
+ var o = (o) => {
7
+ let s = o.baseName ?? o.name, c = e.find((e) => s === `@voltro/plugin-${e}`);
8
+ if (c === void 0) throw TypeError("Game license admission requires an actual canonical Games plugin identity");
9
+ let l = o.onActivate;
10
+ return a({
11
+ ...o,
12
+ onActivate: (e) => i.gen(function* () {
13
+ let a = (e.env.NODE_ENV ?? process.env.NODE_ENV ?? "production").toLowerCase();
14
+ if (a !== "development" && a !== "test") {
15
+ let e = n();
16
+ if (e.state !== "active" && e.state !== "grace" || e.tenant !== void 0) return yield* i.fail(new t({ plugin: c }));
17
+ yield* r(c);
18
+ }
19
+ let o = l?.(e);
20
+ i.isEffect(o) ? yield* o : o !== void 0 && (yield* i.tryPromise({
21
+ try: () => Promise.resolve(o),
22
+ catch: (e) => e
23
+ }));
24
+ })
25
+ });
26
+ };
27
+ export { o as defineLicensedGamePlugin };
package/dist/index.d.ts CHANGED
@@ -67,6 +67,20 @@ export declare interface LicenseSnapshot {
67
67
  readonly state: () => 'active' | 'grace' | 'expired';
68
68
  }
69
69
 
70
+ /** Redacted transport refusal. No response body, URL, token or credential cause. */
71
+ export declare class LicenseSnapshotFetchError extends LicenseSnapshotFetchError_base<{
72
+ readonly reason: 'credentials' | 'transport' | 'status' | 'response';
73
+ readonly status?: number;
74
+ }> {
75
+ }
76
+
77
+ declare const LicenseSnapshotFetchError_base: new <A extends Record<string, any> = {}>(args: VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
78
+ readonly _tag: "LicenseSnapshotFetchError";
79
+ } & Readonly<A>;
80
+
81
+ /** Swappable server transport; verification and installation remain with Licensing. */
82
+ export declare type LicenseSnapshotSource = () => Effect.Effect<string, LicenseSnapshotFetchError>;
83
+
70
84
  /** The service handlers can `yield*` to read the current license directly (for
71
85
  * feature checks beyond billing's metered entitlements). */
72
86
  /**
@@ -111,6 +125,13 @@ declare const LicenseVerifyError_base: new <A extends Record<string, any> = {}>(
111
125
  readonly _tag: "LicenseVerifyError";
112
126
  } & Readonly<A>;
113
127
 
128
+ export declare interface LicenseVerifyOptions {
129
+ /** Explicit expiry-only grace. Signature, not-before and claim checks remain strict. */
130
+ readonly graceSeconds?: number;
131
+ /** Trusted server/test clock in whole Unix seconds. Defaults to the actual clock. */
132
+ readonly nowSeconds?: number;
133
+ }
134
+
114
135
  export declare const licensingPlugin: (options?: LicensingPluginOptions) => VoltroPlugin;
115
136
 
116
137
  export declare interface LicensingPluginOptions {
@@ -119,13 +140,10 @@ export declare interface LicensingPluginOptions {
119
140
  readonly publicKey?: string;
120
141
  /** The signed license token. Falls back to `VOLTRO_LICENSE_KEY`. */
121
142
  readonly licenseKey?: string;
122
- /** URL returning JSON containing a signed `token`. The coordinated refresh
123
- * verifies its signature before installing the new snapshot. */
124
- readonly snapshotUrl?: string;
125
- /** Server-only snapshot headers, for example a service Authorization header.
126
- * Redirects are refused. Keep credentials out of browsers and URLs. */
127
- readonly snapshotHeaders?: Readonly<Record<string, string>>;
128
- /** Refresh interval (ms) for `snapshotUrl`. Default 15 minutes. */
143
+ /** Actual server transport port for periodically retrieving a signed token.
144
+ * Verification remains offline; use ./cloud for authenticated JSON retrieval. */
145
+ readonly fetchSnapshot?: LicenseSnapshotSource;
146
+ /** Refresh interval (ms) for `fetchSnapshot`. Default 15 minutes. */
129
147
  readonly refreshMs?: number;
130
148
  /** Seconds an expired license is still honored (grace) before it goes dark.
131
149
  * Default 0. */
@@ -152,8 +170,9 @@ export declare const requireLicensedPlugin: (plugin: string) => Effect.Effect<vo
152
170
  /**
153
171
  * Verify a license token against an Ed25519 public key (SPKI/PEM), OFFLINE.
154
172
  * Returns the decoded {@link LicenseClaims} or a typed {@link LicenseVerifyError}
155
- * (expiry is enforced by `jose`). No network access.
173
+ * (expiry is enforced by `jose`, with explicit authenticated expiry-only grace).
174
+ * No network access. Grace never changes the clock tolerance for other claims.
156
175
  */
157
- export declare const verifyLicense: (token: string, publicKeyPem: string) => Effect.Effect<LicenseClaims, LicenseVerifyError>;
176
+ export declare const verifyLicense: (token: string, publicKeyPem: string, options?: LicenseVerifyOptions) => Effect.Effect<LicenseClaims, LicenseVerifyError>;
158
177
 
159
178
  export { }
package/dist/index.js CHANGED
@@ -1,18 +1,20 @@
1
- import { fetchLicenseSnapshot as e } from "./refresh.js";
2
- import { LicenseVerifyError as t, verifyLicense as n } from "./verify.js";
3
- import { getRejection as r, getSnapshot as i, makeSnapshot as a, setRejection as o, setSnapshot as s } from "./snapshot.js";
4
- import { entitlementResolver as c } from "./resolver.js";
5
- import { Data as l, Effect as u } from "effect";
6
- import { definePlugin as d, definePluginService as f, pluginInstanceName as p } from "@voltro/protocol";
7
- import { pluginEnv as m } from "@voltro/env";
8
- //#region src/index.ts
9
- var h = () => {
10
- let e = i(), t = e === void 0 ? void 0 : {
1
+ import { GAMES_PLUGIN_GRANTS as e } from "./games.js";
2
+ import { LicensingStatusSchema as t } from "./inspect.js";
3
+ import { LicenseVerifyError as n, verifyLicense as r } from "./verify.js";
4
+ import { getRejection as i, getSnapshot as a, makeSnapshot as o, setRejection as s, setSnapshot as c } from "./snapshot.js";
5
+ import { entitlementResolver as l } from "./resolver.js";
6
+ import { LicenseSnapshotFetchError as u } from "./cloud.js";
7
+ import { Data as d, Effect as f, Result as p, Schema as m } from "effect";
8
+ import { definePlugin as h, definePluginService as g, pluginInstanceName as _ } from "@voltro/protocol";
9
+ import { pluginEnv as v } from "@voltro/env";
10
+ // src/index.ts
11
+ var y = () => {
12
+ let e = a(), t = e === void 0 ? void 0 : {
11
13
  state: e.state(),
12
14
  edition: e.claims.edition,
13
15
  expiresAt: e.claims.exp,
14
16
  ...e.claims.tenant === void 0 ? {} : { tenant: e.claims.tenant }
15
- }, n = r();
17
+ }, n = i();
16
18
  return n === void 0 ? t ?? { state: "none" } : {
17
19
  state: "rejected",
18
20
  reason: n.reason,
@@ -23,7 +25,7 @@ var h = () => {
23
25
  expiresAt: t.expiresAt
24
26
  } }
25
27
  };
26
- }, g = m([{
28
+ }, b = v([{
27
29
  name: "VOLTRO_LICENSE_PUBLIC_KEY",
28
30
  required: !1,
29
31
  secret: !1,
@@ -31,61 +33,96 @@ var h = () => {
31
33
  }, {
32
34
  name: "VOLTRO_LICENSE_KEY",
33
35
  required: !1,
34
- secret: !1,
35
- description: "The signed EdDSA license token, verified offline at boot."
36
- }]), { Tag: _, Live: v } = f("@voltro/plugin-licensing/LicenseService", {
37
- snapshot: () => i(),
38
- status: () => h(),
39
- limitFor: (e, t) => i()?.limitFor(e, t),
40
- hasFeature: (e) => i()?.hasFeature(e) ?? !1,
41
- allowsPlugin: (e) => i()?.allowsPlugin(e) ?? !1,
42
- edition: () => i()?.claims.edition
43
- }), y = class extends l.TaggedError("PluginNotLicensed") {}, b = (e) => i()?.allowsPlugin(e) ?? !1, x = (e) => b(e) ? u.void : u.fail(new y({ plugin: e })), S = (t = {}) => {
44
- let r = g.read("VOLTRO_LICENSE_PUBLIC_KEY", t.publicKey), i = g.read("VOLTRO_LICENSE_KEY", t.licenseKey), c = t.graceSeconds ?? 0, l = t.refreshMs ?? 9e5, f, m = (e, t) => n(e, t).pipe(u.match({
36
+ secret: !0,
37
+ description: "The signed EdDSA license token, verified offline at boot; private local custody."
38
+ }]), { Tag: x, Live: S } = g("@voltro/plugin-licensing/LicenseService", {
39
+ snapshot: () => a(),
40
+ status: () => y(),
41
+ limitFor: (e, t) => a()?.limitFor(e, t),
42
+ hasFeature: (e) => a()?.hasFeature(e) ?? !1,
43
+ allowsPlugin: (e) => a()?.allowsPlugin(e) ?? !1,
44
+ edition: () => a()?.claims.edition
45
+ }), C = class extends d.TaggedError("PluginNotLicensed") {}, w = (e) => a()?.allowsPlugin(e) ?? !1, T = (e) => w(e) ? f.void : f.fail(new C({ plugin: e })), E = (n = {}) => {
46
+ let l = b.read("VOLTRO_LICENSE_PUBLIC_KEY", n.publicKey), u = b.read("VOLTRO_LICENSE_KEY", n.licenseKey), d = n.graceSeconds ?? 0, g = n.refreshMs ?? 9e5, v = n.fetchSnapshot;
47
+ if (!Number.isSafeInteger(g) || g <= 0 || !Number.isSafeInteger(d) || d < 0 || v !== void 0 && typeof v != "function") throw Error("Licensing requires positive whole-millisecond refresh, nonnegative whole-second grace and a callable snapshot port");
48
+ let y, x = 0, C = !1, w = (e, t, n) => r(e, t, { graceSeconds: d }).pipe(f.match({
45
49
  onSuccess: (e) => {
46
- s(a(e, { graceSeconds: c })), o(void 0), f?.info("license active", {
50
+ C && x === n && (c(o(e, { graceSeconds: d })), s(void 0), y?.info("license active", {
47
51
  edition: e.edition,
48
52
  entitlements: Object.keys(e.entitlements).length,
49
53
  exp: e.exp
50
- });
54
+ }));
51
55
  },
52
56
  onFailure: (e) => {
53
- o({
57
+ C && x === n && (s({
54
58
  reason: e.reason,
55
59
  message: e.message
56
- }), f?.warn("license verification failed — entitlements fall back to static plan", {
60
+ }), y?.warn("license verification failed — entitlements fall back to static plan", {
57
61
  reason: e.reason,
58
62
  message: e.message
59
- });
63
+ }));
60
64
  }
61
65
  }));
62
- return d({
63
- name: p({
66
+ return h({
67
+ name: _({
64
68
  base: "@voltro/plugin-licensing",
65
- instance: t.name
69
+ instance: n.name
66
70
  }),
71
+ baseName: "@voltro/plugin-licensing",
72
+ permissions: ["inspect:read"],
73
+ inspectEndpoints: [{
74
+ method: "GET",
75
+ path: "/status",
76
+ scope: "process",
77
+ description: "Redacted current-process license status; no credentials, tenant IDs or entitlement values.",
78
+ handler: () => f.try({
79
+ try: () => {
80
+ let n = a(), r = i(), o = n?.state(), s = n === void 0 ? null : {
81
+ state: o,
82
+ edition: n.claims.edition,
83
+ expiresAt: n.claims.exp,
84
+ scope: n.claims.tenant === void 0 ? "deployment" : "tenant",
85
+ gamesGrants: o === "expired" ? [] : e.filter((e) => n.allowsPlugin(e))
86
+ };
87
+ return {
88
+ kind: "json",
89
+ data: m.decodeUnknownSync(t)({
90
+ observedAt: Date.now(),
91
+ activated: C,
92
+ state: r === void 0 ? o ?? "none" : "rejected",
93
+ installed: s,
94
+ rejection: r?.reason ?? null,
95
+ graceSeconds: d,
96
+ refreshIntervalMs: v === void 0 || l === void 0 ? null : g
97
+ })
98
+ };
99
+ },
100
+ catch: () => /* @__PURE__ */ Error("License inspection unavailable")
101
+ })
102
+ }],
67
103
  description: "Offline-verified EdDSA license keys + entitlement snapshots; feeds @voltro/plugin-billing via entitlementResolver.",
68
- declaredEnv: g.declared,
69
- services: v,
70
- onActivate: (e) => u.gen(function* () {
71
- f = e.logger, r === void 0 || i === void 0 ? e.logger.debug("licensing: no license configured — static plan entitlements apply") : yield* m(i, r);
104
+ declaredEnv: b.declared,
105
+ services: S,
106
+ onActivate: (e) => f.gen(function* () {
107
+ y = e.logger, C = !0;
108
+ let t = ++x;
109
+ c(void 0), s(void 0), l === void 0 || u === void 0 ? e.logger.debug("licensing: no license configured — static plan entitlements apply") : yield* w(u, l, t);
72
110
  }),
73
- bindDataStore: (n, i) => {
74
- let a = t.snapshotUrl;
75
- if (a === void 0 || r === void 0 || i?.scheduleCoordinated === void 0) return;
76
- let o = r;
77
- i.scheduleCoordinated("licensing.refresh", l, async () => {
78
- let n = await e(a, t.snapshotHeaders);
79
- n.kind === "token" ? await u.runPromise(m(n.token, o)) : f?.warn("license refresh failed — retaining the last verified snapshot", {
80
- reason: n.reason,
81
- ...n.reason === "http" ? { status: n.status } : {}
82
- });
111
+ bindDataStore: (e, t) => {
112
+ if (v === void 0 || l === void 0 || t?.scheduleCoordinated === void 0) return;
113
+ let n = l;
114
+ t.scheduleCoordinated("licensing.refresh", g, async () => {
115
+ if (!C) return;
116
+ let e = x, t = await f.runPromise(f.result(v()));
117
+ C && x === e && (p.isFailure(t) ? y?.warn("license refresh failed — retaining the last verified snapshot", {
118
+ reason: t.failure.reason,
119
+ ...t.failure.status === void 0 ? {} : { status: t.failure.status }
120
+ }) : await f.runPromise(w(t.success, n, e)));
83
121
  });
84
122
  },
85
- onDeactivate: () => u.sync(() => {
86
- s(void 0), o(void 0);
123
+ onDeactivate: () => f.sync(() => {
124
+ C = !1, x++, c(void 0), s(void 0);
87
125
  })
88
126
  });
89
127
  };
90
- //#endregion
91
- export { _ as LicenseService, t as LicenseVerifyError, y as PluginNotLicensed, c as entitlementResolver, b as licenseAllowsPlugin, h as licenseStatus, S as licensingPlugin, x as requireLicensedPlugin, n as verifyLicense };
128
+ export { x as LicenseService, u as LicenseSnapshotFetchError, n as LicenseVerifyError, C as PluginNotLicensed, l as entitlementResolver, w as licenseAllowsPlugin, y as licenseStatus, E as licensingPlugin, T as requireLicensedPlugin, r as verifyLicense };
@@ -0,0 +1,56 @@
1
+ import { Schema } from 'effect';
2
+
3
+ declare const base: Schema.Struct<{
4
+ observedAt: Schema.Number;
5
+ activated: Schema.Boolean;
6
+ state: Schema.Literals<readonly ["none", "active", "grace", "expired", "rejected"]>;
7
+ installed: Schema.NullOr<Schema.Struct<{
8
+ readonly state: Schema.Literals<readonly ["active", "grace", "expired"]>;
9
+ readonly edition: Schema.String;
10
+ readonly expiresAt: Schema.Number;
11
+ readonly scope: Schema.Literals<readonly ["deployment", "tenant"]>;
12
+ readonly gamesGrants: Schema.$Array<Schema.Literals<readonly ["rooms", "matchmaking", "leaderboards", "social", "chat", "guilds", "inventory", "economy", "progression", "liveops", "players", "saves", "sanctions", "gm"]>>;
13
+ }>>;
14
+ rejection: Schema.NullOr<Schema.Literals<readonly ["expired", "invalid", "key", "malformed"]>>;
15
+ graceSeconds: Schema.Number;
16
+ refreshIntervalMs: Schema.NullOr<Schema.Number>;
17
+ }>;
18
+
19
+ export declare const isLicensingStatusConsistent: (value: typeof base.Type) => boolean;
20
+
21
+ export declare type LicensingStatus = typeof LicensingStatusSchema.Type;
22
+
23
+ export declare const LicensingStatusFields: {
24
+ observedAt: Schema.Number;
25
+ activated: Schema.Boolean;
26
+ state: Schema.Literals<readonly ["none", "active", "grace", "expired", "rejected"]>;
27
+ installed: Schema.NullOr<Schema.Struct<{
28
+ readonly state: Schema.Literals<readonly ["active", "grace", "expired"]>;
29
+ readonly edition: Schema.String;
30
+ readonly expiresAt: Schema.Number;
31
+ readonly scope: Schema.Literals<readonly ["deployment", "tenant"]>;
32
+ readonly gamesGrants: Schema.$Array<Schema.Literals<readonly ["rooms", "matchmaking", "leaderboards", "social", "chat", "guilds", "inventory", "economy", "progression", "liveops", "players", "saves", "sanctions", "gm"]>>;
33
+ }>>;
34
+ rejection: Schema.NullOr<Schema.Literals<readonly ["expired", "invalid", "key", "malformed"]>>;
35
+ graceSeconds: Schema.Number;
36
+ refreshIntervalMs: Schema.NullOr<Schema.Number>;
37
+ };
38
+
39
+ /** Browser-safe redacted process observation, never a token or grant authority. */
40
+ export declare const LicensingStatusSchema: Schema.Struct<{
41
+ observedAt: Schema.Number;
42
+ activated: Schema.Boolean;
43
+ state: Schema.Literals<readonly ["none", "active", "grace", "expired", "rejected"]>;
44
+ installed: Schema.NullOr<Schema.Struct<{
45
+ readonly state: Schema.Literals<readonly ["active", "grace", "expired"]>;
46
+ readonly edition: Schema.String;
47
+ readonly expiresAt: Schema.Number;
48
+ readonly scope: Schema.Literals<readonly ["deployment", "tenant"]>;
49
+ readonly gamesGrants: Schema.$Array<Schema.Literals<readonly ["rooms", "matchmaking", "leaderboards", "social", "chat", "guilds", "inventory", "economy", "progression", "liveops", "players", "saves", "sanctions", "gm"]>>;
50
+ }>>;
51
+ rejection: Schema.NullOr<Schema.Literals<readonly ["expired", "invalid", "key", "malformed"]>>;
52
+ graceSeconds: Schema.Number;
53
+ refreshIntervalMs: Schema.NullOr<Schema.Number>;
54
+ }>;
55
+
56
+ export { }
@@ -0,0 +1,47 @@
1
+ import { GAMES_PLUGIN_GRANTS as e } from "./games.js";
2
+ import { Schema as t } from "effect";
3
+ // src/inspect.ts
4
+ var n = t.Number.pipe(t.check(t.isInt()), t.check(t.isBetween({
5
+ minimum: 0,
6
+ maximum: 2 ** 53 - 1
7
+ }))), r = t.Literals([
8
+ "active",
9
+ "grace",
10
+ "expired"
11
+ ]), i = {
12
+ observedAt: t.Number.pipe(t.check(t.isInt()), t.check(t.isBetween({
13
+ minimum: 0,
14
+ maximum: 864e13
15
+ }))),
16
+ activated: t.Boolean,
17
+ state: t.Literals([
18
+ "none",
19
+ "active",
20
+ "grace",
21
+ "expired",
22
+ "rejected"
23
+ ]),
24
+ installed: t.NullOr(t.Struct({
25
+ state: r,
26
+ edition: t.String.pipe(t.check(t.isMinLength(1)), t.check(t.isMaxLength(256))),
27
+ expiresAt: t.Number.pipe(t.check(t.isInt()), t.check(t.isBetween({
28
+ minimum: 0,
29
+ maximum: 864e10
30
+ }))),
31
+ scope: t.Literals(["deployment", "tenant"]),
32
+ gamesGrants: t.Array(t.Literals(e))
33
+ })),
34
+ rejection: t.NullOr(t.Literals([
35
+ "expired",
36
+ "invalid",
37
+ "key",
38
+ "malformed"
39
+ ])),
40
+ graceSeconds: n,
41
+ refreshIntervalMs: t.NullOr(n.pipe(t.check(t.isGreaterThan(0))))
42
+ }, a = t.Struct(i), o = (e) => {
43
+ if (e.state === "rejected" != (e.rejection !== null) || e.state === "none" && e.installed !== null || e.state !== "none" && e.state !== "rejected" && e.installed?.state !== e.state) return !1;
44
+ let t = e.installed;
45
+ return !(t !== null && (new Set(t.gamesGrants).size !== t.gamesGrants.length || t.state === "expired" && t.gamesGrants.length > 0 || t.state === "grace" && (e.graceSeconds === 0 || t.expiresAt === 0)));
46
+ }, s = a.pipe(t.check(t.makeFilter(o)));
47
+ export { i as LicensingStatusFields, s as LicensingStatusSchema, o as isLicensingStatusConsistent };
package/dist/resolver.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { getSnapshot as e } from "./snapshot.js";
2
2
  import { Effect as t } from "effect";
3
- //#region src/resolver.ts
3
+ // src/resolver.ts
4
4
  var n = (n, r) => t.sync(() => {
5
5
  let t = e()?.limitFor(n, r);
6
6
  return t === void 0 ? null : t;
7
7
  });
8
- //#endregion
9
8
  export { n as entitlementResolver };
package/dist/sign.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SignJWT as e, importPKCS8 as t } from "jose";
2
- //#region src/sign.ts
2
+ // src/sign.ts
3
3
  var n = async (n, r, i) => {
4
4
  let a = await t(r, "EdDSA"), o = i ?? Math.floor(Date.now() / 1e3);
5
5
  return new e({
@@ -13,5 +13,4 @@ var n = async (n, r, i) => {
13
13
  ...n.kid === void 0 ? {} : { kid: n.kid }
14
14
  }).setIssuedAt(o).setExpirationTime(o + n.ttlSeconds).sign(a);
15
15
  };
16
- //#endregion
17
16
  export { n as signLicense };