@usagekit/meter 0.1.0 → 0.4.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/access.d.ts +11 -0
- package/dist/access.js +11 -0
- package/dist/catalog.d.ts +15 -0
- package/dist/catalog.js +62 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/meter.d.ts +6 -2
- package/dist/meter.js +71 -16
- package/dist/validation.d.ts +9 -1
- package/dist/validation.js +42 -5
- package/package.json +3 -3
package/dist/access.d.ts
CHANGED
|
@@ -5,4 +5,15 @@ export type OwnershipResolver = (scope: Extract<BudgetScope, {
|
|
|
5
5
|
}>) => Promise<BudgetOwner | null>;
|
|
6
6
|
export declare const includes: (list: readonly string[] | "*", id: string) => boolean;
|
|
7
7
|
export declare function canRead(access: AccessContext, scope: UsageScope): boolean;
|
|
8
|
+
/** Pools and tags span principals: their figures are shared, so unreadable ones are redacted, not forbidden. */
|
|
9
|
+
export declare const isShared: (scope: BudgetScope) => scope is {
|
|
10
|
+
kind: "platform_pool";
|
|
11
|
+
namespace: string;
|
|
12
|
+
poolId: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "tag";
|
|
15
|
+
namespace: string;
|
|
16
|
+
tag: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function canReadShared(access: AccessContext, scope: BudgetScope): boolean;
|
|
8
19
|
export declare function canReadBudget(resolveOwnership: OwnershipResolver | undefined, access: AccessContext, scope: BudgetScope): Promise<boolean>;
|
package/dist/access.js
CHANGED
|
@@ -13,9 +13,20 @@ export function canRead(access, scope) {
|
|
|
13
13
|
return access.canManageBudgets || includes(access.readablePools, scope.poolId);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
/** Pools and tags span principals: their figures are shared, so unreadable ones are redacted, not forbidden. */
|
|
17
|
+
export const isShared = (scope) => scope.kind === "platform_pool" || scope.kind === "tag";
|
|
18
|
+
export function canReadShared(access, scope) {
|
|
19
|
+
if (access.namespace !== scope.namespace)
|
|
20
|
+
return false;
|
|
21
|
+
if (scope.kind === "platform_pool")
|
|
22
|
+
return canRead(access, scope);
|
|
23
|
+
return access.canManageBudgets || access.readablePrincipals === "*";
|
|
24
|
+
}
|
|
16
25
|
export async function canReadBudget(resolveOwnership, access, scope) {
|
|
17
26
|
if (access.namespace !== scope.namespace)
|
|
18
27
|
return false;
|
|
28
|
+
if (isShared(scope))
|
|
29
|
+
return canReadShared(access, scope);
|
|
19
30
|
if (scope.kind !== "connection" && scope.kind !== "access_credential")
|
|
20
31
|
return canRead(access, scope);
|
|
21
32
|
const owner = await resolveOwnership?.(scope);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ConnectionPolicy, MeterReserveInput, Operation, PricingCatalog, ReserveInput, ValidationFailure } from "@usagekit/core";
|
|
2
|
+
/** Host lookup of a connection's provider, plan, manual prices and tracking policy. */
|
|
3
|
+
export type ConnectionResolver = (connectionId: string) => Promise<ConnectionPolicy | undefined> | ConnectionPolicy | undefined;
|
|
4
|
+
/** What reserve does with a request after the catalog and the connection policy spoke. */
|
|
5
|
+
export type ReserveResolution = {
|
|
6
|
+
kind: "reserve";
|
|
7
|
+
input: ReserveInput;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "count";
|
|
10
|
+
state: "passthrough" | "unpriced";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "invalid";
|
|
13
|
+
failure: ValidationFailure;
|
|
14
|
+
};
|
|
15
|
+
export declare function resolveReserve(requested: MeterReserveInput, existing: Operation | null, catalog?: PricingCatalog, resolveConnection?: ConnectionResolver): Promise<ReserveResolution>;
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const invalid = (field, reason) => ({
|
|
2
|
+
kind: "invalid",
|
|
3
|
+
failure: { outcome: "invalid", field, reason },
|
|
4
|
+
});
|
|
5
|
+
/**
|
|
6
|
+
* Order: a stored operation replays with its own estimate; then the connection's tracking
|
|
7
|
+
* policy and free catalog operations count as passthrough; then a given estimate is used as is;
|
|
8
|
+
* then the catalog resolves one (manual, measured, list, unknown), and an operation the catalog
|
|
9
|
+
* does not know counts as unpriced.
|
|
10
|
+
*/
|
|
11
|
+
const provenanceOf = (estimateSource, providerPriceVersion) => ({
|
|
12
|
+
...(estimateSource ? { estimateSource } : {}),
|
|
13
|
+
...(providerPriceVersion ? { providerPriceVersion } : {}),
|
|
14
|
+
});
|
|
15
|
+
export async function resolveReserve(requested, existing, catalog, resolveConnection) {
|
|
16
|
+
const { options = {}, estimate, estimateSource, providerPriceVersion, ...rest } = requested;
|
|
17
|
+
// A retry that omits the estimate replays the stored one, including provenance. A retry that
|
|
18
|
+
// sends an estimate keeps only the provenance on that request.
|
|
19
|
+
if (existing) {
|
|
20
|
+
if (estimate === undefined)
|
|
21
|
+
return {
|
|
22
|
+
kind: "reserve",
|
|
23
|
+
input: {
|
|
24
|
+
...rest,
|
|
25
|
+
estimate: existing.estimate,
|
|
26
|
+
...provenanceOf(existing.estimateSource, existing.providerPriceVersion),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
kind: "reserve",
|
|
31
|
+
input: { ...rest, estimate, ...provenanceOf(estimateSource, providerPriceVersion) },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const policy = catalog && resolveConnection ? await resolveConnection(rest.scope.connection) : undefined;
|
|
35
|
+
if (policy && policy.provider !== rest.provider)
|
|
36
|
+
return invalid("provider", "differs from the connection provider");
|
|
37
|
+
if (policy && catalog) {
|
|
38
|
+
const known = catalog.operationsOf(policy.provider).find((o) => o.id === rest.operation);
|
|
39
|
+
if (policy.tracking?.[rest.operation] === "passthrough" || known?.billable === false)
|
|
40
|
+
return { kind: "count", state: "passthrough" };
|
|
41
|
+
if (estimate === undefined) {
|
|
42
|
+
if (!known)
|
|
43
|
+
return { kind: "count", state: "unpriced" };
|
|
44
|
+
const { tracking: _, ...connection } = policy, resolved = catalog.estimate({ ...connection, id: rest.scope.connection }, rest.operation, options);
|
|
45
|
+
return {
|
|
46
|
+
kind: "reserve",
|
|
47
|
+
input: {
|
|
48
|
+
...rest,
|
|
49
|
+
estimate: resolved.quantities,
|
|
50
|
+
estimateSource: resolved.source,
|
|
51
|
+
...(resolved.priceVersion ? { providerPriceVersion: resolved.priceVersion } : {}),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (estimate === undefined)
|
|
57
|
+
return invalid("estimate", catalog && resolveConnection ? "unknown connection" : "required without a catalog");
|
|
58
|
+
return {
|
|
59
|
+
kind: "reserve",
|
|
60
|
+
input: { ...rest, estimate, ...provenanceOf(estimateSource, providerPriceVersion) },
|
|
61
|
+
};
|
|
62
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export { createMeter } from "./meter.js";
|
|
|
3
3
|
export { defaultPolicy } from "./policy.js";
|
|
4
4
|
export type { MeterPolicy } from "./policy.js";
|
|
5
5
|
export type { OwnershipResolver } from "./access.js";
|
|
6
|
+
export { resolveReserve } from "./catalog.js";
|
|
7
|
+
export type { ConnectionResolver, ReserveResolution } from "./catalog.js";
|
package/dist/index.js
CHANGED
package/dist/meter.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import type { Meter } from "@usagekit/core";
|
|
1
|
+
import type { Meter, PricingCatalog } from "@usagekit/core";
|
|
2
|
+
import type { ConnectionResolver } from "./catalog.js";
|
|
2
3
|
import type { Store, Clock } from "@usagekit/store";
|
|
3
4
|
import type { MeterPolicy } from "./policy.js";
|
|
4
|
-
export declare function createMeter({ store, policy, clock, resolveOwnership, }: {
|
|
5
|
+
export declare function createMeter({ store, policy, clock, resolveOwnership, catalog, resolveConnection, }: {
|
|
5
6
|
store: Store;
|
|
6
7
|
policy?: MeterPolicy;
|
|
7
8
|
clock: Clock;
|
|
8
9
|
resolveOwnership?: import("./access.js").OwnershipResolver;
|
|
10
|
+
/** With resolveConnection, lets reserve omit estimate and applies tracking policy. */
|
|
11
|
+
catalog?: PricingCatalog;
|
|
12
|
+
resolveConnection?: ConnectionResolver;
|
|
9
13
|
}): Meter;
|
package/dist/meter.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { resolveReserve } from "./catalog.js";
|
|
1
2
|
import { InvalidInput } from "@usagekit/store";
|
|
2
3
|
import { defaultPolicy } from "./policy.js";
|
|
3
|
-
import { validate, reserveValidation, usageValidation } from "./validation.js";
|
|
4
|
-
import { canRead, canReadBudget } from "./access.js";
|
|
5
|
-
|
|
4
|
+
import { validate, reserveValidation, usageValidation, operationsValidation, sourceValidation, provenanceValidation, requestCountsValidation, } from "./validation.js";
|
|
5
|
+
import { canRead, canReadBudget, canReadShared, isShared } from "./access.js";
|
|
6
|
+
import { defaultBudgetOrder, normalizeTags } from "@usagekit/core";
|
|
7
|
+
export function createMeter({ store, policy = defaultPolicy, clock, resolveOwnership, catalog, resolveConnection, }) {
|
|
6
8
|
if (!Number.isFinite(clock.now().getTime()))
|
|
7
9
|
throw new Error("InvalidInput: clock");
|
|
8
|
-
if (policy.budgetOrder.length !==
|
|
9
|
-
new Set(policy.budgetOrder).size !==
|
|
10
|
-
policy.budgetOrder.some((k) => !
|
|
10
|
+
if (policy.budgetOrder.length !== defaultBudgetOrder.length ||
|
|
11
|
+
new Set(policy.budgetOrder).size !== defaultBudgetOrder.length ||
|
|
12
|
+
policy.budgetOrder.some((k) => !defaultBudgetOrder.includes(k)))
|
|
11
13
|
throw new Error("InvalidInput: budgetOrder");
|
|
12
14
|
async function safely(fn) {
|
|
13
15
|
try {
|
|
@@ -29,21 +31,54 @@ export function createMeter({ store, policy = defaultPolicy, clock, resolveOwner
|
|
|
29
31
|
const read = async (fn) => safely(async () => ({ outcome: "ok", value: await fn() }));
|
|
30
32
|
return {
|
|
31
33
|
expireReservations: (i) => command(i, () => store.expireReservations(i)),
|
|
32
|
-
reserve: async (
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
reserve: async (requested) => {
|
|
35
|
+
const early = validate({ ...requested, estimate: requested.estimate ?? [] }) ??
|
|
36
|
+
sourceValidation(requested) ??
|
|
37
|
+
provenanceValidation(requested);
|
|
38
|
+
if (early)
|
|
39
|
+
return early;
|
|
36
40
|
return safely(async () => {
|
|
37
41
|
const existing = await store.getOperation({
|
|
38
|
-
namespace:
|
|
39
|
-
principal:
|
|
40
|
-
operationId:
|
|
42
|
+
namespace: requested.scope.namespace,
|
|
43
|
+
principal: requested.scope.principal,
|
|
44
|
+
operationId: requested.operationId,
|
|
41
45
|
});
|
|
46
|
+
const resolution = await resolveReserve(requested, existing, catalog, resolveConnection);
|
|
47
|
+
if (resolution.kind === "invalid")
|
|
48
|
+
return resolution.failure;
|
|
49
|
+
if (resolution.kind === "count") {
|
|
50
|
+
const { scope, surface, source, provider, operation, platformPools } = requested;
|
|
51
|
+
const counted = await store.countRequest({
|
|
52
|
+
commandId: requested.operationId,
|
|
53
|
+
scope,
|
|
54
|
+
surface,
|
|
55
|
+
source,
|
|
56
|
+
provider,
|
|
57
|
+
operation,
|
|
58
|
+
state: resolution.state,
|
|
59
|
+
...(platformPools ? { platformPools } : {}),
|
|
60
|
+
});
|
|
61
|
+
if (counted.outcome === "conflict")
|
|
62
|
+
return {
|
|
63
|
+
outcome: "invalid",
|
|
64
|
+
field: "operationId",
|
|
65
|
+
reason: "reused for a different counted request",
|
|
66
|
+
};
|
|
67
|
+
if (counted.outcome === "invalid")
|
|
68
|
+
return counted;
|
|
69
|
+
return { outcome: resolution.state, replayed: counted.replayed, operation: null };
|
|
70
|
+
}
|
|
71
|
+
const raw = resolution.input, invalid = reserveValidation(raw);
|
|
72
|
+
if (invalid)
|
|
73
|
+
return invalid;
|
|
74
|
+
// Tags are attribution: sorted here so every adapter stores one canonical order.
|
|
75
|
+
const tags = raw.scope.tags === undefined ? undefined : normalizeTags(raw.scope.tags), i = tags ? { ...raw, scope: { ...raw.scope, tags } } : raw;
|
|
42
76
|
if (existing)
|
|
43
77
|
return store.reserve(i, { budgetOrder: policy.budgetOrder });
|
|
44
78
|
const statuses = await store.applicableBudgets({
|
|
45
79
|
scope: i.scope,
|
|
46
80
|
surface: i.surface,
|
|
81
|
+
source: i.source,
|
|
47
82
|
units: i.estimate.map((q) => q.unit),
|
|
48
83
|
...(i.platformPools ? { platformPools: i.platformPools } : {}),
|
|
49
84
|
});
|
|
@@ -64,6 +99,18 @@ export function createMeter({ store, policy = defaultPolicy, clock, resolveOwner
|
|
|
64
99
|
settle: (i) => command(i, () => store.settle(i)),
|
|
65
100
|
correct: (i) => command(i, () => store.correct(i)),
|
|
66
101
|
releaseUndispatched: (i) => command(i, () => store.releaseUndispatched(i)),
|
|
102
|
+
countRequest: (i) => {
|
|
103
|
+
const invalid = validate(i) ?? sourceValidation(i);
|
|
104
|
+
return invalid ? Promise.resolve(invalid) : safely(() => store.countRequest(i));
|
|
105
|
+
},
|
|
106
|
+
requestCounts: async (access, q) => {
|
|
107
|
+
const invalid = requestCountsValidation(q);
|
|
108
|
+
if (invalid)
|
|
109
|
+
return invalid;
|
|
110
|
+
if (!access.canReadBillingDetail || !canRead(access, q.scope))
|
|
111
|
+
return { outcome: "forbidden" };
|
|
112
|
+
return read(() => store.requestCounts(q));
|
|
113
|
+
},
|
|
67
114
|
getOperation: async (access, i) => {
|
|
68
115
|
const invalid = validate(i);
|
|
69
116
|
if (invalid)
|
|
@@ -81,6 +128,14 @@ export function createMeter({ store, policy = defaultPolicy, clock, resolveOwner
|
|
|
81
128
|
return { outcome: "forbidden" };
|
|
82
129
|
return read(() => store.aggregate(q));
|
|
83
130
|
},
|
|
131
|
+
listOperations: async (access, q) => {
|
|
132
|
+
const invalid = operationsValidation(q);
|
|
133
|
+
if (invalid)
|
|
134
|
+
return invalid;
|
|
135
|
+
if (!access.canReadBillingDetail || !canRead(access, q.scope))
|
|
136
|
+
return { outcome: "forbidden" };
|
|
137
|
+
return read(() => store.listOperations(q));
|
|
138
|
+
},
|
|
84
139
|
definedBudgets: async (access, q) => {
|
|
85
140
|
const invalid = validate(q);
|
|
86
141
|
if (invalid)
|
|
@@ -90,7 +145,7 @@ export function createMeter({ store, policy = defaultPolicy, clock, resolveOwner
|
|
|
90
145
|
return read(() => store.definedBudgets(q));
|
|
91
146
|
},
|
|
92
147
|
applicableBudgets: async (access, q) => {
|
|
93
|
-
const invalid = validate(q);
|
|
148
|
+
const invalid = validate(q) ?? sourceValidation(q);
|
|
94
149
|
if (invalid)
|
|
95
150
|
return invalid;
|
|
96
151
|
if (!canRead(access, {
|
|
@@ -102,13 +157,13 @@ export function createMeter({ store, policy = defaultPolicy, clock, resolveOwner
|
|
|
102
157
|
return safely(async () => {
|
|
103
158
|
const rows = await store.applicableBudgets(q);
|
|
104
159
|
for (const status of rows) {
|
|
105
|
-
if (status.budget.scope
|
|
160
|
+
if (!isShared(status.budget.scope) &&
|
|
106
161
|
!(await canReadBudget(resolveOwnership, access, status.budget.scope)))
|
|
107
162
|
return { outcome: "forbidden" };
|
|
108
163
|
}
|
|
109
164
|
return {
|
|
110
165
|
outcome: "ok",
|
|
111
|
-
value: rows.map((s) => s.budget.scope
|
|
166
|
+
value: rows.map((s) => isShared(s.budget.scope) && !canReadShared(access, s.budget.scope)
|
|
112
167
|
? {
|
|
113
168
|
...s,
|
|
114
169
|
budget: { ...s.budget, limit: null },
|
package/dist/validation.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type { ValidationFailure, ReserveInput, UsageQuery } from "@usagekit/core";
|
|
1
|
+
import type { ValidationFailure, ReserveInput, UsageQuery, ApplicableBudgetsQuery, OperationsQuery, RequestCountsQuery } from "@usagekit/core";
|
|
2
2
|
export declare function validate(value: unknown, field?: string): ValidationFailure | null;
|
|
3
|
+
/** The host sets both fields at authentication; a source outside its surface group is a forgery or a bug. */
|
|
4
|
+
export declare function sourceValidation(i: Pick<ApplicableBudgetsQuery, "surface" | "source">): ValidationFailure | null;
|
|
3
5
|
export declare function reserveValidation(i: ReserveInput): ValidationFailure | null;
|
|
6
|
+
export declare function operationsValidation(q: OperationsQuery): ValidationFailure | null;
|
|
4
7
|
export declare function usageValidation(q: UsageQuery): ValidationFailure | null;
|
|
8
|
+
/** Hosts may name the source; a value outside the catalog order is rejected before Store. */
|
|
9
|
+
export declare function provenanceValidation(i: {
|
|
10
|
+
estimateSource?: string;
|
|
11
|
+
}): ValidationFailure | null;
|
|
12
|
+
export declare function requestCountsValidation(q: RequestCountsQuery): ValidationFailure | null;
|
package/dist/validation.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sourcesOf, normalizeTags } from "@usagekit/core";
|
|
1
2
|
export function validate(value, field = "input") {
|
|
2
3
|
const fail = (reason) => ({ outcome: "invalid", field, reason });
|
|
3
4
|
if (typeof value === "string" && !value.trim())
|
|
@@ -22,24 +23,49 @@ export function validate(value, field = "input") {
|
|
|
22
23
|
}
|
|
23
24
|
return null;
|
|
24
25
|
}
|
|
26
|
+
/** The host sets both fields at authentication; a source outside its surface group is a forgery or a bug. */
|
|
27
|
+
export function sourceValidation(i) {
|
|
28
|
+
if (i.source !== undefined && !sourcesOf(i.surface)?.includes(i.source))
|
|
29
|
+
return { outcome: "invalid", field: "source", reason: "source outside surface group" };
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
25
32
|
export function reserveValidation(i) {
|
|
26
|
-
const error = validate(i);
|
|
33
|
+
const error = validate(i) ?? sourceValidation(i);
|
|
27
34
|
if (error)
|
|
28
35
|
return error;
|
|
36
|
+
if (i.scope.tags !== undefined && !normalizeTags(i.scope.tags))
|
|
37
|
+
return {
|
|
38
|
+
outcome: "invalid",
|
|
39
|
+
field: "scope.tags",
|
|
40
|
+
reason: "1 to 16 unique tags matching the tag pattern",
|
|
41
|
+
};
|
|
29
42
|
if (new Set(i.estimate.map((q) => q.unit)).size !== i.estimate.length)
|
|
30
43
|
return { outcome: "invalid", field: "estimate", reason: "duplicate unit" };
|
|
31
44
|
return null;
|
|
32
45
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (error)
|
|
36
|
-
return error;
|
|
46
|
+
const lifecycle = ["reserved", "dispatch_intended", "pending", "settled", "released"];
|
|
47
|
+
function windowValidation(q) {
|
|
37
48
|
if (!Number.isFinite(Date.parse(q.from)) ||
|
|
38
49
|
!Number.isFinite(Date.parse(q.to)) ||
|
|
39
50
|
Date.parse(q.from) > Date.parse(q.to))
|
|
40
51
|
return { outcome: "invalid", field: "window", reason: "invalid time interval" };
|
|
41
52
|
if (q.limit !== undefined && (q.limit < 1 || q.limit > 1000))
|
|
42
53
|
return { outcome: "invalid", field: "limit", reason: "must be between 1 and 1000" };
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
export function operationsValidation(q) {
|
|
57
|
+
const error = validate(q) ?? windowValidation(q);
|
|
58
|
+
if (error)
|
|
59
|
+
return error;
|
|
60
|
+
if (q.states !== undefined &&
|
|
61
|
+
(q.states.length < 1 || q.states.some((state) => !lifecycle.includes(state))))
|
|
62
|
+
return { outcome: "invalid", field: "states", reason: "one or more lifecycle states" };
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
export function usageValidation(q) {
|
|
66
|
+
const error = validate(q) ?? windowValidation(q);
|
|
67
|
+
if (error)
|
|
68
|
+
return error;
|
|
43
69
|
if (q.groupBy.includes("principal") && !["namespace", "group"].includes(q.scope.kind))
|
|
44
70
|
return {
|
|
45
71
|
outcome: "invalid",
|
|
@@ -48,3 +74,14 @@ export function usageValidation(q) {
|
|
|
48
74
|
};
|
|
49
75
|
return null;
|
|
50
76
|
}
|
|
77
|
+
const estimateSources = ["manual", "measured", "list", "unknown"];
|
|
78
|
+
/** Hosts may name the source; a value outside the catalog order is rejected before Store. */
|
|
79
|
+
export function provenanceValidation(i) {
|
|
80
|
+
if (i.estimateSource !== undefined &&
|
|
81
|
+
!estimateSources.includes(i.estimateSource))
|
|
82
|
+
return { outcome: "invalid", field: "estimateSource", reason: "unknown estimate source" };
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
export function requestCountsValidation(q) {
|
|
86
|
+
return validate(q) ?? windowValidation(q);
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usagekit/meter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "Admission, reservations, settlement and recovery.",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"prepack": "node ../../scripts/package-license.mjs"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@usagekit/core": "0.
|
|
27
|
-
"@usagekit/store": "0.
|
|
26
|
+
"@usagekit/core": "0.4.0",
|
|
27
|
+
"@usagekit/store": "0.4.0"
|
|
28
28
|
},
|
|
29
29
|
"sideEffects": false,
|
|
30
30
|
"publishConfig": {
|