@kbve/droid 0.0.5 → 0.1.1

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 (109) hide show
  1. package/README.md +3 -1
  2. package/comlink.js +1 -7
  3. package/droid.mjs +191 -61
  4. package/index.d.ts +29 -0
  5. package/index.js +9054 -2763
  6. package/lib/gateway/SupabaseGateway.d.ts +38 -0
  7. package/lib/gateway/SupabaseGateway.d.ts.map +1 -0
  8. package/lib/gateway/WorkerCommunication.d.ts +39 -0
  9. package/lib/gateway/WorkerCommunication.d.ts.map +1 -0
  10. package/lib/gateway/WorkerPool.d.ts +55 -0
  11. package/lib/gateway/WorkerPool.d.ts.map +1 -0
  12. package/lib/gateway/capabilities.d.ts +23 -0
  13. package/lib/gateway/capabilities.d.ts.map +1 -0
  14. package/lib/gateway/index.d.ts +9 -0
  15. package/lib/gateway/index.d.ts.map +1 -0
  16. package/lib/gateway/strategies/DirectStrategy.d.ts +52 -0
  17. package/lib/gateway/strategies/DirectStrategy.d.ts.map +1 -0
  18. package/lib/gateway/strategies/SharedWorkerStrategy.d.ts +46 -0
  19. package/lib/gateway/strategies/SharedWorkerStrategy.d.ts.map +1 -0
  20. package/lib/gateway/strategies/WebWorkerStrategy.d.ts +45 -0
  21. package/lib/gateway/strategies/WebWorkerStrategy.d.ts.map +1 -0
  22. package/lib/gateway/types.d.ts +81 -0
  23. package/lib/gateway/types.d.ts.map +1 -0
  24. package/lib/workers/canvas-ui-renderer.d.ts +25 -0
  25. package/lib/workers/canvas-ui-renderer.d.ts.map +1 -0
  26. package/lib/workers/canvas-worker.d.ts +14 -0
  27. package/lib/workers/canvas-worker.d.ts.map +1 -0
  28. package/lib/workers/canvas-worker.js +291 -145
  29. package/lib/workers/data.d.ts +40 -0
  30. package/lib/workers/data.d.ts.map +1 -0
  31. package/lib/workers/db-worker.d.ts +69 -0
  32. package/lib/workers/db-worker.d.ts.map +1 -0
  33. package/lib/workers/db-worker.js +2680 -2147
  34. package/lib/workers/events.d.ts +11 -0
  35. package/lib/workers/events.d.ts.map +1 -0
  36. package/lib/workers/flexbuilder.d.ts +4 -0
  37. package/lib/workers/flexbuilder.d.ts.map +1 -0
  38. package/lib/workers/init.d.ts +11 -0
  39. package/lib/workers/init.d.ts.map +1 -0
  40. package/lib/workers/main.d.ts +107 -0
  41. package/lib/workers/main.d.ts.map +1 -0
  42. package/lib/workers/supabase-db-worker.d.ts +2 -0
  43. package/lib/workers/supabase-db-worker.d.ts.map +1 -0
  44. package/lib/workers/supabase-db-worker.js +9337 -0
  45. package/lib/workers/supabase-shared-worker.d.ts +2 -0
  46. package/lib/workers/supabase-shared-worker.d.ts.map +1 -0
  47. package/lib/workers/supabase-shared-worker.js +12635 -0
  48. package/lib/workers/tools.d.ts +4 -0
  49. package/lib/workers/tools.d.ts.map +1 -0
  50. package/lib/workers/validate.d.ts +15 -0
  51. package/lib/workers/validate.d.ts.map +1 -0
  52. package/lib/workers/ws-worker.d.ts +11 -0
  53. package/lib/workers/ws-worker.d.ts.map +1 -0
  54. package/lib/workers/ws-worker.js +174 -115
  55. package/main.js +4875 -0
  56. package/package.json +17 -6
  57. package/reference.js +688 -228
  58. package/src/index.ts +38 -13
  59. package/src/lib/droid.spec.ts +14 -19
  60. package/src/lib/droid.ts +48 -4
  61. package/src/lib/gateway/SupabaseGateway.ts +183 -0
  62. package/src/lib/gateway/WorkerCommunication.ts +132 -0
  63. package/src/lib/gateway/WorkerPool.ts +304 -0
  64. package/src/lib/gateway/capabilities.ts +80 -0
  65. package/src/lib/gateway/index.ts +15 -0
  66. package/src/lib/gateway/strategies/DirectStrategy.ts +443 -0
  67. package/src/lib/gateway/strategies/SharedWorkerStrategy.ts +278 -0
  68. package/src/lib/gateway/strategies/WebWorkerStrategy.ts +278 -0
  69. package/src/lib/gateway/types.ts +112 -0
  70. package/src/lib/mod/mod-manager.ts +13 -8
  71. package/src/lib/mod/module/bento/mod-bento.worker.ts +1 -1
  72. package/src/lib/mod/module/supabase/mod-supabase.spec.ts +21 -25
  73. package/src/lib/mod/module/supabase/mod-supabase.ts +68 -0
  74. package/src/lib/mod/module/supabase/mod-supabase.worker.ts +1 -56
  75. package/src/lib/state/auth.spec.ts +58 -0
  76. package/src/lib/state/auth.ts +30 -0
  77. package/src/lib/state/index.ts +34 -0
  78. package/src/lib/state/overlay-manager.ts +125 -0
  79. package/src/lib/state/router.ts +24 -0
  80. package/src/lib/state/theme-sync.ts +75 -0
  81. package/src/lib/state/toasts.spec.ts +106 -0
  82. package/src/lib/state/toasts.ts +34 -0
  83. package/src/lib/state/ui.spec.ts +148 -0
  84. package/src/lib/state/ui.ts +45 -0
  85. package/src/lib/state/welcome-toast.ts +76 -0
  86. package/src/lib/types/bento.spec.ts +116 -0
  87. package/src/lib/types/bento.ts +94 -55
  88. package/src/lib/types/event-types.spec.ts +93 -0
  89. package/src/lib/types/event-types.ts +39 -3
  90. package/src/lib/types/modules.ts +12 -5
  91. package/src/lib/types/ui-event-types.ts +66 -0
  92. package/src/lib/workers/canvas-ui-renderer.ts +266 -0
  93. package/src/lib/workers/canvas-worker.ts +94 -9
  94. package/src/lib/workers/data.ts +59 -36
  95. package/src/lib/workers/db-worker.ts +79 -32
  96. package/src/lib/workers/events.spec.ts +208 -0
  97. package/src/lib/workers/events.ts +27 -14
  98. package/src/lib/workers/flexbuilder.ts +2 -2
  99. package/src/lib/workers/init.ts +25 -17
  100. package/src/lib/workers/main.ts +509 -272
  101. package/src/lib/workers/supabase-db-worker.ts +221 -0
  102. package/src/lib/workers/supabase-shared-worker.ts +572 -0
  103. package/src/lib/workers/validate.ts +64 -0
  104. package/src/lib/workers/ws-worker.ts +156 -25
  105. package/src/setup-vitest.ts +4 -2
  106. package/src/types.ts +30 -0
  107. package/src/vite-env.d.ts +1 -0
  108. package/workers/main.js +10 -1089
  109. package/src/types.d.ts +0 -31
@@ -0,0 +1,116 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ BentoTileSchema,
4
+ BENTO_VARIANT_CLASS_MAP,
5
+ BENTO_ANIMATION_CLASS_MAP,
6
+ BENTO_BADGE_CLASS_MAP,
7
+ } from './bento';
8
+
9
+ const validTile = {
10
+ title: 'Test Tile',
11
+ primaryColor: 'blue-500',
12
+ secondaryColor: 'red-300',
13
+ };
14
+
15
+ describe('BentoTileSchema', () => {
16
+ it('accepts a minimal valid tile', () => {
17
+ const result = BentoTileSchema.safeParse(validTile);
18
+ expect(result.success).toBe(true);
19
+ });
20
+
21
+ it('rejects empty title', () => {
22
+ const result = BentoTileSchema.safeParse({
23
+ ...validTile,
24
+ title: '',
25
+ });
26
+ expect(result.success).toBe(false);
27
+ });
28
+
29
+ it('rejects invalid primary color format', () => {
30
+ const result = BentoTileSchema.safeParse({
31
+ ...validTile,
32
+ primaryColor: 'invalid',
33
+ });
34
+ expect(result.success).toBe(false);
35
+ });
36
+
37
+ it('accepts valid ULID id', () => {
38
+ const result = BentoTileSchema.safeParse({
39
+ ...validTile,
40
+ id: '01ARZ3NDEKTSV4RRFFQ69G5FAV',
41
+ });
42
+ expect(result.success).toBe(true);
43
+ });
44
+
45
+ it('rejects invalid ULID id', () => {
46
+ const result = BentoTileSchema.safeParse({
47
+ ...validTile,
48
+ id: 'not-a-ulid',
49
+ });
50
+ expect(result.success).toBe(false);
51
+ });
52
+
53
+ it('accepts valid span format', () => {
54
+ const result = BentoTileSchema.safeParse({
55
+ ...validTile,
56
+ span: 'col-span-2',
57
+ });
58
+ expect(result.success).toBe(true);
59
+ });
60
+
61
+ it('requires target when href is provided', () => {
62
+ const result = BentoTileSchema.safeParse({
63
+ ...validTile,
64
+ href: 'https://example.com',
65
+ });
66
+ expect(result.success).toBe(false);
67
+ });
68
+
69
+ it('accepts href with target', () => {
70
+ const result = BentoTileSchema.safeParse({
71
+ ...validTile,
72
+ href: 'https://example.com',
73
+ target: '_blank',
74
+ });
75
+ expect(result.success).toBe(true);
76
+ });
77
+
78
+ it('requires badge text when badgeType is set', () => {
79
+ const result = BentoTileSchema.safeParse({
80
+ ...validTile,
81
+ badgeType: 'success',
82
+ });
83
+ expect(result.success).toBe(false);
84
+ });
85
+
86
+ it('accepts badge with badgeType', () => {
87
+ const result = BentoTileSchema.safeParse({
88
+ ...validTile,
89
+ badge: 'New',
90
+ badgeType: 'success',
91
+ });
92
+ expect(result.success).toBe(true);
93
+ });
94
+ });
95
+
96
+ describe('Bento class maps', () => {
97
+ it('BENTO_VARIANT_CLASS_MAP has all variants', () => {
98
+ expect(BENTO_VARIANT_CLASS_MAP).toHaveProperty('default');
99
+ expect(BENTO_VARIANT_CLASS_MAP).toHaveProperty('minimal');
100
+ expect(BENTO_VARIANT_CLASS_MAP).toHaveProperty('image-heavy');
101
+ });
102
+
103
+ it('BENTO_ANIMATION_CLASS_MAP has all animations', () => {
104
+ expect(BENTO_ANIMATION_CLASS_MAP).toHaveProperty('fade-in');
105
+ expect(BENTO_ANIMATION_CLASS_MAP).toHaveProperty('slide-up');
106
+ expect(BENTO_ANIMATION_CLASS_MAP).toHaveProperty('none');
107
+ expect(BENTO_ANIMATION_CLASS_MAP['none']).toBe('');
108
+ });
109
+
110
+ it('BENTO_BADGE_CLASS_MAP has all badge types', () => {
111
+ expect(BENTO_BADGE_CLASS_MAP).toHaveProperty('default');
112
+ expect(BENTO_BADGE_CLASS_MAP).toHaveProperty('success');
113
+ expect(BENTO_BADGE_CLASS_MAP).toHaveProperty('warning');
114
+ expect(BENTO_BADGE_CLASS_MAP).toHaveProperty('error');
115
+ });
116
+ });
@@ -5,7 +5,12 @@ export const BENTO_VARIANTS = ['default', 'minimal', 'image-heavy'] as const;
5
5
  export const BENTO_ANIMATIONS = ['fade-in', 'slide-up', 'none'] as const;
6
6
  export const BENTO_TARGETS = ['_self', '_blank', '_parent', '_top'] as const;
7
7
  export const BENTO_ROLES = ['button', 'link', 'region', 'article'] as const;
8
- export const BENTO_BADGE_TYPES = ['default', 'success', 'warning', 'error'] as const;
8
+ export const BENTO_BADGE_TYPES = [
9
+ 'default',
10
+ 'success',
11
+ 'warning',
12
+ 'error',
13
+ ] as const;
9
14
 
10
15
  // Regex validators
11
16
  const tailwindColorRegex = /^[a-z]+-(100|200|300|400|500|600|700|800|900)$/;
@@ -13,68 +18,102 @@ const spanRegex = /^(?:(?:[a-z]+:)?(col|row)-span-\d+\s*)+$/;
13
18
  const ulidRegex = /^01[0-9A-HJKMNP-TV-Z]{24}$/;
14
19
 
15
20
  // Schema
16
- export const BentoTileSchema = z.object({
17
- id: z.string().regex(ulidRegex, 'Invalid ULID format').optional(),
18
-
19
- title: z.string().min(1, 'Title is required'),
20
- subtitle: z.string().optional(),
21
- description: z.string().optional(),
22
- span: z.string().regex(spanRegex, 'Invalid span format (e.g., col-span-2, row-span-1)').optional(),
23
-
24
- primaryColor: z.string().regex(tailwindColorRegex, 'Invalid Tailwind color format (e.g., blue-500)'),
25
- secondaryColor: z.string().regex(tailwindColorRegex, 'Invalid Tailwind color format (e.g., blue-500)'),
26
-
27
- icon: z.string().optional(),
28
- backgroundImage: z.string().url().optional(),
29
-
30
- onclick: z.string().optional(),
31
- href: z.string().url().optional(),
32
- target: z.enum(BENTO_TARGETS).optional(),
33
-
34
- ariaLabel: z.string().optional(),
35
- role: z.enum(BENTO_ROLES).optional(),
36
-
37
- variant: z.enum(BENTO_VARIANTS).optional(),
38
- animation: z.enum(BENTO_ANIMATIONS).optional(),
39
-
40
- badge: z.string().optional(),
41
- badgeType: z.enum(BENTO_BADGE_TYPES).optional(),
42
-
43
- priority: z.number().int().min(0).optional(),
44
- tooltip: z.string().optional(),
45
- disabled: z.boolean().optional(),
46
-
47
- meta: z.record(z.any()).optional(),
48
- dataset: z.record(z.string()).optional(),
49
- className: z.string().optional(),
50
- }).refine(
51
- (data) => !data.href || (data.href && data.target),
52
- { message: 'target is required when href is provided', path: ['target'] }
53
- ).refine(
54
- (data) => !data.badgeType || (data.badge && data.badge.length > 0),
55
- { message: 'badge must be provided if badgeType is set', path: ['badge'] }
56
- );
21
+ export const BentoTileSchema = z
22
+ .object({
23
+ id: z.string().regex(ulidRegex, 'Invalid ULID format').optional(),
24
+
25
+ title: z.string().min(1, 'Title is required'),
26
+ subtitle: z.string().optional(),
27
+ description: z.string().optional(),
28
+ span: z
29
+ .string()
30
+ .regex(
31
+ spanRegex,
32
+ 'Invalid span format (e.g., col-span-2, row-span-1)',
33
+ )
34
+ .optional(),
35
+
36
+ primaryColor: z
37
+ .string()
38
+ .regex(
39
+ tailwindColorRegex,
40
+ 'Invalid Tailwind color format (e.g., blue-500)',
41
+ ),
42
+ secondaryColor: z
43
+ .string()
44
+ .regex(
45
+ tailwindColorRegex,
46
+ 'Invalid Tailwind color format (e.g., blue-500)',
47
+ ),
48
+
49
+ icon: z.string().optional(),
50
+ backgroundImage: z.string().url().optional(),
51
+
52
+ onclick: z.string().optional(),
53
+ href: z.string().url().optional(),
54
+ target: z.enum(BENTO_TARGETS).optional(),
55
+
56
+ ariaLabel: z.string().optional(),
57
+ role: z.enum(BENTO_ROLES).optional(),
58
+
59
+ variant: z.enum(BENTO_VARIANTS).optional(),
60
+ animation: z.enum(BENTO_ANIMATIONS).optional(),
61
+
62
+ badge: z.string().optional(),
63
+ badgeType: z.enum(BENTO_BADGE_TYPES).optional(),
64
+
65
+ priority: z.number().int().min(0).optional(),
66
+ tooltip: z.string().optional(),
67
+ disabled: z.boolean().optional(),
68
+
69
+ meta: z.record(z.string(), z.any()).optional(),
70
+ dataset: z.record(z.string(), z.string()).optional(),
71
+ className: z.string().optional(),
72
+ })
73
+ .refine((data) => !data.href || (data.href && data.target), {
74
+ message: 'target is required when href is provided',
75
+ path: ['target'],
76
+ })
77
+ .refine(
78
+ (data) => !data.badgeType || (data.badge && data.badge.length > 0),
79
+ {
80
+ message: 'badge must be provided if badgeType is set',
81
+ path: ['badge'],
82
+ },
83
+ );
57
84
 
58
85
  // Types
59
86
  export type BentoTile = z.infer<typeof BentoTileSchema>;
60
87
  export type BentoVariantClass = { base: string; hover?: string };
61
88
 
62
89
  // Mappings
63
- export const BENTO_VARIANT_CLASS_MAP: Record<(typeof BENTO_VARIANTS)[number], BentoVariantClass> = {
64
- default: { base: 'p-4 flex flex-col justify-between' },
65
- minimal: { base: 'p-2 bg-opacity-30 backdrop-blur-sm', hover: 'bg-opacity-50' },
66
- 'image-heavy': { base: 'p-0 overflow-hidden', hover: 'scale-105' },
90
+ export const BENTO_VARIANT_CLASS_MAP: Record<
91
+ (typeof BENTO_VARIANTS)[number],
92
+ BentoVariantClass
93
+ > = {
94
+ default: { base: 'p-4 flex flex-col justify-between' },
95
+ minimal: {
96
+ base: 'p-2 bg-opacity-30 backdrop-blur-sm',
97
+ hover: 'bg-opacity-50',
98
+ },
99
+ 'image-heavy': { base: 'p-0 overflow-hidden', hover: 'scale-105' },
67
100
  };
68
101
 
69
- export const BENTO_ANIMATION_CLASS_MAP: Record<(typeof BENTO_ANIMATIONS)[number], string> = {
70
- 'fade-in': 'animate-fade-in',
71
- 'slide-up': 'animate-slide-up',
72
- none: '',
102
+ export const BENTO_ANIMATION_CLASS_MAP: Record<
103
+ (typeof BENTO_ANIMATIONS)[number],
104
+ string
105
+ > = {
106
+ 'fade-in': 'animate-fade-in',
107
+ 'slide-up': 'animate-slide-up',
108
+ none: '',
73
109
  };
74
110
 
75
- export const BENTO_BADGE_CLASS_MAP: Record<(typeof BENTO_BADGE_TYPES)[number], string> = {
76
- default: 'bg-white/30 text-white',
77
- success: 'bg-green-500/80 text-white',
78
- warning: 'bg-yellow-500/80 text-black',
79
- error: 'bg-red-500/80 text-white',
111
+ export const BENTO_BADGE_CLASS_MAP: Record<
112
+ (typeof BENTO_BADGE_TYPES)[number],
113
+ string
114
+ > = {
115
+ default: 'bg-white/30 text-white',
116
+ success: 'bg-green-500/80 text-white',
117
+ warning: 'bg-yellow-500/80 text-black',
118
+ error: 'bg-red-500/80 text-white',
80
119
  };
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ DroidEventSchemas,
4
+ DroidReadySchema,
5
+ DroidFirstConnectSchema,
6
+ DroidDownscaleSchema,
7
+ DroidUpscaleSchema,
8
+ } from './event-types';
9
+
10
+ describe('DroidEventSchemas', () => {
11
+ it('has all expected event keys', () => {
12
+ const keys = Object.keys(DroidEventSchemas);
13
+ expect(keys).toContain('droid-first-connect');
14
+ expect(keys).toContain('droid-ready');
15
+ expect(keys).toContain('droid-mod-ready');
16
+ expect(keys).toContain('droid-downscale');
17
+ expect(keys).toContain('droid-upscale');
18
+ expect(keys).toContain('panel-open');
19
+ expect(keys).toContain('panel-close');
20
+ expect(keys).toContain('toast-added');
21
+ expect(keys).toContain('toast-removed');
22
+ expect(keys).toContain('tooltip-opened');
23
+ expect(keys).toContain('tooltip-closed');
24
+ expect(keys).toContain('modal-opened');
25
+ expect(keys).toContain('modal-closed');
26
+ });
27
+
28
+ it('has exactly 13 event types', () => {
29
+ expect(Object.keys(DroidEventSchemas)).toHaveLength(13);
30
+ });
31
+ });
32
+
33
+ describe('DroidReadySchema', () => {
34
+ it('accepts valid payload', () => {
35
+ const result = DroidReadySchema.safeParse({ timestamp: 123 });
36
+ expect(result.success).toBe(true);
37
+ });
38
+
39
+ it('rejects missing timestamp', () => {
40
+ const result = DroidReadySchema.safeParse({});
41
+ expect(result.success).toBe(false);
42
+ });
43
+
44
+ it('rejects non-number timestamp', () => {
45
+ const result = DroidReadySchema.safeParse({ timestamp: 'now' });
46
+ expect(result.success).toBe(false);
47
+ });
48
+ });
49
+
50
+ describe('DroidFirstConnectSchema', () => {
51
+ it('accepts valid payload', () => {
52
+ const result = DroidFirstConnectSchema.safeParse({
53
+ timestamp: 1,
54
+ workersFirst: { db: true, ws: false },
55
+ });
56
+ expect(result.success).toBe(true);
57
+ });
58
+
59
+ it('rejects missing workersFirst', () => {
60
+ const result = DroidFirstConnectSchema.safeParse({ timestamp: 1 });
61
+ expect(result.success).toBe(false);
62
+ });
63
+ });
64
+
65
+ describe('DroidDownscaleSchema', () => {
66
+ it('accepts valid payload', () => {
67
+ const result = DroidDownscaleSchema.safeParse({
68
+ timestamp: 1,
69
+ level: 'minimal',
70
+ });
71
+ expect(result.success).toBe(true);
72
+ });
73
+
74
+ it('rejects missing level', () => {
75
+ const result = DroidDownscaleSchema.safeParse({ timestamp: 1 });
76
+ expect(result.success).toBe(false);
77
+ });
78
+ });
79
+
80
+ describe('DroidUpscaleSchema', () => {
81
+ it('accepts valid payload', () => {
82
+ const result = DroidUpscaleSchema.safeParse({
83
+ timestamp: 1,
84
+ level: 'full',
85
+ });
86
+ expect(result.success).toBe(true);
87
+ });
88
+
89
+ it('rejects missing timestamp', () => {
90
+ const result = DroidUpscaleSchema.safeParse({ level: 'full' });
91
+ expect(result.success).toBe(false);
92
+ });
93
+ });
@@ -1,5 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { PanelIdSchema } from './panel-types';
3
+ import {
4
+ ToastPayloadSchema,
5
+ TooltipPayloadSchema,
6
+ ModalPayloadSchema,
7
+ } from './ui-event-types';
3
8
 
4
9
  export const ModMetaSchema = z.object({
5
10
  name: z.string(),
@@ -20,16 +25,47 @@ export const DroidReadySchema = z.object({
20
25
  timestamp: z.number(),
21
26
  });
22
27
 
28
+ export const DroidFirstConnectSchema = z.object({
29
+ timestamp: z.number(),
30
+ workersFirst: z.object({
31
+ db: z.boolean(),
32
+ ws: z.boolean(),
33
+ }),
34
+ });
35
+
36
+ export const DroidDownscaleSchema = z.object({
37
+ timestamp: z.number(),
38
+ level: z.string(),
39
+ });
40
+
41
+ export const DroidUpscaleSchema = z.object({
42
+ timestamp: z.number(),
43
+ level: z.string(),
44
+ });
45
+
23
46
  export const DroidEventSchemas = {
24
- 'droid-ready': DroidReadySchema,
47
+ 'droid-first-connect': DroidFirstConnectSchema,
48
+ 'droid-ready': DroidReadySchema,
25
49
  'droid-mod-ready': DroidModReadySchema,
50
+ 'droid-downscale': DroidDownscaleSchema,
51
+ 'droid-upscale': DroidUpscaleSchema,
26
52
  'panel-open': PanelEventSchema,
27
53
  'panel-close': PanelEventSchema,
54
+ 'toast-added': ToastPayloadSchema,
55
+ 'toast-removed': ToastPayloadSchema.pick({ id: true }),
56
+ 'tooltip-opened': TooltipPayloadSchema,
57
+ 'tooltip-closed': TooltipPayloadSchema.pick({ id: true }),
58
+ 'modal-opened': ModalPayloadSchema,
59
+ 'modal-closed': ModalPayloadSchema.pick({ id: true }),
28
60
  };
29
61
 
30
62
  export type DroidEventMap = {
31
- [K in keyof typeof DroidEventSchemas]: z.infer<(typeof DroidEventSchemas)[K]>;
63
+ [K in keyof typeof DroidEventSchemas]: z.infer<
64
+ (typeof DroidEventSchemas)[K]
65
+ >;
32
66
  };
33
67
 
34
68
  export type EventKey = keyof DroidEventMap;
35
- export type EventHandler<K extends EventKey> = (payload: DroidEventMap[K]) => void;
69
+ export type EventHandler<K extends EventKey> = (
70
+ payload: DroidEventMap[K],
71
+ ) => void;
@@ -1,8 +1,13 @@
1
1
  import type { Remote } from 'comlink';
2
2
 
3
+ export interface ModInitContext {
4
+ emitFromWorker?: (msg: unknown) => void;
5
+ }
6
+
3
7
  export interface BaseModAPI {
4
8
  getMeta(): Promise<ModMeta>;
5
- init?(ctx: any): void | Promise<void>;
9
+ init?(ctx: ModInitContext): void | Promise<void>;
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
11
  [key: string]: any;
7
12
  }
8
13
 
@@ -31,8 +36,10 @@ export interface ModManager {
31
36
 
32
37
  export interface SupabaseModAPI extends BaseModAPI {
33
38
  configure(url: string, key: string): Promise<void>;
34
- queryTestTable(): Promise<{ data: any; error: any }>;
35
- insertTest(payload: Record<string, any>): Promise<{ data: any; error: any }>;
39
+ queryTestTable(): Promise<{ data: unknown; error: unknown }>;
40
+ insertTest(
41
+ payload: Record<string, unknown>,
42
+ ): Promise<{ data: unknown; error: unknown }>;
36
43
  }
37
44
 
38
45
  export type VirtualNode = {
@@ -40,7 +47,7 @@ export type VirtualNode = {
40
47
  id?: string;
41
48
  key?: string;
42
49
  class?: string;
43
- attrs?: Record<string, any>;
50
+ attrs?: Record<string, string | number | boolean>;
44
51
  style?: Partial<CSSStyleDeclaration>;
45
52
  children?: (string | VirtualNode)[];
46
- };
53
+ };
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ import type { VirtualNode } from './modules';
3
+
4
+ // Recursive VirtualNode schema matching the type in modules.ts.
5
+ // Validates worker-produced descriptors at runtime.
6
+ const VirtualNodeSchema: z.ZodType<VirtualNode, VirtualNode> = z.lazy(() =>
7
+ z.object({
8
+ tag: z.string(),
9
+ id: z.string().optional(),
10
+ key: z.string().optional(),
11
+ class: z.string().optional(),
12
+ attrs: z.record(z.string(), z.any()).optional(),
13
+ style: z.record(z.string(), z.string()).optional(),
14
+ children: z.array(z.union([z.string(), VirtualNodeSchema])).optional(),
15
+ }),
16
+ );
17
+
18
+ // ── Toast ──
19
+
20
+ export const ToastSeveritySchema = z.enum([
21
+ 'success',
22
+ 'warning',
23
+ 'error',
24
+ 'info',
25
+ ]);
26
+ export type ToastSeverity = z.infer<typeof ToastSeveritySchema>;
27
+
28
+ export const ToastPayloadSchema = z.object({
29
+ id: z.string(),
30
+ message: z.string(),
31
+ severity: ToastSeveritySchema,
32
+ duration: z.number().positive().optional(),
33
+ vnode: VirtualNodeSchema.optional(),
34
+ });
35
+ export type ToastPayload = z.infer<typeof ToastPayloadSchema>;
36
+
37
+ // ── Tooltip ──
38
+
39
+ export const TooltipPositionSchema = z.enum([
40
+ 'top',
41
+ 'bottom',
42
+ 'left',
43
+ 'right',
44
+ 'auto',
45
+ ]);
46
+ export type TooltipPosition = z.infer<typeof TooltipPositionSchema>;
47
+
48
+ export const TooltipPayloadSchema = z.object({
49
+ id: z.string(),
50
+ content: z.union([z.string(), VirtualNodeSchema]).optional(),
51
+ position: TooltipPositionSchema.optional(),
52
+ anchor: z.string().optional(),
53
+ });
54
+ export type TooltipPayload = z.infer<typeof TooltipPayloadSchema>;
55
+
56
+ // ── Modal ──
57
+
58
+ export const ModalPayloadSchema = z.object({
59
+ id: z.string(),
60
+ content: VirtualNodeSchema.optional(),
61
+ title: z.string().optional(),
62
+ metadata: z.record(z.string(), z.any()).optional(),
63
+ });
64
+ export type ModalPayload = z.infer<typeof ModalPayloadSchema>;
65
+
66
+ export { VirtualNodeSchema };