@wordrhyme/plugin 0.1.0-alpha.5

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 (52) hide show
  1. package/dist/admin/index.d.ts +19 -0
  2. package/dist/admin/index.js +42 -0
  3. package/dist/admin/index.js.map +1 -0
  4. package/dist/artifact.d.ts +17 -0
  5. package/dist/artifact.js +96 -0
  6. package/dist/artifact.js.map +1 -0
  7. package/dist/chunk-46KUIGB6.js +230 -0
  8. package/dist/chunk-46KUIGB6.js.map +1 -0
  9. package/dist/chunk-6GDCFR67.js +218 -0
  10. package/dist/chunk-6GDCFR67.js.map +1 -0
  11. package/dist/chunk-DY44Q4CK.js +188 -0
  12. package/dist/chunk-DY44Q4CK.js.map +1 -0
  13. package/dist/chunk-MZOLSLJ7.js +65 -0
  14. package/dist/chunk-MZOLSLJ7.js.map +1 -0
  15. package/dist/chunk-QNCOGISF.js +694 -0
  16. package/dist/chunk-QNCOGISF.js.map +1 -0
  17. package/dist/chunk-UGMYO6AU.js +37 -0
  18. package/dist/chunk-UGMYO6AU.js.map +1 -0
  19. package/dist/chunk-YQDKOEUI.js +242 -0
  20. package/dist/chunk-YQDKOEUI.js.map +1 -0
  21. package/dist/chunk-ZUBFLOKU.js +153 -0
  22. package/dist/chunk-ZUBFLOKU.js.map +1 -0
  23. package/dist/client-WXWbuuvd.d.ts +100 -0
  24. package/dist/client.d.ts +3 -0
  25. package/dist/client.js +29 -0
  26. package/dist/client.js.map +1 -0
  27. package/dist/dev-utils.d.ts +68 -0
  28. package/dist/dev-utils.js +21 -0
  29. package/dist/dev-utils.js.map +1 -0
  30. package/dist/entity-extensions-B5BbZH-m.d.ts +56 -0
  31. package/dist/globalization.d.ts +80 -0
  32. package/dist/globalization.js +43 -0
  33. package/dist/globalization.js.map +1 -0
  34. package/dist/index.d.ts +316 -0
  35. package/dist/index.js +354 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/manifest-CPSCN_Ft.d.ts +1471 -0
  38. package/dist/react.d.ts +145 -0
  39. package/dist/react.js +94 -0
  40. package/dist/react.js.map +1 -0
  41. package/dist/release-CyapqJ3z.d.ts +230 -0
  42. package/dist/server.d.ts +114 -0
  43. package/dist/server.js +194 -0
  44. package/dist/server.js.map +1 -0
  45. package/dist/time.d.ts +34 -0
  46. package/dist/time.js +31 -0
  47. package/dist/time.js.map +1 -0
  48. package/dist/trpc.d.ts +43 -0
  49. package/dist/trpc.js +11 -0
  50. package/dist/trpc.js.map +1 -0
  51. package/dist/types-8P7XyoyQ.d.ts +1599 -0
  52. package/package.json +83 -0
@@ -0,0 +1,218 @@
1
+ // src/time.ts
2
+ import { TZDate } from "@date-fns/tz";
3
+ var DEFAULT_TIME_ZONE = "UTC";
4
+ var CLIENT_TIME_ZONE_HEADER = "x-time-zone";
5
+ var CLIENT_TIME_ZONE_COOKIE = "wr_time_zone";
6
+ var DATE_ONLY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
7
+ var LOCAL_DATE_TIME_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/;
8
+ function readPart(parts, type) {
9
+ const value = parts.find((part) => part.type === type)?.value;
10
+ return value ? Number.parseInt(value, 10) : Number.NaN;
11
+ }
12
+ function parseDateOnly(value) {
13
+ const match = DATE_ONLY_PATTERN.exec(value);
14
+ if (!match) return void 0;
15
+ const year = Number.parseInt(match[1], 10);
16
+ const month = Number.parseInt(match[2], 10);
17
+ const day = Number.parseInt(match[3], 10);
18
+ const check = new Date(Date.UTC(year, month - 1, day));
19
+ if (check.getUTCFullYear() !== year || check.getUTCMonth() !== month - 1 || check.getUTCDate() !== day) {
20
+ return void 0;
21
+ }
22
+ return { year, month, day };
23
+ }
24
+ function formatDateOnly(parts) {
25
+ return [
26
+ String(parts.year).padStart(4, "0"),
27
+ String(parts.month).padStart(2, "0"),
28
+ String(parts.day).padStart(2, "0")
29
+ ].join("-");
30
+ }
31
+ function datePartsInTimeZone(value, timeZone) {
32
+ if (Number.isNaN(value.getTime())) return void 0;
33
+ const parts = new Intl.DateTimeFormat("en-CA", {
34
+ timeZone,
35
+ calendar: "gregory",
36
+ numberingSystem: "latn",
37
+ year: "numeric",
38
+ month: "2-digit",
39
+ day: "2-digit",
40
+ hour: "2-digit",
41
+ minute: "2-digit",
42
+ second: "2-digit",
43
+ hourCycle: "h23"
44
+ }).formatToParts(value);
45
+ const result = {
46
+ year: readPart(parts, "year"),
47
+ month: readPart(parts, "month"),
48
+ day: readPart(parts, "day"),
49
+ hour: readPart(parts, "hour"),
50
+ minute: readPart(parts, "minute"),
51
+ second: readPart(parts, "second")
52
+ };
53
+ return Object.values(result).every(Number.isFinite) ? result : void 0;
54
+ }
55
+ function addDays(value, amount) {
56
+ const parts = parseDateOnly(value);
57
+ if (!parts) return void 0;
58
+ const date = new Date(Date.UTC(parts.year, parts.month - 1, parts.day + amount));
59
+ return formatDateOnly({
60
+ year: date.getUTCFullYear(),
61
+ month: date.getUTCMonth() + 1,
62
+ day: date.getUTCDate()
63
+ });
64
+ }
65
+ function dateOnlyFromValue(value, clientTimeZone) {
66
+ if (typeof value === "string") {
67
+ const trimmed = value.trim();
68
+ if (parseDateOnly(trimmed)) return trimmed;
69
+ const timestamp = Number(trimmed);
70
+ if (!Number.isFinite(timestamp)) return void 0;
71
+ value = timestamp;
72
+ }
73
+ if (typeof value !== "number" && !(value instanceof Date)) return void 0;
74
+ const zone = canonicalizeTimeZone(clientTimeZone);
75
+ if (!zone) return void 0;
76
+ const date = value instanceof Date ? value : new Date(value);
77
+ const parts = datePartsInTimeZone(date, zone);
78
+ return parts ? formatDateOnly(parts) : void 0;
79
+ }
80
+ function canonicalizeTimeZone(value) {
81
+ if (typeof value !== "string" || !value.trim()) return void 0;
82
+ try {
83
+ return new Intl.DateTimeFormat("en-US", { timeZone: value.trim() }).resolvedOptions().timeZone;
84
+ } catch {
85
+ return void 0;
86
+ }
87
+ }
88
+ function isValidTimeZone(value) {
89
+ return canonicalizeTimeZone(value) !== void 0;
90
+ }
91
+ function normalizeTimeZone(value, fallback = DEFAULT_TIME_ZONE) {
92
+ return canonicalizeTimeZone(value) ?? canonicalizeTimeZone(fallback) ?? DEFAULT_TIME_ZONE;
93
+ }
94
+ function getClientTimeZone() {
95
+ try {
96
+ return canonicalizeTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone);
97
+ } catch {
98
+ return void 0;
99
+ }
100
+ }
101
+ function getSupportedTimeZones() {
102
+ const intl = Intl;
103
+ const values = intl.supportedValuesOf?.("timeZone") ?? [];
104
+ return [DEFAULT_TIME_ZONE, ...values.filter((value) => value !== DEFAULT_TIME_ZONE)];
105
+ }
106
+ function toUtcDayRange(dateOnly, timeZone) {
107
+ const date = parseDateOnly(dateOnly);
108
+ const zone = canonicalizeTimeZone(timeZone);
109
+ if (!date || !zone) return void 0;
110
+ const start = new TZDate(date.year, date.month - 1, date.day, zone);
111
+ const end = new TZDate(date.year, date.month - 1, date.day + 1, zone);
112
+ if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return void 0;
113
+ return {
114
+ start: new Date(start.getTime()),
115
+ endExclusive: new Date(end.getTime())
116
+ };
117
+ }
118
+ function resolveDateFilterRange(filter, options) {
119
+ const timeZone = canonicalizeTimeZone(options.timeZone);
120
+ if (!timeZone) return void 0;
121
+ if (filter.operator === "isRelativeToToday" && typeof filter.value === "string") {
122
+ const [rawAmount, unit] = filter.value.trim().split(/\s+/, 2);
123
+ const amount = Number.parseInt(rawAmount ?? "", 10);
124
+ if (!Number.isFinite(amount) || !unit) return void 0;
125
+ const todayParts = datePartsInTimeZone(options.now ?? /* @__PURE__ */ new Date(), timeZone);
126
+ if (!todayParts) return void 0;
127
+ const today = formatDateOnly(todayParts);
128
+ const offset = unit === "weeks" ? amount * 7 : unit === "months" ? amount * 30 : amount;
129
+ const span = unit === "weeks" ? 7 : unit === "months" ? 30 : unit === "days" ? 1 : 0;
130
+ if (span === 0) return void 0;
131
+ const startDate = addDays(today, offset);
132
+ const endDate = startDate ? addDays(startDate, span - 1) : void 0;
133
+ if (!startDate || !endDate) return void 0;
134
+ const startRange = toUtcDayRange(startDate, timeZone);
135
+ const endRange = toUtcDayRange(endDate, timeZone);
136
+ if (!startRange?.start || !endRange?.endExclusive) return void 0;
137
+ return { start: startRange.start, endExclusive: endRange.endExclusive };
138
+ }
139
+ if (Array.isArray(filter.value)) {
140
+ const clientTimeZone = options.clientTimeZone ?? options.legacyClientTimeZone;
141
+ const startDate = dateOnlyFromValue(filter.value[0], clientTimeZone);
142
+ const endDate = dateOnlyFromValue(filter.value[1], clientTimeZone);
143
+ if (!startDate && !endDate) return void 0;
144
+ const startRange = startDate ? toUtcDayRange(startDate, timeZone) : void 0;
145
+ const endRange = endDate ? toUtcDayRange(endDate, timeZone) : void 0;
146
+ return {
147
+ ...startRange?.start ? { start: startRange.start } : {},
148
+ ...endRange?.endExclusive ? { endExclusive: endRange.endExclusive } : {}
149
+ };
150
+ }
151
+ const dateOnly = dateOnlyFromValue(
152
+ filter.value,
153
+ options.clientTimeZone ?? options.legacyClientTimeZone
154
+ );
155
+ return dateOnly ? toUtcDayRange(dateOnly, timeZone) : void 0;
156
+ }
157
+ function toUtcDateTime(localValue, timeZone) {
158
+ const match = LOCAL_DATE_TIME_PATTERN.exec(localValue.trim());
159
+ const zone = canonicalizeTimeZone(timeZone);
160
+ if (!match || !zone) return void 0;
161
+ const parts = {
162
+ year: Number.parseInt(match[1], 10),
163
+ month: Number.parseInt(match[2], 10),
164
+ day: Number.parseInt(match[3], 10),
165
+ hour: Number.parseInt(match[4], 10),
166
+ minute: Number.parseInt(match[5], 10),
167
+ second: Number.parseInt(match[6] ?? "0", 10),
168
+ millisecond: Number.parseInt((match[7] ?? "0").padEnd(3, "0"), 10)
169
+ };
170
+ const zoned = new TZDate(
171
+ parts.year,
172
+ parts.month - 1,
173
+ parts.day,
174
+ parts.hour,
175
+ parts.minute,
176
+ parts.second,
177
+ parts.millisecond,
178
+ zone
179
+ );
180
+ if (Number.isNaN(zoned.getTime())) return void 0;
181
+ if (zoned.getFullYear() !== parts.year || zoned.getMonth() !== parts.month - 1 || zoned.getDate() !== parts.day || zoned.getHours() !== parts.hour || zoned.getMinutes() !== parts.minute) {
182
+ return void 0;
183
+ }
184
+ return new Date(zoned.getTime());
185
+ }
186
+ function toDateTimeLocal(value, timeZone) {
187
+ const date = value instanceof Date ? value : new Date(value);
188
+ const zone = normalizeTimeZone(timeZone);
189
+ const parts = datePartsInTimeZone(date, zone);
190
+ if (!parts) return "";
191
+ return `${formatDateOnly(parts)}T${String(parts.hour).padStart(2, "0")}:${String(parts.minute).padStart(2, "0")}:${String(parts.second).padStart(2, "0")}`;
192
+ }
193
+ function formatDateTime(value, options = {}) {
194
+ const date = value instanceof Date ? value : new Date(value);
195
+ if (Number.isNaN(date.getTime())) return "";
196
+ const { locale = "en-US", timeZone, ...formatOptions } = options;
197
+ return new Intl.DateTimeFormat(locale, {
198
+ ...formatOptions,
199
+ timeZone: normalizeTimeZone(timeZone)
200
+ }).format(date);
201
+ }
202
+
203
+ export {
204
+ DEFAULT_TIME_ZONE,
205
+ CLIENT_TIME_ZONE_HEADER,
206
+ CLIENT_TIME_ZONE_COOKIE,
207
+ canonicalizeTimeZone,
208
+ isValidTimeZone,
209
+ normalizeTimeZone,
210
+ getClientTimeZone,
211
+ getSupportedTimeZones,
212
+ toUtcDayRange,
213
+ resolveDateFilterRange,
214
+ toUtcDateTime,
215
+ toDateTimeLocal,
216
+ formatDateTime
217
+ };
218
+ //# sourceMappingURL=chunk-6GDCFR67.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/time.ts"],"sourcesContent":["import { TZDate } from '@date-fns/tz';\n\nexport const DEFAULT_TIME_ZONE = 'UTC';\nexport const CLIENT_TIME_ZONE_HEADER = 'x-time-zone';\nexport const CLIENT_TIME_ZONE_COOKIE = 'wr_time_zone';\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst LOCAL_DATE_TIME_PATTERN =\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{1,3}))?)?$/;\n\nexport interface DateFilterInput {\n value: unknown;\n variant: string;\n operator: string;\n}\n\nexport interface DateFilterRange {\n start?: Date;\n endExclusive?: Date;\n}\n\nexport type DateRangeResolver = (filter: DateFilterInput) => DateFilterRange | undefined;\n\nexport interface FormatDateTimeOptions extends Intl.DateTimeFormatOptions {\n locale?: string;\n timeZone?: string;\n}\n\ntype DateParts = {\n year: number;\n month: number;\n day: number;\n};\n\ntype DateTimeParts = DateParts & {\n hour: number;\n minute: number;\n second: number;\n};\n\nfunction readPart(parts: Intl.DateTimeFormatPart[], type: Intl.DateTimeFormatPartTypes): number {\n const value = parts.find((part) => part.type === type)?.value;\n return value ? Number.parseInt(value, 10) : Number.NaN;\n}\n\nfunction parseDateOnly(value: string): DateParts | undefined {\n const match = DATE_ONLY_PATTERN.exec(value);\n if (!match) return undefined;\n\n const year = Number.parseInt(match[1]!, 10);\n const month = Number.parseInt(match[2]!, 10);\n const day = Number.parseInt(match[3]!, 10);\n const check = new Date(Date.UTC(year, month - 1, day));\n\n if (\n check.getUTCFullYear() !== year\n || check.getUTCMonth() !== month - 1\n || check.getUTCDate() !== day\n ) {\n return undefined;\n }\n\n return { year, month, day };\n}\n\nfunction formatDateOnly(parts: DateParts): string {\n return [\n String(parts.year).padStart(4, '0'),\n String(parts.month).padStart(2, '0'),\n String(parts.day).padStart(2, '0'),\n ].join('-');\n}\n\nfunction datePartsInTimeZone(value: Date, timeZone: string): DateTimeParts | undefined {\n if (Number.isNaN(value.getTime())) return undefined;\n\n const parts = new Intl.DateTimeFormat('en-CA', {\n timeZone,\n calendar: 'gregory',\n numberingSystem: 'latn',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hourCycle: 'h23',\n }).formatToParts(value);\n\n const result = {\n year: readPart(parts, 'year'),\n month: readPart(parts, 'month'),\n day: readPart(parts, 'day'),\n hour: readPart(parts, 'hour'),\n minute: readPart(parts, 'minute'),\n second: readPart(parts, 'second'),\n };\n\n return Object.values(result).every(Number.isFinite) ? result : undefined;\n}\n\nfunction addDays(value: string, amount: number): string | undefined {\n const parts = parseDateOnly(value);\n if (!parts) return undefined;\n\n const date = new Date(Date.UTC(parts.year, parts.month - 1, parts.day + amount));\n return formatDateOnly({\n year: date.getUTCFullYear(),\n month: date.getUTCMonth() + 1,\n day: date.getUTCDate(),\n });\n}\n\nfunction dateOnlyFromValue(value: unknown, clientTimeZone?: string): string | undefined {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (parseDateOnly(trimmed)) return trimmed;\n\n const timestamp = Number(trimmed);\n if (!Number.isFinite(timestamp)) return undefined;\n value = timestamp;\n }\n\n if (typeof value !== 'number' && !(value instanceof Date)) return undefined;\n\n const zone = canonicalizeTimeZone(clientTimeZone);\n if (!zone) return undefined;\n\n const date = value instanceof Date ? value : new Date(value);\n const parts = datePartsInTimeZone(date, zone);\n return parts ? formatDateOnly(parts) : undefined;\n}\n\nexport function canonicalizeTimeZone(value: unknown): string | undefined {\n if (typeof value !== 'string' || !value.trim()) return undefined;\n\n try {\n return new Intl.DateTimeFormat('en-US', { timeZone: value.trim() })\n .resolvedOptions()\n .timeZone;\n } catch {\n return undefined;\n }\n}\n\nexport function isValidTimeZone(value: unknown): value is string {\n return canonicalizeTimeZone(value) !== undefined;\n}\n\nexport function normalizeTimeZone(value: unknown, fallback = DEFAULT_TIME_ZONE): string {\n return canonicalizeTimeZone(value) ?? canonicalizeTimeZone(fallback) ?? DEFAULT_TIME_ZONE;\n}\n\nexport function getClientTimeZone(): string | undefined {\n try {\n return canonicalizeTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone);\n } catch {\n return undefined;\n }\n}\n\nexport function getSupportedTimeZones(): string[] {\n const intl = Intl as typeof Intl & {\n supportedValuesOf?: (key: 'timeZone') => string[];\n };\n const values = intl.supportedValuesOf?.('timeZone') ?? [];\n return [DEFAULT_TIME_ZONE, ...values.filter((value) => value !== DEFAULT_TIME_ZONE)];\n}\n\nexport function toUtcDayRange(dateOnly: string, timeZone: string): DateFilterRange | undefined {\n const date = parseDateOnly(dateOnly);\n const zone = canonicalizeTimeZone(timeZone);\n if (!date || !zone) return undefined;\n\n const start = new TZDate(date.year, date.month - 1, date.day, zone);\n const end = new TZDate(date.year, date.month - 1, date.day + 1, zone);\n if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return undefined;\n\n return {\n start: new Date(start.getTime()),\n endExclusive: new Date(end.getTime()),\n };\n}\n\nexport function resolveDateFilterRange(\n filter: DateFilterInput,\n options: {\n timeZone: string;\n clientTimeZone?: string;\n legacyClientTimeZone?: string;\n now?: Date;\n },\n): DateFilterRange | undefined {\n const timeZone = canonicalizeTimeZone(options.timeZone);\n if (!timeZone) return undefined;\n\n if (filter.operator === 'isRelativeToToday' && typeof filter.value === 'string') {\n const [rawAmount, unit] = filter.value.trim().split(/\\s+/, 2);\n const amount = Number.parseInt(rawAmount ?? '', 10);\n if (!Number.isFinite(amount) || !unit) return undefined;\n\n const todayParts = datePartsInTimeZone(options.now ?? new Date(), timeZone);\n if (!todayParts) return undefined;\n\n const today = formatDateOnly(todayParts);\n const offset = unit === 'weeks' ? amount * 7 : unit === 'months' ? amount * 30 : amount;\n const span = unit === 'weeks' ? 7 : unit === 'months' ? 30 : unit === 'days' ? 1 : 0;\n if (span === 0) return undefined;\n\n const startDate = addDays(today, offset);\n const endDate = startDate ? addDays(startDate, span - 1) : undefined;\n if (!startDate || !endDate) return undefined;\n\n const startRange = toUtcDayRange(startDate, timeZone);\n const endRange = toUtcDayRange(endDate, timeZone);\n if (!startRange?.start || !endRange?.endExclusive) return undefined;\n\n return { start: startRange.start, endExclusive: endRange.endExclusive };\n }\n\n if (Array.isArray(filter.value)) {\n const clientTimeZone = options.clientTimeZone ?? options.legacyClientTimeZone;\n const startDate = dateOnlyFromValue(filter.value[0], clientTimeZone);\n const endDate = dateOnlyFromValue(filter.value[1], clientTimeZone);\n if (!startDate && !endDate) return undefined;\n\n const startRange = startDate ? toUtcDayRange(startDate, timeZone) : undefined;\n const endRange = endDate ? toUtcDayRange(endDate, timeZone) : undefined;\n return {\n ...(startRange?.start ? { start: startRange.start } : {}),\n ...(endRange?.endExclusive ? { endExclusive: endRange.endExclusive } : {}),\n };\n }\n\n const dateOnly = dateOnlyFromValue(\n filter.value,\n options.clientTimeZone ?? options.legacyClientTimeZone,\n );\n return dateOnly ? toUtcDayRange(dateOnly, timeZone) : undefined;\n}\n\nexport function toUtcDateTime(localValue: string, timeZone: string): Date | undefined {\n const match = LOCAL_DATE_TIME_PATTERN.exec(localValue.trim());\n const zone = canonicalizeTimeZone(timeZone);\n if (!match || !zone) return undefined;\n\n const parts = {\n year: Number.parseInt(match[1]!, 10),\n month: Number.parseInt(match[2]!, 10),\n day: Number.parseInt(match[3]!, 10),\n hour: Number.parseInt(match[4]!, 10),\n minute: Number.parseInt(match[5]!, 10),\n second: Number.parseInt(match[6] ?? '0', 10),\n millisecond: Number.parseInt((match[7] ?? '0').padEnd(3, '0'), 10),\n };\n\n const zoned = new TZDate(\n parts.year,\n parts.month - 1,\n parts.day,\n parts.hour,\n parts.minute,\n parts.second,\n parts.millisecond,\n zone,\n );\n if (Number.isNaN(zoned.getTime())) return undefined;\n\n if (\n zoned.getFullYear() !== parts.year\n || zoned.getMonth() !== parts.month - 1\n || zoned.getDate() !== parts.day\n || zoned.getHours() !== parts.hour\n || zoned.getMinutes() !== parts.minute\n ) {\n return undefined;\n }\n\n return new Date(zoned.getTime());\n}\n\nexport function toDateTimeLocal(value: Date | string | number, timeZone: string): string {\n const date = value instanceof Date ? value : new Date(value);\n const zone = normalizeTimeZone(timeZone);\n const parts = datePartsInTimeZone(date, zone);\n if (!parts) return '';\n\n return `${formatDateOnly(parts)}T${String(parts.hour).padStart(2, '0')}:${String(parts.minute).padStart(2, '0')}:${String(parts.second).padStart(2, '0')}`;\n}\n\nexport function formatDateTime(\n value: Date | string | number,\n options: FormatDateTimeOptions = {},\n): string {\n const date = value instanceof Date ? value : new Date(value);\n if (Number.isNaN(date.getTime())) return '';\n\n const { locale = 'en-US', timeZone, ...formatOptions } = options;\n return new Intl.DateTimeFormat(locale, {\n ...formatOptions,\n timeZone: normalizeTimeZone(timeZone),\n }).format(date);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,188 @@
1
+ // src/release.ts
2
+ import { z } from "zod";
3
+ var PLUGIN_ARCHIVE_MEDIA_TYPE = "application/vnd.wordrhyme.plugin+zip";
4
+ var PLUGIN_RELEASE_SCHEMA_VERSION = 1;
5
+ var REGISTRY_RELEASE_SCHEMA_VERSION = 1;
6
+ var REGISTRY_RELEASE_SIGNATURE_DOMAIN = "wordrhyme.registry-release.v1\0";
7
+ var REGISTRY_CATALOG_SIGNATURE_DOMAIN = "wordrhyme.registry-catalog-page.v1\0";
8
+ var pluginIdSchema = z.string().min(3).max(128).regex(/^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+$/, "pluginId must be in reverse domain notation");
9
+ var versionSchema = z.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/, "version must be valid semver");
10
+ var sha256Schema = z.string().regex(/^[a-f0-9]{64}$/, "sha256 must be lowercase hex");
11
+ var pluginArchivePathSchema = z.string().min(1).max(512).refine((value) => !value.startsWith("/") && !value.includes("\\"), {
12
+ message: "archive paths must be relative POSIX paths"
13
+ }).refine((value) => value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== ".."), {
14
+ message: "archive paths must not contain empty, dot, or parent segments"
15
+ });
16
+ var pluginReleaseFileSchema = z.object({
17
+ path: pluginArchivePathSchema,
18
+ size: z.number().int().nonnegative(),
19
+ sha256: sha256Schema
20
+ });
21
+ var pluginReleaseSchema = z.object({
22
+ schemaVersion: z.literal(PLUGIN_RELEASE_SCHEMA_VERSION),
23
+ pluginId: pluginIdSchema,
24
+ version: versionSchema,
25
+ createdAt: z.iso.datetime({ offset: true }),
26
+ mediaType: z.literal(PLUGIN_ARCHIVE_MEDIA_TYPE),
27
+ files: z.array(pluginReleaseFileSchema).min(1),
28
+ sbom: z.object({
29
+ path: pluginArchivePathSchema,
30
+ format: z.literal("spdx-json")
31
+ })
32
+ }).superRefine((release, ctx) => {
33
+ const paths = /* @__PURE__ */ new Set();
34
+ for (const [index, file] of release.files.entries()) {
35
+ if (paths.has(file.path)) {
36
+ ctx.addIssue({
37
+ code: "custom",
38
+ path: ["files", index, "path"],
39
+ message: `duplicate release file: ${file.path}`
40
+ });
41
+ }
42
+ paths.add(file.path);
43
+ }
44
+ if (!paths.has("manifest.json")) {
45
+ ctx.addIssue({
46
+ code: "custom",
47
+ path: ["files"],
48
+ message: "release must include manifest.json"
49
+ });
50
+ }
51
+ if (!paths.has(release.sbom.path)) {
52
+ ctx.addIssue({
53
+ code: "custom",
54
+ path: ["sbom", "path"],
55
+ message: "SBOM path must reference a release file"
56
+ });
57
+ }
58
+ });
59
+ var pluginSignatureSchema = z.object({
60
+ schemaVersion: z.literal(PLUGIN_RELEASE_SCHEMA_VERSION),
61
+ pluginId: pluginIdSchema,
62
+ version: versionSchema,
63
+ algorithm: z.literal("ed25519"),
64
+ keyId: z.string().min(1).max(160),
65
+ signedAt: z.iso.datetime({ offset: true }),
66
+ releaseSha256: sha256Schema,
67
+ signature: z.string().min(1)
68
+ });
69
+ var registryReleaseEnvelopeSchema = z.object({
70
+ schemaVersion: z.literal(REGISTRY_RELEASE_SCHEMA_VERSION),
71
+ pluginId: pluginIdSchema,
72
+ version: versionSchema,
73
+ publisherId: z.string().min(1).max(160),
74
+ state: z.literal("published"),
75
+ trustLevel: z.enum(["first_party", "third_party"]),
76
+ artifactUrl: z.url(),
77
+ artifactSha256: sha256Schema,
78
+ releaseSha256: sha256Schema,
79
+ publishedAt: z.iso.datetime({ offset: true })
80
+ });
81
+ var registrySignatureSchema = z.object({
82
+ schemaVersion: z.literal(REGISTRY_RELEASE_SCHEMA_VERSION),
83
+ algorithm: z.literal("ed25519"),
84
+ keyId: z.string().min(1).max(160),
85
+ signedAt: z.iso.datetime({ offset: true }),
86
+ envelopeSha256: sha256Schema,
87
+ signature: z.string().min(1)
88
+ });
89
+ var registryCatalogPageEnvelopeSchema = z.object({
90
+ schemaVersion: z.literal(REGISTRY_RELEASE_SCHEMA_VERSION),
91
+ snapshotId: z.string().min(1).max(160),
92
+ generatedAt: z.iso.datetime({ offset: true }),
93
+ cursor: z.string().nullable(),
94
+ nextCursor: z.string().nullable(),
95
+ releasesSha256: sha256Schema
96
+ });
97
+ function canonicalRegistryReleaseEnvelope(input) {
98
+ const envelope = registryReleaseEnvelopeSchema.parse(input);
99
+ return JSON.stringify({
100
+ schemaVersion: envelope.schemaVersion,
101
+ pluginId: envelope.pluginId,
102
+ version: envelope.version,
103
+ publisherId: envelope.publisherId,
104
+ state: envelope.state,
105
+ trustLevel: envelope.trustLevel,
106
+ artifactUrl: envelope.artifactUrl,
107
+ artifactSha256: envelope.artifactSha256,
108
+ releaseSha256: envelope.releaseSha256,
109
+ publishedAt: envelope.publishedAt
110
+ });
111
+ }
112
+ function registryReleaseSignaturePayload(input) {
113
+ return new TextEncoder().encode(
114
+ `${REGISTRY_RELEASE_SIGNATURE_DOMAIN}${canonicalRegistryReleaseEnvelope(input)}`
115
+ );
116
+ }
117
+ function canonicalRegistryCatalogPageEnvelope(input) {
118
+ const envelope = registryCatalogPageEnvelopeSchema.parse(input);
119
+ return JSON.stringify({
120
+ schemaVersion: envelope.schemaVersion,
121
+ snapshotId: envelope.snapshotId,
122
+ generatedAt: envelope.generatedAt,
123
+ cursor: envelope.cursor,
124
+ nextCursor: envelope.nextCursor,
125
+ releasesSha256: envelope.releasesSha256
126
+ });
127
+ }
128
+ function registryCatalogPageSignaturePayload(input) {
129
+ return new TextEncoder().encode(
130
+ `${REGISTRY_CATALOG_SIGNATURE_DOMAIN}${canonicalRegistryCatalogPageEnvelope(input)}`
131
+ );
132
+ }
133
+ var pluginRegistryReleaseSchema = z.object({
134
+ pluginId: pluginIdSchema,
135
+ version: versionSchema,
136
+ publisherId: z.string().min(1).max(160),
137
+ state: z.enum(["published", "archived", "suspended", "removed"]),
138
+ trustLevel: z.enum(["first_party", "third_party"]),
139
+ manifest: z.record(z.string(), z.unknown()),
140
+ release: pluginReleaseSchema,
141
+ artifactUrl: z.url(),
142
+ artifactSha256: sha256Schema,
143
+ releaseSha256: sha256Schema,
144
+ publisherSignature: pluginSignatureSchema.nullable(),
145
+ /** @deprecated Compatibility alias for publisherSignature. */
146
+ signature: pluginSignatureSchema.nullable().optional(),
147
+ registrySignature: registrySignatureSchema,
148
+ publishedAt: z.iso.datetime({ offset: true })
149
+ });
150
+ var pluginRegistryCatalogSchema = z.object({
151
+ schemaVersion: z.literal(PLUGIN_RELEASE_SCHEMA_VERSION),
152
+ snapshotId: z.string().min(1).max(160),
153
+ generatedAt: z.iso.datetime({ offset: true }),
154
+ releases: z.array(pluginRegistryReleaseSchema),
155
+ nextCursor: z.string().nullable(),
156
+ catalogSignature: registrySignatureSchema
157
+ });
158
+ function canonicalJson(value) {
159
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
160
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
161
+ const record = value;
162
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
163
+ }
164
+ function registryCatalogReleasesPayload(input) {
165
+ const releases = z.array(pluginRegistryReleaseSchema).parse(input);
166
+ return new TextEncoder().encode(canonicalJson(releases));
167
+ }
168
+
169
+ export {
170
+ PLUGIN_ARCHIVE_MEDIA_TYPE,
171
+ PLUGIN_RELEASE_SCHEMA_VERSION,
172
+ REGISTRY_CATALOG_SIGNATURE_DOMAIN,
173
+ pluginArchivePathSchema,
174
+ pluginReleaseFileSchema,
175
+ pluginReleaseSchema,
176
+ pluginSignatureSchema,
177
+ registryReleaseEnvelopeSchema,
178
+ registrySignatureSchema,
179
+ registryCatalogPageEnvelopeSchema,
180
+ canonicalRegistryReleaseEnvelope,
181
+ registryReleaseSignaturePayload,
182
+ canonicalRegistryCatalogPageEnvelope,
183
+ registryCatalogPageSignaturePayload,
184
+ pluginRegistryReleaseSchema,
185
+ pluginRegistryCatalogSchema,
186
+ registryCatalogReleasesPayload
187
+ };
188
+ //# sourceMappingURL=chunk-DY44Q4CK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/release.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const PLUGIN_ARCHIVE_MEDIA_TYPE = \"application/vnd.wordrhyme.plugin+zip\";\nexport const PLUGIN_RELEASE_SCHEMA_VERSION = 1 as const;\nexport const REGISTRY_RELEASE_SCHEMA_VERSION = 1 as const;\nexport const REGISTRY_RELEASE_SIGNATURE_DOMAIN = \"wordrhyme.registry-release.v1\\0\";\nexport const REGISTRY_CATALOG_SIGNATURE_DOMAIN = \"wordrhyme.registry-catalog-page.v1\\0\";\n\nconst pluginIdSchema = z\n .string()\n .min(3)\n .max(128)\n .regex(/^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)+$/, \"pluginId must be in reverse domain notation\");\n\nconst versionSchema = z\n .string()\n .regex(/^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/, \"version must be valid semver\");\n\nconst sha256Schema = z.string().regex(/^[a-f0-9]{64}$/, \"sha256 must be lowercase hex\");\n\nexport const pluginArchivePathSchema = z\n .string()\n .min(1)\n .max(512)\n .refine((value) => !value.startsWith(\"/\") && !value.includes(\"\\\\\"), {\n message: \"archive paths must be relative POSIX paths\",\n })\n .refine((value) => value.split(\"/\").every((segment) => segment !== \"\" && segment !== \".\" && segment !== \"..\"), {\n message: \"archive paths must not contain empty, dot, or parent segments\",\n });\n\nexport const pluginReleaseFileSchema = z.object({\n path: pluginArchivePathSchema,\n size: z.number().int().nonnegative(),\n sha256: sha256Schema,\n});\n\nexport const pluginReleaseSchema = z\n .object({\n schemaVersion: z.literal(PLUGIN_RELEASE_SCHEMA_VERSION),\n pluginId: pluginIdSchema,\n version: versionSchema,\n createdAt: z.iso.datetime({ offset: true }),\n mediaType: z.literal(PLUGIN_ARCHIVE_MEDIA_TYPE),\n files: z.array(pluginReleaseFileSchema).min(1),\n sbom: z.object({\n path: pluginArchivePathSchema,\n format: z.literal(\"spdx-json\"),\n }),\n })\n .superRefine((release, ctx) => {\n const paths = new Set<string>();\n for (const [index, file] of release.files.entries()) {\n if (paths.has(file.path)) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"files\", index, \"path\"],\n message: `duplicate release file: ${file.path}`,\n });\n }\n paths.add(file.path);\n }\n\n if (!paths.has(\"manifest.json\")) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"files\"],\n message: \"release must include manifest.json\",\n });\n }\n\n if (!paths.has(release.sbom.path)) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"sbom\", \"path\"],\n message: \"SBOM path must reference a release file\",\n });\n }\n });\n\nexport const pluginSignatureSchema = z.object({\n schemaVersion: z.literal(PLUGIN_RELEASE_SCHEMA_VERSION),\n pluginId: pluginIdSchema,\n version: versionSchema,\n algorithm: z.literal(\"ed25519\"),\n keyId: z.string().min(1).max(160),\n signedAt: z.iso.datetime({ offset: true }),\n releaseSha256: sha256Schema,\n signature: z.string().min(1),\n});\n\nexport const registryReleaseEnvelopeSchema = z.object({\n schemaVersion: z.literal(REGISTRY_RELEASE_SCHEMA_VERSION),\n pluginId: pluginIdSchema,\n version: versionSchema,\n publisherId: z.string().min(1).max(160),\n state: z.literal(\"published\"),\n trustLevel: z.enum([\"first_party\", \"third_party\"]),\n artifactUrl: z.url(),\n artifactSha256: sha256Schema,\n releaseSha256: sha256Schema,\n publishedAt: z.iso.datetime({ offset: true }),\n});\n\nexport const registrySignatureSchema = z.object({\n schemaVersion: z.literal(REGISTRY_RELEASE_SCHEMA_VERSION),\n algorithm: z.literal(\"ed25519\"),\n keyId: z.string().min(1).max(160),\n signedAt: z.iso.datetime({ offset: true }),\n envelopeSha256: sha256Schema,\n signature: z.string().min(1),\n});\n\nexport const registryCatalogPageEnvelopeSchema = z.object({\n schemaVersion: z.literal(REGISTRY_RELEASE_SCHEMA_VERSION),\n snapshotId: z.string().min(1).max(160),\n generatedAt: z.iso.datetime({ offset: true }),\n cursor: z.string().nullable(),\n nextCursor: z.string().nullable(),\n releasesSha256: sha256Schema,\n});\n\n/**\n * The Registry signature contract intentionally uses an explicit field order.\n * Do not replace this with JSON.stringify(input): callers may construct the\n * same envelope with a different property insertion order.\n */\nexport function canonicalRegistryReleaseEnvelope(input: RegistryReleaseEnvelope): string {\n const envelope = registryReleaseEnvelopeSchema.parse(input);\n return JSON.stringify({\n schemaVersion: envelope.schemaVersion,\n pluginId: envelope.pluginId,\n version: envelope.version,\n publisherId: envelope.publisherId,\n state: envelope.state,\n trustLevel: envelope.trustLevel,\n artifactUrl: envelope.artifactUrl,\n artifactSha256: envelope.artifactSha256,\n releaseSha256: envelope.releaseSha256,\n publishedAt: envelope.publishedAt,\n });\n}\n\nexport function registryReleaseSignaturePayload(input: RegistryReleaseEnvelope): Uint8Array {\n return new TextEncoder().encode(\n `${REGISTRY_RELEASE_SIGNATURE_DOMAIN}${canonicalRegistryReleaseEnvelope(input)}`,\n );\n}\n\nexport function canonicalRegistryCatalogPageEnvelope(input: RegistryCatalogPageEnvelope): string {\n const envelope = registryCatalogPageEnvelopeSchema.parse(input);\n return JSON.stringify({\n schemaVersion: envelope.schemaVersion,\n snapshotId: envelope.snapshotId,\n generatedAt: envelope.generatedAt,\n cursor: envelope.cursor,\n nextCursor: envelope.nextCursor,\n releasesSha256: envelope.releasesSha256,\n });\n}\n\nexport function registryCatalogPageSignaturePayload(input: RegistryCatalogPageEnvelope): Uint8Array {\n return new TextEncoder().encode(\n `${REGISTRY_CATALOG_SIGNATURE_DOMAIN}${canonicalRegistryCatalogPageEnvelope(input)}`,\n );\n}\n\nexport const pluginRegistryReleaseSchema = z.object({\n pluginId: pluginIdSchema,\n version: versionSchema,\n publisherId: z.string().min(1).max(160),\n state: z.enum([\"published\", \"archived\", \"suspended\", \"removed\"]),\n trustLevel: z.enum([\"first_party\", \"third_party\"]),\n manifest: z.record(z.string(), z.unknown()),\n release: pluginReleaseSchema,\n artifactUrl: z.url(),\n artifactSha256: sha256Schema,\n releaseSha256: sha256Schema,\n publisherSignature: pluginSignatureSchema.nullable(),\n /** @deprecated Compatibility alias for publisherSignature. */\n signature: pluginSignatureSchema.nullable().optional(),\n registrySignature: registrySignatureSchema,\n publishedAt: z.iso.datetime({ offset: true }),\n});\n\nexport const pluginRegistryCatalogSchema = z.object({\n schemaVersion: z.literal(PLUGIN_RELEASE_SCHEMA_VERSION),\n snapshotId: z.string().min(1).max(160),\n generatedAt: z.iso.datetime({ offset: true }),\n releases: z.array(pluginRegistryReleaseSchema),\n nextCursor: z.string().nullable(),\n catalogSignature: registrySignatureSchema,\n});\n\nfunction canonicalJson(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(\",\")}]`;\n const record = value as Record<string, unknown>;\n return `{${Object.keys(record)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)\n .join(\",\")}}`;\n}\n\nexport function registryCatalogReleasesPayload(input: PluginRegistryRelease[]): Uint8Array {\n const releases = z.array(pluginRegistryReleaseSchema).parse(input);\n return new TextEncoder().encode(canonicalJson(releases));\n}\n\nexport type PluginReleaseFile = z.infer<typeof pluginReleaseFileSchema>;\nexport type PluginRelease = z.infer<typeof pluginReleaseSchema>;\nexport type PluginSignature = z.infer<typeof pluginSignatureSchema>;\nexport type RegistryReleaseEnvelope = z.infer<typeof registryReleaseEnvelopeSchema>;\nexport type RegistrySignature = z.infer<typeof registrySignatureSchema>;\nexport type RegistryCatalogPageEnvelope = z.infer<typeof registryCatalogPageEnvelopeSchema>;\nexport type PluginRegistryRelease = z.infer<typeof pluginRegistryReleaseSchema>;\nexport type PluginRegistryCatalog = z.infer<typeof pluginRegistryCatalogSchema>;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,65 @@
1
+ // src/extension-helpers.ts
2
+ function stripUndefined(obj) {
3
+ const result = {};
4
+ for (const [k, v] of Object.entries(obj)) {
5
+ if (v !== void 0) result[k] = v;
6
+ }
7
+ return result;
8
+ }
9
+ function defineExtension(ext) {
10
+ return stripUndefined({
11
+ ...ext,
12
+ targets: ext.targets.map((target) => stripUndefined(target))
13
+ });
14
+ }
15
+ function navExtension(ext) {
16
+ const { path, order, requiredPermission, hidden, ...rest } = ext;
17
+ const target = stripUndefined({
18
+ slot: "nav.sidebar",
19
+ path,
20
+ order,
21
+ requiredPermission
22
+ });
23
+ return defineExtension({ ...stripUndefined(rest), targets: [target], ...hidden ? { hidden: true } : {} });
24
+ }
25
+ function routeExtension(ext) {
26
+ const { path, order, params, ...rest } = ext;
27
+ const target = stripUndefined({
28
+ slot: "plugin.route",
29
+ path,
30
+ order,
31
+ params
32
+ });
33
+ return defineExtension({ ...stripUndefined(rest), targets: [target] });
34
+ }
35
+ function settingsExtension(ext) {
36
+ const { order, visibility, ...rest } = ext;
37
+ const target = stripUndefined({
38
+ slot: "settings.plugin",
39
+ order,
40
+ visibility
41
+ });
42
+ return defineExtension({ ...stripUndefined(rest), targets: [target] });
43
+ }
44
+ function dashboardExtension(ext) {
45
+ const { order, colSpan, ...rest } = ext;
46
+ const target = stripUndefined({
47
+ slot: "dashboard.widgets",
48
+ order,
49
+ colSpan
50
+ });
51
+ return defineExtension({ ...stripUndefined(rest), targets: [target] });
52
+ }
53
+ function multiSlotExtension(ext) {
54
+ return defineExtension(ext);
55
+ }
56
+
57
+ export {
58
+ defineExtension,
59
+ navExtension,
60
+ routeExtension,
61
+ settingsExtension,
62
+ dashboardExtension,
63
+ multiSlotExtension
64
+ };
65
+ //# sourceMappingURL=chunk-MZOLSLJ7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/extension-helpers.ts"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ComponentType = (props: any) => any;\n\n// ─── 类型定义(与 admin 侧 extension-types.ts 保持一致) ───\n\nexport type Target = NavTarget | NavGroupTarget | RouteTarget | SettingsTarget | DashboardTarget | GenericTarget;\n\nexport interface NavTarget {\n slot: 'nav.sidebar';\n path: string;\n icon?: string;\n order?: number;\n requiredPermission?: string;\n parent?: string;\n}\n\nexport interface NavGroupTarget {\n slot: 'nav.sidebar.group';\n path?: string;\n icon?: string;\n order?: number;\n}\n\nexport interface RouteTarget {\n slot: 'plugin.route';\n path: string;\n order?: number;\n params?: Record<string, string>;\n}\n\nexport interface SettingsTarget {\n slot: 'settings.plugin';\n category?: string;\n order?: number;\n visibility?: 'platform' | 'all';\n}\n\nexport interface DashboardTarget {\n slot: 'dashboard.widgets' | 'dashboard.overview';\n order?: number;\n colSpan?: 1 | 2 | 3 | 4;\n}\n\nexport interface GenericTarget {\n slot: string;\n order?: number;\n}\n\nexport interface SlotContext {\n [key: string]: unknown;\n}\n\nexport interface UIExtensionDef {\n id: string;\n label: string;\n icon?: string;\n category?: string;\n component?: ComponentType;\n remoteComponent?: string;\n targets: Target[];\n /** If true, the extension is registered for component matching but hidden from sidebar */\n hidden?: boolean;\n}\n\nexport type ExtensionComponentMap = Record<string, ComponentType>;\nexport type ToolbarHandlerMap = Record<string, () => void>;\n\n/** Remove undefined values from an object to satisfy exactOptionalPropertyTypes */\nfunction stripUndefined<T extends object>(obj: T): T {\n const result = {} as Record<string, unknown>;\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) result[k] = v;\n }\n return result as T;\n}\n\n// ─── 辅助函数 ───\n\nexport function defineExtension(ext: UIExtensionDef): UIExtensionDef {\n return stripUndefined({\n ...ext,\n targets: ext.targets.map((target) => stripUndefined(target)),\n });\n}\n\nexport function navExtension(ext: {\n id: string;\n label: string;\n icon?: string;\n component?: ComponentType;\n remoteComponent?: string;\n path: string;\n order?: number;\n requiredPermission?: string;\n /** If true, registers component but hides from sidebar (menu managed by host) */\n hidden?: boolean;\n}): UIExtensionDef {\n const { path, order, requiredPermission, hidden, ...rest } = ext;\n const target: NavTarget = stripUndefined({\n slot: 'nav.sidebar' as const,\n path,\n order,\n requiredPermission,\n }) as NavTarget;\n return defineExtension({ ...stripUndefined(rest), targets: [target], ...(hidden ? { hidden: true } : {}) });\n}\n\nexport function routeExtension(ext: {\n id: string;\n label: string;\n icon?: string;\n component?: ComponentType;\n remoteComponent?: string;\n path: string;\n order?: number;\n params?: Record<string, string>;\n}): UIExtensionDef {\n const { path, order, params, ...rest } = ext;\n const target: RouteTarget = stripUndefined({\n slot: 'plugin.route' as const,\n path,\n order,\n params,\n }) as RouteTarget;\n return defineExtension({ ...stripUndefined(rest), targets: [target] });\n}\n\nexport function settingsExtension(ext: {\n id: string;\n label: string;\n icon?: string;\n component?: ComponentType;\n remoteComponent?: string;\n order?: number;\n category?: string;\n visibility?: 'platform' | 'all';\n}): UIExtensionDef {\n const { order, visibility, ...rest } = ext;\n const target: SettingsTarget = stripUndefined({\n slot: 'settings.plugin' as const,\n order,\n visibility,\n }) as SettingsTarget;\n return defineExtension({ ...stripUndefined(rest), targets: [target] });\n}\n\nexport function dashboardExtension(ext: {\n id: string;\n label: string;\n icon?: string;\n component?: ComponentType;\n remoteComponent?: string;\n order?: number;\n colSpan?: 1 | 2 | 3 | 4;\n}): UIExtensionDef {\n const { order, colSpan, ...rest } = ext;\n const target: DashboardTarget = stripUndefined({\n slot: 'dashboard.widgets' as const,\n order,\n colSpan,\n }) as DashboardTarget;\n return defineExtension({ ...stripUndefined(rest), targets: [target] });\n}\n\nexport function multiSlotExtension(ext: UIExtensionDef): UIExtensionDef {\n return defineExtension(ext);\n}\n"],"mappings":";AAoEA,SAAS,eAAiC,KAAW;AACjD,QAAM,SAAS,CAAC;AAChB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,GAAG;AACtC,QAAI,MAAM,OAAW,QAAO,CAAC,IAAI;AAAA,EACrC;AACA,SAAO;AACX;AAIO,SAAS,gBAAgB,KAAqC;AACjE,SAAO,eAAe;AAAA,IAClB,GAAG;AAAA,IACH,SAAS,IAAI,QAAQ,IAAI,CAAC,WAAW,eAAe,MAAM,CAAC;AAAA,EAC/D,CAAC;AACL;AAEO,SAAS,aAAa,KAWV;AACf,QAAM,EAAE,MAAM,OAAO,oBAAoB,QAAQ,GAAG,KAAK,IAAI;AAC7D,QAAM,SAAoB,eAAe;AAAA,IACrC,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACD,SAAO,gBAAgB,EAAE,GAAG,eAAe,IAAI,GAAG,SAAS,CAAC,MAAM,GAAG,GAAI,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC,EAAG,CAAC;AAC9G;AAEO,SAAS,eAAe,KASZ;AACf,QAAM,EAAE,MAAM,OAAO,QAAQ,GAAG,KAAK,IAAI;AACzC,QAAM,SAAsB,eAAe;AAAA,IACvC,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACD,SAAO,gBAAgB,EAAE,GAAG,eAAe,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;AACzE;AAEO,SAAS,kBAAkB,KASf;AACf,QAAM,EAAE,OAAO,YAAY,GAAG,KAAK,IAAI;AACvC,QAAM,SAAyB,eAAe;AAAA,IAC1C,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACJ,CAAC;AACD,SAAO,gBAAgB,EAAE,GAAG,eAAe,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;AACzE;AAEO,SAAS,mBAAmB,KAQhB;AACf,QAAM,EAAE,OAAO,SAAS,GAAG,KAAK,IAAI;AACpC,QAAM,SAA0B,eAAe;AAAA,IAC3C,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACJ,CAAC;AACD,SAAO,gBAAgB,EAAE,GAAG,eAAe,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;AACzE;AAEO,SAAS,mBAAmB,KAAqC;AACpE,SAAO,gBAAgB,GAAG;AAC9B;","names":[]}