@webwaka/core 1.3.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 (151) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/ai.d.ts +25 -0
  3. package/dist/ai.d.ts.map +1 -0
  4. package/dist/ai.js +53 -0
  5. package/dist/ai.js.map +1 -0
  6. package/dist/core/ai/AIEngine.d.ts +69 -0
  7. package/dist/core/ai/AIEngine.d.ts.map +1 -0
  8. package/dist/core/ai/AIEngine.js +185 -0
  9. package/dist/core/ai/AIEngine.js.map +1 -0
  10. package/dist/core/auth/index.d.ts +183 -0
  11. package/dist/core/auth/index.d.ts.map +1 -0
  12. package/dist/core/auth/index.js +369 -0
  13. package/dist/core/auth/index.js.map +1 -0
  14. package/dist/core/billing/index.d.ts +52 -0
  15. package/dist/core/billing/index.d.ts.map +1 -0
  16. package/dist/core/billing/index.js +91 -0
  17. package/dist/core/billing/index.js.map +1 -0
  18. package/dist/core/booking/index.d.ts +38 -0
  19. package/dist/core/booking/index.d.ts.map +1 -0
  20. package/dist/core/booking/index.js +60 -0
  21. package/dist/core/booking/index.js.map +1 -0
  22. package/dist/core/chat/index.d.ts +48 -0
  23. package/dist/core/chat/index.d.ts.map +1 -0
  24. package/dist/core/chat/index.js +83 -0
  25. package/dist/core/chat/index.js.map +1 -0
  26. package/dist/core/document/index.d.ts +41 -0
  27. package/dist/core/document/index.d.ts.map +1 -0
  28. package/dist/core/document/index.js +68 -0
  29. package/dist/core/document/index.js.map +1 -0
  30. package/dist/core/events/index.d.ts +64 -0
  31. package/dist/core/events/index.d.ts.map +1 -0
  32. package/dist/core/events/index.js +60 -0
  33. package/dist/core/events/index.js.map +1 -0
  34. package/dist/core/geolocation/index.d.ts +32 -0
  35. package/dist/core/geolocation/index.d.ts.map +1 -0
  36. package/dist/core/geolocation/index.js +50 -0
  37. package/dist/core/geolocation/index.js.map +1 -0
  38. package/dist/core/kyc/index.d.ts +37 -0
  39. package/dist/core/kyc/index.d.ts.map +1 -0
  40. package/dist/core/kyc/index.js +60 -0
  41. package/dist/core/kyc/index.js.map +1 -0
  42. package/dist/core/logger/index.d.ts +60 -0
  43. package/dist/core/logger/index.d.ts.map +1 -0
  44. package/dist/core/logger/index.js +91 -0
  45. package/dist/core/logger/index.js.map +1 -0
  46. package/dist/core/notifications/index.d.ts +41 -0
  47. package/dist/core/notifications/index.d.ts.map +1 -0
  48. package/dist/core/notifications/index.js +111 -0
  49. package/dist/core/notifications/index.js.map +1 -0
  50. package/dist/core/rbac/index.d.ts +43 -0
  51. package/dist/core/rbac/index.d.ts.map +1 -0
  52. package/dist/core/rbac/index.js +66 -0
  53. package/dist/core/rbac/index.js.map +1 -0
  54. package/dist/events.d.ts +23 -0
  55. package/dist/events.d.ts.map +1 -0
  56. package/dist/events.js +22 -0
  57. package/dist/events.js.map +1 -0
  58. package/dist/index.d.ts +33 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +56 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/kyc.d.ts +12 -0
  63. package/dist/kyc.d.ts.map +1 -0
  64. package/dist/kyc.js +2 -0
  65. package/dist/kyc.js.map +1 -0
  66. package/dist/nanoid.d.ts +8 -0
  67. package/dist/nanoid.d.ts.map +1 -0
  68. package/dist/nanoid.js +15 -0
  69. package/dist/nanoid.js.map +1 -0
  70. package/dist/ndpr.d.ts +13 -0
  71. package/dist/ndpr.d.ts.map +1 -0
  72. package/dist/ndpr.js +19 -0
  73. package/dist/ndpr.js.map +1 -0
  74. package/dist/optimistic-lock.d.ts +11 -0
  75. package/dist/optimistic-lock.d.ts.map +1 -0
  76. package/dist/optimistic-lock.js +24 -0
  77. package/dist/optimistic-lock.js.map +1 -0
  78. package/dist/payment.d.ts +41 -0
  79. package/dist/payment.d.ts.map +1 -0
  80. package/dist/payment.js +116 -0
  81. package/dist/payment.js.map +1 -0
  82. package/dist/pin.d.ts +6 -0
  83. package/dist/pin.d.ts.map +1 -0
  84. package/dist/pin.js +18 -0
  85. package/dist/pin.js.map +1 -0
  86. package/dist/query-helpers.d.ts +18 -0
  87. package/dist/query-helpers.d.ts.map +1 -0
  88. package/dist/query-helpers.js +22 -0
  89. package/dist/query-helpers.js.map +1 -0
  90. package/dist/rate-limit.d.ts +13 -0
  91. package/dist/rate-limit.d.ts.map +1 -0
  92. package/dist/rate-limit.js +16 -0
  93. package/dist/rate-limit.js.map +1 -0
  94. package/dist/sms.d.ts +23 -0
  95. package/dist/sms.d.ts.map +1 -0
  96. package/dist/sms.js +60 -0
  97. package/dist/sms.js.map +1 -0
  98. package/dist/tax.d.ts +25 -0
  99. package/dist/tax.d.ts.map +1 -0
  100. package/dist/tax.js +31 -0
  101. package/dist/tax.js.map +1 -0
  102. package/package.json +99 -0
  103. package/src/ai.test.ts +146 -0
  104. package/src/ai.ts +75 -0
  105. package/src/core/ai/AIEngine.test.ts +386 -0
  106. package/src/core/ai/AIEngine.ts +281 -0
  107. package/src/core/auth/index.test.ts +268 -0
  108. package/src/core/auth/index.ts +570 -0
  109. package/src/core/billing/index.test.ts +154 -0
  110. package/src/core/billing/index.ts +132 -0
  111. package/src/core/booking/index.test.ts +153 -0
  112. package/src/core/booking/index.ts +91 -0
  113. package/src/core/chat/index.test.ts +159 -0
  114. package/src/core/chat/index.ts +130 -0
  115. package/src/core/document/index.test.ts +106 -0
  116. package/src/core/document/index.ts +99 -0
  117. package/src/core/events/index.test.ts +91 -0
  118. package/src/core/events/index.ts +91 -0
  119. package/src/core/geolocation/index.test.ts +70 -0
  120. package/src/core/geolocation/index.ts +69 -0
  121. package/src/core/kyc/index.test.ts +105 -0
  122. package/src/core/kyc/index.ts +86 -0
  123. package/src/core/logger/index.test.ts +110 -0
  124. package/src/core/logger/index.ts +127 -0
  125. package/src/core/notifications/index.test.ts +85 -0
  126. package/src/core/notifications/index.ts +136 -0
  127. package/src/core/rbac/index.test.ts +81 -0
  128. package/src/core/rbac/index.ts +85 -0
  129. package/src/events.test.ts +43 -0
  130. package/src/events.ts +23 -0
  131. package/src/index.test.ts +123 -0
  132. package/src/index.ts +97 -0
  133. package/src/kyc.ts +23 -0
  134. package/src/nanoid.test.ts +43 -0
  135. package/src/nanoid.ts +16 -0
  136. package/src/ndpr.test.ts +68 -0
  137. package/src/ndpr.ts +49 -0
  138. package/src/optimistic-lock.test.ts +75 -0
  139. package/src/optimistic-lock.ts +36 -0
  140. package/src/payment.test.ts +152 -0
  141. package/src/payment.ts +163 -0
  142. package/src/pin.test.ts +57 -0
  143. package/src/pin.ts +38 -0
  144. package/src/query-helpers.test.ts +98 -0
  145. package/src/query-helpers.ts +36 -0
  146. package/src/rate-limit.test.ts +98 -0
  147. package/src/rate-limit.ts +33 -0
  148. package/src/sms.test.ts +112 -0
  149. package/src/sms.ts +85 -0
  150. package/src/tax.test.ts +85 -0
  151. package/src/tax.ts +57 -0
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "@webwaka/core",
3
+ "version": "1.3.0",
4
+ "description": "WebWaka OS v4 — Shared platform primitives: auth, RBAC, CORS, rate-limiting, event bus types, and billing utilities.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./auth": {
14
+ "import": "./dist/core/auth/index.js",
15
+ "types": "./dist/core/auth/index.d.ts"
16
+ },
17
+ "./rbac": {
18
+ "import": "./dist/core/rbac/index.js",
19
+ "types": "./dist/core/rbac/index.d.ts"
20
+ },
21
+ "./billing": {
22
+ "import": "./dist/core/billing/index.js",
23
+ "types": "./dist/core/billing/index.d.ts"
24
+ },
25
+ "./logger": {
26
+ "import": "./dist/core/logger/index.js",
27
+ "types": "./dist/core/logger/index.d.ts"
28
+ },
29
+ "./events": {
30
+ "import": "./dist/core/events/index.js",
31
+ "types": "./dist/core/events/index.d.ts"
32
+ },
33
+ "./ai": {
34
+ "import": "./dist/core/ai/AIEngine.js",
35
+ "types": "./dist/core/ai/AIEngine.d.ts"
36
+ },
37
+ "./notifications": {
38
+ "import": "./dist/core/notifications/index.js",
39
+ "types": "./dist/core/notifications/index.d.ts"
40
+ },
41
+ "./kyc": {
42
+ "import": "./dist/core/kyc/index.js",
43
+ "types": "./dist/core/kyc/index.d.ts"
44
+ },
45
+ "./geolocation": {
46
+ "import": "./dist/core/geolocation/index.js",
47
+ "types": "./dist/core/geolocation/index.d.ts"
48
+ },
49
+ "./document": {
50
+ "import": "./dist/core/document/index.js",
51
+ "types": "./dist/core/document/index.d.ts"
52
+ },
53
+ "./chat": {
54
+ "import": "./dist/core/chat/index.js",
55
+ "types": "./dist/core/chat/index.d.ts"
56
+ },
57
+ "./booking": {
58
+ "import": "./dist/core/booking/index.js",
59
+ "types": "./dist/core/booking/index.d.ts"
60
+ }
61
+ },
62
+ "peerDependencies": {
63
+ "hono": ">=4.0.0"
64
+ },
65
+ "devDependencies": {
66
+ "@cloudflare/workers-types": "^4.20260317.1",
67
+ "@vitest/coverage-v8": "^1.6.1",
68
+ "hono": "^4.12.9",
69
+ "typescript": "^5.9.3",
70
+ "vitest": "^1.6.1"
71
+ },
72
+ "files": [
73
+ "dist",
74
+ "src",
75
+ "CHANGELOG.md"
76
+ ],
77
+ "keywords": [
78
+ "webwaka",
79
+ "cloudflare-workers",
80
+ "auth",
81
+ "jwt",
82
+ "rbac",
83
+ "hono",
84
+ "nigeria",
85
+ "africa",
86
+ "saas"
87
+ ],
88
+ "license": "UNLICENSED",
89
+ "repository": {
90
+ "type": "git",
91
+ "url": "https://github.com/WebWakaDOS/webwaka-core.git"
92
+ },
93
+ "scripts": {
94
+ "build": "tsc",
95
+ "test": "vitest run",
96
+ "test:watch": "vitest",
97
+ "lint": "tsc --noEmit"
98
+ }
99
+ }
package/src/ai.test.ts ADDED
@@ -0,0 +1,146 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { OpenRouterClient, createAiClient } from './ai';
3
+
4
+ const mockFetch = vi.fn();
5
+ vi.stubGlobal('fetch', mockFetch);
6
+
7
+ beforeEach(() => {
8
+ mockFetch.mockReset();
9
+ });
10
+
11
+ const client = new OpenRouterClient('or-test-key');
12
+
13
+ describe('OpenRouterClient.complete', () => {
14
+ it('returns content on successful completion', async () => {
15
+ mockFetch.mockResolvedValueOnce({
16
+ ok: true,
17
+ json: () => Promise.resolve({
18
+ choices: [{ message: { content: 'Hello from AI' } }],
19
+ model: 'openai/gpt-4o-mini',
20
+ usage: { total_tokens: 42 },
21
+ }),
22
+ });
23
+ const result = await client.complete({
24
+ messages: [{ role: 'user', content: 'Say hello' }],
25
+ });
26
+ expect(result.content).toBe('Hello from AI');
27
+ expect(result.tokensUsed).toBe(42);
28
+ expect(result.model).toBe('openai/gpt-4o-mini');
29
+ expect(result.error).toBeUndefined();
30
+ });
31
+
32
+ it('uses custom model when provided', async () => {
33
+ mockFetch.mockResolvedValueOnce({
34
+ ok: true,
35
+ json: () => Promise.resolve({
36
+ choices: [{ message: { content: 'Custom model response' } }],
37
+ model: 'anthropic/claude-3-haiku',
38
+ usage: { total_tokens: 10 },
39
+ }),
40
+ });
41
+ const result = await client.complete({
42
+ model: 'anthropic/claude-3-haiku',
43
+ messages: [{ role: 'user', content: 'Hi' }],
44
+ });
45
+ const body = JSON.parse(mockFetch.mock.calls[0][1].body);
46
+ expect(body.model).toBe('anthropic/claude-3-haiku');
47
+ expect(result.content).toBe('Custom model response');
48
+ });
49
+
50
+ it('includes maxTokens and temperature when provided', async () => {
51
+ mockFetch.mockResolvedValueOnce({
52
+ ok: true,
53
+ json: () => Promise.resolve({
54
+ choices: [{ message: { content: 'ok' } }],
55
+ model: 'openai/gpt-4o-mini',
56
+ usage: { total_tokens: 5 },
57
+ }),
58
+ });
59
+ await client.complete({
60
+ messages: [{ role: 'user', content: 'test' }],
61
+ maxTokens: 100,
62
+ temperature: 0.7,
63
+ });
64
+ const body = JSON.parse(mockFetch.mock.calls[0][1].body);
65
+ expect(body.max_tokens).toBe(100);
66
+ expect(body.temperature).toBe(0.7);
67
+ });
68
+
69
+ it('does not include maxTokens/temperature when not provided', async () => {
70
+ mockFetch.mockResolvedValueOnce({
71
+ ok: true,
72
+ json: () => Promise.resolve({
73
+ choices: [{ message: { content: 'ok' } }],
74
+ model: 'openai/gpt-4o-mini',
75
+ usage: { total_tokens: 5 },
76
+ }),
77
+ });
78
+ await client.complete({
79
+ messages: [{ role: 'user', content: 'test' }],
80
+ });
81
+ const body = JSON.parse(mockFetch.mock.calls[0][1].body);
82
+ expect(body.max_tokens).toBeUndefined();
83
+ expect(body.temperature).toBeUndefined();
84
+ });
85
+
86
+ it('returns error when response is not ok', async () => {
87
+ mockFetch.mockResolvedValueOnce({
88
+ ok: false,
89
+ json: () => Promise.resolve({ error: { message: 'Rate limit exceeded' } }),
90
+ });
91
+ const result = await client.complete({
92
+ messages: [{ role: 'user', content: 'test' }],
93
+ });
94
+ expect(result.content).toBe('');
95
+ expect(result.tokensUsed).toBe(0);
96
+ expect(result.error).toBe('Rate limit exceeded');
97
+ });
98
+
99
+ it('returns fallback error message when error.message is missing', async () => {
100
+ mockFetch.mockResolvedValueOnce({
101
+ ok: false,
102
+ json: () => Promise.resolve({}),
103
+ });
104
+ const result = await client.complete({
105
+ messages: [{ role: 'user', content: 'test' }],
106
+ });
107
+ expect(result.error).toBe('OpenRouter request failed');
108
+ });
109
+
110
+ it('returns error when fetch throws', async () => {
111
+ mockFetch.mockRejectedValueOnce(new Error('Network failure'));
112
+ const result = await client.complete({
113
+ messages: [{ role: 'user', content: 'test' }],
114
+ });
115
+ expect(result.success).toBeUndefined();
116
+ expect(result.error).toBe('Network failure');
117
+ expect(result.content).toBe('');
118
+ });
119
+
120
+ it('returns empty content when choices is missing', async () => {
121
+ mockFetch.mockResolvedValueOnce({
122
+ ok: true,
123
+ json: () => Promise.resolve({
124
+ choices: [],
125
+ model: 'openai/gpt-4o-mini',
126
+ usage: { total_tokens: 0 },
127
+ }),
128
+ });
129
+ const result = await client.complete({
130
+ messages: [{ role: 'user', content: 'test' }],
131
+ });
132
+ expect(result.content).toBe('');
133
+ });
134
+ });
135
+
136
+ describe('createAiClient', () => {
137
+ it('returns an OpenRouterClient instance', () => {
138
+ const c = createAiClient('or-key-123');
139
+ expect(c).toBeInstanceOf(OpenRouterClient);
140
+ });
141
+
142
+ it('accepts custom default model', () => {
143
+ const c = createAiClient('or-key-123', 'anthropic/claude-3-haiku');
144
+ expect(c).toBeInstanceOf(OpenRouterClient);
145
+ });
146
+ });
package/src/ai.ts ADDED
@@ -0,0 +1,75 @@
1
+ export interface AiMessage {
2
+ role: 'system' | 'user' | 'assistant';
3
+ content: string;
4
+ }
5
+
6
+ export interface AiCompletionOptions {
7
+ model?: string;
8
+ messages: AiMessage[];
9
+ maxTokens?: number;
10
+ temperature?: number;
11
+ }
12
+
13
+ export interface AiCompletionResult {
14
+ content: string;
15
+ model: string;
16
+ tokensUsed: number;
17
+ error?: string;
18
+ }
19
+
20
+ export class OpenRouterClient {
21
+ private apiKey: string;
22
+ private defaultModel: string;
23
+ private baseUrl = 'https://openrouter.ai/api/v1';
24
+
25
+ constructor(apiKey: string, defaultModel = 'openai/gpt-4o-mini') {
26
+ this.apiKey = apiKey;
27
+ this.defaultModel = defaultModel;
28
+ }
29
+
30
+ async complete(opts: AiCompletionOptions): Promise<AiCompletionResult> {
31
+ const model = opts.model ?? this.defaultModel;
32
+
33
+ try {
34
+ const body: Record<string, unknown> = {
35
+ model,
36
+ messages: opts.messages,
37
+ };
38
+ if (opts.maxTokens !== undefined) body.max_tokens = opts.maxTokens;
39
+ if (opts.temperature !== undefined) body.temperature = opts.temperature;
40
+
41
+ const res = await fetch(`${this.baseUrl}/chat/completions`, {
42
+ method: 'POST',
43
+ headers: {
44
+ Authorization: `Bearer ${this.apiKey}`,
45
+ 'Content-Type': 'application/json',
46
+ 'HTTP-Referer': 'https://webwaka.com',
47
+ 'X-Title': 'WebWaka Commerce',
48
+ },
49
+ body: JSON.stringify(body),
50
+ });
51
+
52
+ const data = (await res.json()) as any;
53
+ if (!res.ok) {
54
+ return {
55
+ content: '',
56
+ model,
57
+ tokensUsed: 0,
58
+ error: data?.error?.message ?? 'OpenRouter request failed',
59
+ };
60
+ }
61
+
62
+ return {
63
+ content: data.choices?.[0]?.message?.content ?? '',
64
+ model: data.model ?? model,
65
+ tokensUsed: data.usage?.total_tokens ?? 0,
66
+ };
67
+ } catch (err: any) {
68
+ return { content: '', model, tokensUsed: 0, error: err?.message };
69
+ }
70
+ }
71
+ }
72
+
73
+ export function createAiClient(apiKey: string, defaultModel?: string): OpenRouterClient {
74
+ return new OpenRouterClient(apiKey, defaultModel);
75
+ }