@neetru/sdk 1.0.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/README.md +218 -0
  3. package/dist/auth.cjs +1137 -0
  4. package/dist/auth.cjs.map +1 -0
  5. package/dist/auth.d.cts +25 -0
  6. package/dist/auth.d.ts +25 -0
  7. package/dist/auth.mjs +1135 -0
  8. package/dist/auth.mjs.map +1 -0
  9. package/dist/catalog.cjs +179 -0
  10. package/dist/catalog.cjs.map +1 -0
  11. package/dist/catalog.d.cts +17 -0
  12. package/dist/catalog.d.ts +17 -0
  13. package/dist/catalog.mjs +177 -0
  14. package/dist/catalog.mjs.map +1 -0
  15. package/dist/db.cjs +232 -0
  16. package/dist/db.cjs.map +1 -0
  17. package/dist/db.d.cts +8 -0
  18. package/dist/db.d.ts +8 -0
  19. package/dist/db.mjs +230 -0
  20. package/dist/db.mjs.map +1 -0
  21. package/dist/entitlements.cjs +140 -0
  22. package/dist/entitlements.cjs.map +1 -0
  23. package/dist/entitlements.d.cts +14 -0
  24. package/dist/entitlements.d.ts +14 -0
  25. package/dist/entitlements.mjs +138 -0
  26. package/dist/entitlements.mjs.map +1 -0
  27. package/dist/errors.cjs +20 -0
  28. package/dist/errors.cjs.map +1 -0
  29. package/dist/errors.d.cts +27 -0
  30. package/dist/errors.d.ts +27 -0
  31. package/dist/errors.mjs +18 -0
  32. package/dist/errors.mjs.map +1 -0
  33. package/dist/index.cjs +1154 -0
  34. package/dist/index.cjs.map +1 -0
  35. package/dist/index.d.cts +24 -0
  36. package/dist/index.d.ts +24 -0
  37. package/dist/index.mjs +1142 -0
  38. package/dist/index.mjs.map +1 -0
  39. package/dist/mocks.cjs +281 -0
  40. package/dist/mocks.cjs.map +1 -0
  41. package/dist/mocks.d.cts +138 -0
  42. package/dist/mocks.d.ts +138 -0
  43. package/dist/mocks.mjs +274 -0
  44. package/dist/mocks.mjs.map +1 -0
  45. package/dist/support.cjs +176 -0
  46. package/dist/support.cjs.map +1 -0
  47. package/dist/support.d.cts +5 -0
  48. package/dist/support.d.ts +5 -0
  49. package/dist/support.mjs +174 -0
  50. package/dist/support.mjs.map +1 -0
  51. package/dist/telemetry.cjs +225 -0
  52. package/dist/telemetry.cjs.map +1 -0
  53. package/dist/telemetry.d.cts +35 -0
  54. package/dist/telemetry.d.ts +35 -0
  55. package/dist/telemetry.mjs +223 -0
  56. package/dist/telemetry.mjs.map +1 -0
  57. package/dist/types-PKUaFtBY.d.cts +408 -0
  58. package/dist/types-PKUaFtBY.d.ts +408 -0
  59. package/dist/usage.cjs +235 -0
  60. package/dist/usage.cjs.map +1 -0
  61. package/dist/usage.d.cts +5 -0
  62. package/dist/usage.d.ts +5 -0
  63. package/dist/usage.mjs +233 -0
  64. package/dist/usage.mjs.map +1 -0
  65. package/package.json +79 -0
@@ -0,0 +1,138 @@
1
+ import { j as NeetruUser, A as AuthNamespace, S as SignInOptions, b as AuthStateListener, g as DbNamespace, e as DbCollectionRef, E as EntitlementCheck, m as SupportNamespace, d as CreateTicketInput, p as SupportTicket, r as UsageNamespace, s as UsageQuota } from './types-PKUaFtBY.cjs';
2
+
3
+ /**
4
+ * Mocks determinísticos do SDK — overridáveis em tests do consumer ou
5
+ * ativados automaticamente quando `NEETRU_ENV=dev`.
6
+ *
7
+ * Nada aqui faz I/O. Tudo é puro/sincrono/in-memory. Compat total com
8
+ * runtimes browser/Node/Edge — sem `node:` builtins.
9
+ *
10
+ * Cada mock implementa o mesmo namespace interface das implementações reais
11
+ * (`AuthNamespace`, `UsageNamespace`, `SupportNamespace`) — caller pode
12
+ * substituir 1:1 sem branching.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { createNeetruClient, MockAuth } from '@neetru/sdk';
17
+ *
18
+ * const client = createNeetruClient({
19
+ * mocks: { auth: new MockAuth({ uid: 'fake-user', email: 'x@x.com' }) },
20
+ * });
21
+ * ```
22
+ */
23
+
24
+ /**
25
+ * User fixture determinístico — usado quando `NEETRU_ENV=dev` e nenhum mock
26
+ * custom é injetado. Email/uid estáveis pra snapshot tests.
27
+ */
28
+ declare const DEV_FIXTURE_USER: NeetruUser;
29
+ /**
30
+ * Implementação in-memory do `AuthNamespace`. signIn marca o user como
31
+ * logado; signOut limpa. onAuthStateChanged dispara callback síncrono no
32
+ * subscribe + a cada mudança.
33
+ */
34
+ declare class MockAuth implements AuthNamespace {
35
+ private _user;
36
+ private _listeners;
37
+ constructor(initialUser?: NeetruUser | null);
38
+ signIn(_options?: SignInOptions): Promise<NeetruUser>;
39
+ signOut(): Promise<void>;
40
+ getUser(): NeetruUser | null;
41
+ onAuthStateChanged(listener: AuthStateListener): () => void;
42
+ /** Helper de tests — força um user state arbitrário. */
43
+ __setUser(user: NeetruUser | null): void;
44
+ private _notify;
45
+ }
46
+ interface UsageRecord {
47
+ event: string;
48
+ properties?: Record<string, string | number | boolean | null>;
49
+ timestamp: string;
50
+ }
51
+ /**
52
+ * In-memory `UsageNamespace`. `track()` apenas acumula no array
53
+ * `__getRecords()` (útil pra asserts em test). `getQuota()` retorna fixture
54
+ * com `used = trackedCount` da metric.
55
+ */
56
+ declare class MockUsage implements UsageNamespace {
57
+ private _records;
58
+ private _quotas;
59
+ /** v0.3 — counters in-memory pra `report()` / `check()`. */
60
+ private _counters;
61
+ constructor(initialQuotas?: Record<string, UsageQuota>);
62
+ track(event: string, properties?: Record<string, string | number | boolean | null>): Promise<{
63
+ ok: true;
64
+ }>;
65
+ getQuota(metric: string): Promise<UsageQuota>;
66
+ /** v0.3 — Mock report incrementa o counter local. */
67
+ report(resource: string, qty?: number, _options?: {
68
+ productId?: string;
69
+ tenantId?: string;
70
+ }): Promise<{
71
+ ok: true;
72
+ counterId?: string;
73
+ value?: number;
74
+ limit?: number;
75
+ remaining?: number;
76
+ status?: string;
77
+ }>;
78
+ /** v0.3 — Mock check usa quotas + counters in-memory. */
79
+ check(resource: string, _options?: {
80
+ productId?: string;
81
+ tenantId?: string;
82
+ }): Promise<{
83
+ allowed: boolean;
84
+ reason?: string;
85
+ remaining?: number;
86
+ limit?: number;
87
+ planId?: string | null;
88
+ planFeatures?: string[];
89
+ }>;
90
+ /** Test helper. */
91
+ __getRecords(): readonly UsageRecord[];
92
+ /** Test helper — substitui quota fixture. */
93
+ __setQuota(metric: string, quota: UsageQuota): void;
94
+ /** Test helper — limpa estado. */
95
+ __reset(): void;
96
+ }
97
+ /**
98
+ * In-memory `SupportNamespace`. createTicket gera ID `mock-ticket-{n}`;
99
+ * listMyTickets retorna todos criados (FIFO).
100
+ */
101
+ declare class MockSupport implements SupportNamespace {
102
+ private _tickets;
103
+ createTicket(input: CreateTicketInput): Promise<SupportTicket>;
104
+ listMyTickets(): Promise<SupportTicket[]>;
105
+ /** Test helper. */
106
+ __reset(): void;
107
+ }
108
+ /**
109
+ * In-memory entitlements. Por default `allowed=true` em tudo (dev convém).
110
+ * Use `__deny(slug, feature)` em tests pra simular bloqueio.
111
+ */
112
+ declare class MockEntitlements {
113
+ private _denied;
114
+ check(productSlug: string, feature: string): Promise<boolean>;
115
+ checkDetailed(productSlug: string, feature: string): Promise<EntitlementCheck>;
116
+ /** Test helper. */
117
+ __deny(productSlug: string, feature: string): void;
118
+ /** Test helper. */
119
+ __reset(): void;
120
+ }
121
+ /**
122
+ * In-memory `DbNamespace`. Cada `collection(name)` aponta para um Map; ops
123
+ * são totalmente síncronas (mas API await-friendly).
124
+ *
125
+ * Útil para testar produtos consumidores sem rodar Core REST.
126
+ */
127
+ declare class MockDb implements DbNamespace {
128
+ private _store;
129
+ private _fixtures;
130
+ constructor(initialFixtures?: Record<string, Record<string, Record<string, unknown>>>);
131
+ collection(name: string): DbCollectionRef;
132
+ /** Test helper — substitui fixture inteira de uma collection. */
133
+ __setFixture(name: string, items: Record<string, Record<string, unknown>>): void;
134
+ /** Test helper — reset total. */
135
+ __reset(): void;
136
+ }
137
+
138
+ export { DEV_FIXTURE_USER, MockAuth, MockDb, MockEntitlements, MockSupport, MockUsage };
@@ -0,0 +1,138 @@
1
+ import { j as NeetruUser, A as AuthNamespace, S as SignInOptions, b as AuthStateListener, g as DbNamespace, e as DbCollectionRef, E as EntitlementCheck, m as SupportNamespace, d as CreateTicketInput, p as SupportTicket, r as UsageNamespace, s as UsageQuota } from './types-PKUaFtBY.js';
2
+
3
+ /**
4
+ * Mocks determinísticos do SDK — overridáveis em tests do consumer ou
5
+ * ativados automaticamente quando `NEETRU_ENV=dev`.
6
+ *
7
+ * Nada aqui faz I/O. Tudo é puro/sincrono/in-memory. Compat total com
8
+ * runtimes browser/Node/Edge — sem `node:` builtins.
9
+ *
10
+ * Cada mock implementa o mesmo namespace interface das implementações reais
11
+ * (`AuthNamespace`, `UsageNamespace`, `SupportNamespace`) — caller pode
12
+ * substituir 1:1 sem branching.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { createNeetruClient, MockAuth } from '@neetru/sdk';
17
+ *
18
+ * const client = createNeetruClient({
19
+ * mocks: { auth: new MockAuth({ uid: 'fake-user', email: 'x@x.com' }) },
20
+ * });
21
+ * ```
22
+ */
23
+
24
+ /**
25
+ * User fixture determinístico — usado quando `NEETRU_ENV=dev` e nenhum mock
26
+ * custom é injetado. Email/uid estáveis pra snapshot tests.
27
+ */
28
+ declare const DEV_FIXTURE_USER: NeetruUser;
29
+ /**
30
+ * Implementação in-memory do `AuthNamespace`. signIn marca o user como
31
+ * logado; signOut limpa. onAuthStateChanged dispara callback síncrono no
32
+ * subscribe + a cada mudança.
33
+ */
34
+ declare class MockAuth implements AuthNamespace {
35
+ private _user;
36
+ private _listeners;
37
+ constructor(initialUser?: NeetruUser | null);
38
+ signIn(_options?: SignInOptions): Promise<NeetruUser>;
39
+ signOut(): Promise<void>;
40
+ getUser(): NeetruUser | null;
41
+ onAuthStateChanged(listener: AuthStateListener): () => void;
42
+ /** Helper de tests — força um user state arbitrário. */
43
+ __setUser(user: NeetruUser | null): void;
44
+ private _notify;
45
+ }
46
+ interface UsageRecord {
47
+ event: string;
48
+ properties?: Record<string, string | number | boolean | null>;
49
+ timestamp: string;
50
+ }
51
+ /**
52
+ * In-memory `UsageNamespace`. `track()` apenas acumula no array
53
+ * `__getRecords()` (útil pra asserts em test). `getQuota()` retorna fixture
54
+ * com `used = trackedCount` da metric.
55
+ */
56
+ declare class MockUsage implements UsageNamespace {
57
+ private _records;
58
+ private _quotas;
59
+ /** v0.3 — counters in-memory pra `report()` / `check()`. */
60
+ private _counters;
61
+ constructor(initialQuotas?: Record<string, UsageQuota>);
62
+ track(event: string, properties?: Record<string, string | number | boolean | null>): Promise<{
63
+ ok: true;
64
+ }>;
65
+ getQuota(metric: string): Promise<UsageQuota>;
66
+ /** v0.3 — Mock report incrementa o counter local. */
67
+ report(resource: string, qty?: number, _options?: {
68
+ productId?: string;
69
+ tenantId?: string;
70
+ }): Promise<{
71
+ ok: true;
72
+ counterId?: string;
73
+ value?: number;
74
+ limit?: number;
75
+ remaining?: number;
76
+ status?: string;
77
+ }>;
78
+ /** v0.3 — Mock check usa quotas + counters in-memory. */
79
+ check(resource: string, _options?: {
80
+ productId?: string;
81
+ tenantId?: string;
82
+ }): Promise<{
83
+ allowed: boolean;
84
+ reason?: string;
85
+ remaining?: number;
86
+ limit?: number;
87
+ planId?: string | null;
88
+ planFeatures?: string[];
89
+ }>;
90
+ /** Test helper. */
91
+ __getRecords(): readonly UsageRecord[];
92
+ /** Test helper — substitui quota fixture. */
93
+ __setQuota(metric: string, quota: UsageQuota): void;
94
+ /** Test helper — limpa estado. */
95
+ __reset(): void;
96
+ }
97
+ /**
98
+ * In-memory `SupportNamespace`. createTicket gera ID `mock-ticket-{n}`;
99
+ * listMyTickets retorna todos criados (FIFO).
100
+ */
101
+ declare class MockSupport implements SupportNamespace {
102
+ private _tickets;
103
+ createTicket(input: CreateTicketInput): Promise<SupportTicket>;
104
+ listMyTickets(): Promise<SupportTicket[]>;
105
+ /** Test helper. */
106
+ __reset(): void;
107
+ }
108
+ /**
109
+ * In-memory entitlements. Por default `allowed=true` em tudo (dev convém).
110
+ * Use `__deny(slug, feature)` em tests pra simular bloqueio.
111
+ */
112
+ declare class MockEntitlements {
113
+ private _denied;
114
+ check(productSlug: string, feature: string): Promise<boolean>;
115
+ checkDetailed(productSlug: string, feature: string): Promise<EntitlementCheck>;
116
+ /** Test helper. */
117
+ __deny(productSlug: string, feature: string): void;
118
+ /** Test helper. */
119
+ __reset(): void;
120
+ }
121
+ /**
122
+ * In-memory `DbNamespace`. Cada `collection(name)` aponta para um Map; ops
123
+ * são totalmente síncronas (mas API await-friendly).
124
+ *
125
+ * Útil para testar produtos consumidores sem rodar Core REST.
126
+ */
127
+ declare class MockDb implements DbNamespace {
128
+ private _store;
129
+ private _fixtures;
130
+ constructor(initialFixtures?: Record<string, Record<string, Record<string, unknown>>>);
131
+ collection(name: string): DbCollectionRef;
132
+ /** Test helper — substitui fixture inteira de uma collection. */
133
+ __setFixture(name: string, items: Record<string, Record<string, unknown>>): void;
134
+ /** Test helper — reset total. */
135
+ __reset(): void;
136
+ }
137
+
138
+ export { DEV_FIXTURE_USER, MockAuth, MockDb, MockEntitlements, MockSupport, MockUsage };
package/dist/mocks.mjs ADDED
@@ -0,0 +1,274 @@
1
+ // src/mocks.ts
2
+ var DEV_FIXTURE_USER = Object.freeze({
3
+ uid: "dev-fixture-uid-0001",
4
+ email: "dev@neetru.local",
5
+ emailVerified: true,
6
+ displayName: "Dev Fixture",
7
+ isCustomer: true,
8
+ isStaff: false
9
+ });
10
+ var MockAuth = class {
11
+ _user;
12
+ _listeners;
13
+ constructor(initialUser = null) {
14
+ this._user = initialUser;
15
+ this._listeners = /* @__PURE__ */ new Set();
16
+ }
17
+ async signIn(_options) {
18
+ if (!this._user) this._user = { ...DEV_FIXTURE_USER };
19
+ this._notify();
20
+ return this._user;
21
+ }
22
+ async signOut() {
23
+ this._user = null;
24
+ this._notify();
25
+ }
26
+ getUser() {
27
+ return this._user;
28
+ }
29
+ onAuthStateChanged(listener) {
30
+ this._listeners.add(listener);
31
+ try {
32
+ listener(this._user);
33
+ } catch {
34
+ }
35
+ return () => {
36
+ this._listeners.delete(listener);
37
+ };
38
+ }
39
+ /** Helper de tests — força um user state arbitrário. */
40
+ __setUser(user) {
41
+ this._user = user;
42
+ this._notify();
43
+ }
44
+ _notify() {
45
+ for (const l of this._listeners) {
46
+ try {
47
+ l(this._user);
48
+ } catch {
49
+ }
50
+ }
51
+ }
52
+ };
53
+ var MockUsage = class {
54
+ _records = [];
55
+ _quotas;
56
+ /** v0.3 — counters in-memory pra `report()` / `check()`. */
57
+ _counters = /* @__PURE__ */ new Map();
58
+ constructor(initialQuotas = {}) {
59
+ this._quotas = new Map(Object.entries(initialQuotas));
60
+ }
61
+ async track(event, properties) {
62
+ this._records.push({
63
+ event,
64
+ properties,
65
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
66
+ });
67
+ return { ok: true };
68
+ }
69
+ async getQuota(metric) {
70
+ const existing = this._quotas.get(metric);
71
+ if (existing) return existing;
72
+ return {
73
+ metric,
74
+ used: this._records.filter((r) => r.event === metric).length,
75
+ limit: -1,
76
+ // unlimited em mock por default
77
+ plan: "mock"
78
+ };
79
+ }
80
+ /** v0.3 — Mock report incrementa o counter local. */
81
+ async report(resource, qty = 1, _options) {
82
+ if (!resource) throw new Error("resource required");
83
+ const safeQty = Number.isFinite(qty) && qty > 0 ? Math.floor(qty) : 1;
84
+ const existing = this._counters.get(resource) ?? 0;
85
+ const next = existing + safeQty;
86
+ this._counters.set(resource, next);
87
+ const limitFixture = this._quotas.get(resource);
88
+ const limit = limitFixture?.limit ?? -1;
89
+ return {
90
+ ok: true,
91
+ counterId: `mock_${resource}`,
92
+ value: next,
93
+ limit,
94
+ remaining: limit < 0 ? -1 : Math.max(0, limit - next),
95
+ status: limit > 0 && next >= limit ? "read_only_overlimit" : "ok"
96
+ };
97
+ }
98
+ /** v0.3 — Mock check usa quotas + counters in-memory. */
99
+ async check(resource, _options) {
100
+ const quota = this._quotas.get(resource);
101
+ const used = this._counters.get(resource) ?? 0;
102
+ if (!quota) {
103
+ return { allowed: true, reason: "granted", limit: -1, remaining: -1 };
104
+ }
105
+ const remaining = quota.limit < 0 ? -1 : Math.max(0, quota.limit - used);
106
+ if (remaining === 0) {
107
+ return {
108
+ allowed: false,
109
+ reason: "limit_exceeded",
110
+ limit: quota.limit,
111
+ remaining: 0
112
+ };
113
+ }
114
+ return {
115
+ allowed: true,
116
+ reason: "granted",
117
+ limit: quota.limit,
118
+ remaining
119
+ };
120
+ }
121
+ /** Test helper. */
122
+ __getRecords() {
123
+ return [...this._records];
124
+ }
125
+ /** Test helper — substitui quota fixture. */
126
+ __setQuota(metric, quota) {
127
+ this._quotas.set(metric, quota);
128
+ }
129
+ /** Test helper — limpa estado. */
130
+ __reset() {
131
+ this._records = [];
132
+ this._quotas.clear();
133
+ this._counters.clear();
134
+ }
135
+ };
136
+ var mockTicketSeq = 0;
137
+ var MockSupport = class {
138
+ _tickets = [];
139
+ async createTicket(input) {
140
+ if (!input?.subject) throw new Error("subject required");
141
+ if (!input?.message) throw new Error("message required");
142
+ const ticket = {
143
+ id: `mock-ticket-${++mockTicketSeq}`,
144
+ subject: input.subject,
145
+ message: input.message,
146
+ severity: input.severity ?? "normal",
147
+ status: "open",
148
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
149
+ productSlug: input.productSlug
150
+ };
151
+ this._tickets.push(ticket);
152
+ return ticket;
153
+ }
154
+ async listMyTickets() {
155
+ return [...this._tickets];
156
+ }
157
+ /** Test helper. */
158
+ __reset() {
159
+ this._tickets = [];
160
+ }
161
+ };
162
+ var MockEntitlements = class {
163
+ _denied = /* @__PURE__ */ new Set();
164
+ async check(productSlug, feature) {
165
+ return !this._denied.has(`${productSlug}:${feature}`);
166
+ }
167
+ async checkDetailed(productSlug, feature) {
168
+ const allowed = await this.check(productSlug, feature);
169
+ return {
170
+ allowed,
171
+ productSlug,
172
+ feature,
173
+ reason: allowed ? "granted" : "mock_denied"
174
+ };
175
+ }
176
+ /** Test helper. */
177
+ __deny(productSlug, feature) {
178
+ this._denied.add(`${productSlug}:${feature}`);
179
+ }
180
+ /** Test helper. */
181
+ __reset() {
182
+ this._denied.clear();
183
+ }
184
+ };
185
+ var MockDb = class {
186
+ _store = /* @__PURE__ */ new Map();
187
+ _fixtures;
188
+ constructor(initialFixtures = {}) {
189
+ this._fixtures = new Map(Object.entries(initialFixtures));
190
+ }
191
+ collection(name) {
192
+ const _store = this._store;
193
+ const _fixtures = this._fixtures;
194
+ if (!_store.has(name)) {
195
+ const init = _fixtures.get(name);
196
+ _store.set(
197
+ name,
198
+ init ? new Map(Object.entries(init)) : /* @__PURE__ */ new Map()
199
+ );
200
+ }
201
+ const coll = _store.get(name);
202
+ const matchesFilter = (doc, f) => {
203
+ const v = doc[f.field];
204
+ switch (f.op) {
205
+ case "==":
206
+ return v === f.value;
207
+ case "!=":
208
+ return v !== f.value;
209
+ case "<":
210
+ return typeof v === "number" && typeof f.value === "number" && v < f.value;
211
+ case "<=":
212
+ return typeof v === "number" && typeof f.value === "number" && v <= f.value;
213
+ case ">":
214
+ return typeof v === "number" && typeof f.value === "number" && v > f.value;
215
+ case ">=":
216
+ return typeof v === "number" && typeof f.value === "number" && v >= f.value;
217
+ case "in":
218
+ return Array.isArray(f.value) && f.value.includes(v);
219
+ default:
220
+ return true;
221
+ }
222
+ };
223
+ let autoSeq = 0;
224
+ return {
225
+ async list(opts) {
226
+ let items = Array.from(coll.values());
227
+ if (opts?.where && opts.where.length > 0) {
228
+ items = items.filter(
229
+ (doc) => opts.where.every((f) => matchesFilter(doc, f))
230
+ );
231
+ }
232
+ if (opts?.limit !== void 0) items = items.slice(0, opts.limit);
233
+ return items;
234
+ },
235
+ async get(id) {
236
+ return coll.get(id) ?? null;
237
+ },
238
+ async add(data) {
239
+ const id = `mock-${++autoSeq}-${Math.random().toString(36).slice(2, 8)}`;
240
+ coll.set(id, { ...data, id });
241
+ return { ok: true, id };
242
+ },
243
+ async set(id, data) {
244
+ coll.set(id, { ...data, id });
245
+ return { ok: true };
246
+ },
247
+ async update(id, data) {
248
+ const cur = coll.get(id);
249
+ if (!cur) {
250
+ coll.set(id, { ...data, id });
251
+ } else {
252
+ coll.set(id, { ...cur, ...data });
253
+ }
254
+ return { ok: true };
255
+ },
256
+ async remove(id) {
257
+ coll.delete(id);
258
+ return { ok: true };
259
+ }
260
+ };
261
+ }
262
+ /** Test helper — substitui fixture inteira de uma collection. */
263
+ __setFixture(name, items) {
264
+ this._store.set(name, new Map(Object.entries(items)));
265
+ }
266
+ /** Test helper — reset total. */
267
+ __reset() {
268
+ this._store.clear();
269
+ }
270
+ };
271
+
272
+ export { DEV_FIXTURE_USER, MockAuth, MockDb, MockEntitlements, MockSupport, MockUsage };
273
+ //# sourceMappingURL=mocks.mjs.map
274
+ //# sourceMappingURL=mocks.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mocks.ts"],"names":[],"mappings":"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","file":"mocks.mjs","sourcesContent":["/**\n * Mocks determinísticos do SDK — overridáveis em tests do consumer ou\n * ativados automaticamente quando `NEETRU_ENV=dev`.\n *\n * Nada aqui faz I/O. Tudo é puro/sincrono/in-memory. Compat total com\n * runtimes browser/Node/Edge — sem `node:` builtins.\n *\n * Cada mock implementa o mesmo namespace interface das implementações reais\n * (`AuthNamespace`, `UsageNamespace`, `SupportNamespace`) — caller pode\n * substituir 1:1 sem branching.\n *\n * @example\n * ```ts\n * import { createNeetruClient, MockAuth } from '@neetru/sdk';\n *\n * const client = createNeetruClient({\n * mocks: { auth: new MockAuth({ uid: 'fake-user', email: 'x@x.com' }) },\n * });\n * ```\n */\n\nimport type {\n AuthNamespace,\n AuthStateListener,\n CreateTicketInput,\n DbCollectionRef,\n DbListOptions,\n DbNamespace,\n DbWhereFilter,\n EntitlementCheck,\n NeetruUser,\n SignInOptions,\n SupportNamespace,\n SupportTicket,\n UsageNamespace,\n UsageQuota,\n} from './types';\n\n// ─── User fixture default ──────────────────────────────────────────────────\n\n/**\n * User fixture determinístico — usado quando `NEETRU_ENV=dev` e nenhum mock\n * custom é injetado. Email/uid estáveis pra snapshot tests.\n */\nexport const DEV_FIXTURE_USER: NeetruUser = Object.freeze({\n uid: 'dev-fixture-uid-0001',\n email: 'dev@neetru.local',\n emailVerified: true,\n displayName: 'Dev Fixture',\n isCustomer: true,\n isStaff: false,\n}) as NeetruUser;\n\n// ─── MockAuth ──────────────────────────────────────────────────────────────\n\n/**\n * Implementação in-memory do `AuthNamespace`. signIn marca o user como\n * logado; signOut limpa. onAuthStateChanged dispara callback síncrono no\n * subscribe + a cada mudança.\n */\nexport class MockAuth implements AuthNamespace {\n private _user: NeetruUser | null;\n private _listeners: Set<AuthStateListener>;\n\n constructor(initialUser: NeetruUser | null = null) {\n this._user = initialUser;\n this._listeners = new Set();\n }\n\n async signIn(_options?: SignInOptions): Promise<NeetruUser> {\n // Mock: apenas marca user como logado (usa fixture se não havia).\n if (!this._user) this._user = { ...DEV_FIXTURE_USER };\n this._notify();\n return this._user;\n }\n\n async signOut(): Promise<void> {\n this._user = null;\n this._notify();\n }\n\n getUser(): NeetruUser | null {\n return this._user;\n }\n\n onAuthStateChanged(listener: AuthStateListener): () => void {\n this._listeners.add(listener);\n // Dispara imediatamente com estado atual (espelha API Firebase Auth).\n try {\n listener(this._user);\n } catch {\n // Callback do consumer não pode quebrar o subscribe.\n }\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /** Helper de tests — força um user state arbitrário. */\n __setUser(user: NeetruUser | null): void {\n this._user = user;\n this._notify();\n }\n\n private _notify(): void {\n for (const l of this._listeners) {\n try {\n l(this._user);\n } catch {\n // ignore consumer errors\n }\n }\n }\n}\n\n// ─── MockUsage ─────────────────────────────────────────────────────────────\n\ninterface UsageRecord {\n event: string;\n properties?: Record<string, string | number | boolean | null>;\n timestamp: string;\n}\n\n/**\n * In-memory `UsageNamespace`. `track()` apenas acumula no array\n * `__getRecords()` (útil pra asserts em test). `getQuota()` retorna fixture\n * com `used = trackedCount` da metric.\n */\nexport class MockUsage implements UsageNamespace {\n private _records: UsageRecord[] = [];\n private _quotas: Map<string, UsageQuota>;\n /** v0.3 — counters in-memory pra `report()` / `check()`. */\n private _counters: Map<string, number> = new Map();\n\n constructor(initialQuotas: Record<string, UsageQuota> = {}) {\n this._quotas = new Map(Object.entries(initialQuotas));\n }\n\n async track(\n event: string,\n properties?: Record<string, string | number | boolean | null>,\n ): Promise<{ ok: true }> {\n this._records.push({\n event,\n properties,\n timestamp: new Date().toISOString(),\n });\n return { ok: true };\n }\n\n async getQuota(metric: string): Promise<UsageQuota> {\n const existing = this._quotas.get(metric);\n if (existing) return existing;\n return {\n metric,\n used: this._records.filter((r) => r.event === metric).length,\n limit: -1, // unlimited em mock por default\n plan: 'mock',\n };\n }\n\n /** v0.3 — Mock report incrementa o counter local. */\n async report(\n resource: string,\n qty: number = 1,\n _options?: { productId?: string; tenantId?: string },\n ): Promise<{\n ok: true;\n counterId?: string;\n value?: number;\n limit?: number;\n remaining?: number;\n status?: string;\n }> {\n if (!resource) throw new Error('resource required');\n const safeQty = Number.isFinite(qty) && qty > 0 ? Math.floor(qty) : 1;\n const existing = this._counters.get(resource) ?? 0;\n const next = existing + safeQty;\n this._counters.set(resource, next);\n const limitFixture = this._quotas.get(resource);\n const limit = limitFixture?.limit ?? -1;\n return {\n ok: true,\n counterId: `mock_${resource}`,\n value: next,\n limit,\n remaining: limit < 0 ? -1 : Math.max(0, limit - next),\n status: limit > 0 && next >= limit ? 'read_only_overlimit' : 'ok',\n };\n }\n\n /** v0.3 — Mock check usa quotas + counters in-memory. */\n async check(\n resource: string,\n _options?: { productId?: string; tenantId?: string },\n ): Promise<{\n allowed: boolean;\n reason?: string;\n remaining?: number;\n limit?: number;\n planId?: string | null;\n planFeatures?: string[];\n }> {\n const quota = this._quotas.get(resource);\n const used = this._counters.get(resource) ?? 0;\n if (!quota) {\n return { allowed: true, reason: 'granted', limit: -1, remaining: -1 };\n }\n const remaining = quota.limit < 0 ? -1 : Math.max(0, quota.limit - used);\n if (remaining === 0) {\n return {\n allowed: false,\n reason: 'limit_exceeded',\n limit: quota.limit,\n remaining: 0,\n };\n }\n return {\n allowed: true,\n reason: 'granted',\n limit: quota.limit,\n remaining,\n };\n }\n\n /** Test helper. */\n __getRecords(): readonly UsageRecord[] {\n return [...this._records];\n }\n /** Test helper — substitui quota fixture. */\n __setQuota(metric: string, quota: UsageQuota): void {\n this._quotas.set(metric, quota);\n }\n /** Test helper — limpa estado. */\n __reset(): void {\n this._records = [];\n this._quotas.clear();\n this._counters.clear();\n }\n}\n\n// ─── MockSupport ───────────────────────────────────────────────────────────\n\nlet mockTicketSeq = 0;\n\n/**\n * In-memory `SupportNamespace`. createTicket gera ID `mock-ticket-{n}`;\n * listMyTickets retorna todos criados (FIFO).\n */\nexport class MockSupport implements SupportNamespace {\n private _tickets: SupportTicket[] = [];\n\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\n if (!input?.subject) throw new Error('subject required');\n if (!input?.message) throw new Error('message required');\n const ticket: SupportTicket = {\n id: `mock-ticket-${++mockTicketSeq}`,\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n status: 'open',\n createdAt: new Date().toISOString(),\n productSlug: input.productSlug,\n };\n this._tickets.push(ticket);\n return ticket;\n }\n\n async listMyTickets(): Promise<SupportTicket[]> {\n return [...this._tickets];\n }\n\n /** Test helper. */\n __reset(): void {\n this._tickets = [];\n }\n}\n\n// ─── MockEntitlements ──────────────────────────────────────────────────────\n\n/**\n * In-memory entitlements. Por default `allowed=true` em tudo (dev convém).\n * Use `__deny(slug, feature)` em tests pra simular bloqueio.\n */\nexport class MockEntitlements {\n private _denied: Set<string> = new Set();\n\n async check(productSlug: string, feature: string): Promise<boolean> {\n return !this._denied.has(`${productSlug}:${feature}`);\n }\n\n async checkDetailed(productSlug: string, feature: string): Promise<EntitlementCheck> {\n const allowed = await this.check(productSlug, feature);\n return {\n allowed,\n productSlug,\n feature,\n reason: allowed ? 'granted' : 'mock_denied',\n };\n }\n\n /** Test helper. */\n __deny(productSlug: string, feature: string): void {\n this._denied.add(`${productSlug}:${feature}`);\n }\n /** Test helper. */\n __reset(): void {\n this._denied.clear();\n }\n}\n\n// ─── MockDb (v0.3) ─────────────────────────────────────────────────────────\n\n/**\n * In-memory `DbNamespace`. Cada `collection(name)` aponta para um Map; ops\n * são totalmente síncronas (mas API await-friendly).\n *\n * Útil para testar produtos consumidores sem rodar Core REST.\n */\nexport class MockDb implements DbNamespace {\n private _store: Map<string, Map<string, Record<string, unknown>>> = new Map();\n private _fixtures: Map<string, Record<string, Record<string, unknown>>>;\n\n constructor(initialFixtures: Record<string, Record<string, Record<string, unknown>>> = {}) {\n this._fixtures = new Map(Object.entries(initialFixtures));\n }\n\n collection(name: string): DbCollectionRef {\n const _store = this._store;\n const _fixtures = this._fixtures;\n if (!_store.has(name)) {\n const init = _fixtures.get(name);\n _store.set(\n name,\n init ? new Map(Object.entries(init)) : new Map(),\n );\n }\n const coll = _store.get(name)!;\n\n const matchesFilter = (\n doc: Record<string, unknown>,\n f: DbWhereFilter,\n ): boolean => {\n const v = doc[f.field];\n switch (f.op) {\n case '==':\n return v === f.value;\n case '!=':\n return v !== f.value;\n case '<':\n return typeof v === 'number' && typeof f.value === 'number' && v < f.value;\n case '<=':\n return typeof v === 'number' && typeof f.value === 'number' && v <= f.value;\n case '>':\n return typeof v === 'number' && typeof f.value === 'number' && v > f.value;\n case '>=':\n return typeof v === 'number' && typeof f.value === 'number' && v >= f.value;\n case 'in':\n return Array.isArray(f.value) && (f.value as unknown[]).includes(v);\n default:\n return true;\n }\n };\n\n let autoSeq = 0;\n\n return {\n async list<T = Record<string, unknown>>(opts?: DbListOptions): Promise<T[]> {\n let items = Array.from(coll.values());\n if (opts?.where && opts.where.length > 0) {\n items = items.filter((doc) =>\n (opts.where as DbWhereFilter[]).every((f) => matchesFilter(doc, f)),\n );\n }\n if (opts?.limit !== undefined) items = items.slice(0, opts.limit);\n return items as T[];\n },\n async get<T = Record<string, unknown>>(id: string): Promise<T | null> {\n return (coll.get(id) as T | undefined) ?? null;\n },\n async add(data: Record<string, unknown>): Promise<{ ok: true; id: string }> {\n const id = `mock-${++autoSeq}-${Math.random().toString(36).slice(2, 8)}`;\n coll.set(id, { ...data, id });\n return { ok: true, id };\n },\n async set(id: string, data: Record<string, unknown>): Promise<{ ok: true }> {\n coll.set(id, { ...data, id });\n return { ok: true };\n },\n async update(id: string, data: Record<string, unknown>): Promise<{ ok: true }> {\n const cur = coll.get(id);\n if (!cur) {\n // mock-friendly: cria se não existir (mimic Firestore set merge).\n coll.set(id, { ...data, id });\n } else {\n coll.set(id, { ...cur, ...data });\n }\n return { ok: true };\n },\n async remove(id: string): Promise<{ ok: true }> {\n coll.delete(id);\n return { ok: true };\n },\n };\n }\n\n /** Test helper — substitui fixture inteira de uma collection. */\n __setFixture(name: string, items: Record<string, Record<string, unknown>>): void {\n this._store.set(name, new Map(Object.entries(items)));\n }\n\n /** Test helper — reset total. */\n __reset(): void {\n this._store.clear();\n }\n}\n"]}