@raubjo/architect 0.5.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.
- package/README.md +860 -0
- package/package.json +121 -0
- package/src/cache/cache.ts +46 -0
- package/src/cache/contract.ts +9 -0
- package/src/cache/manager.ts +110 -0
- package/src/cache/provider.ts +11 -0
- package/src/config/contract.ts +63 -0
- package/src/config/discovery.ts +99 -0
- package/src/config/env.global.d.ts +6 -0
- package/src/config/env.ts +68 -0
- package/src/config/index.ts +5 -0
- package/src/config/provider.ts +17 -0
- package/src/config/repository.ts +164 -0
- package/src/container/adapters/builtin.ts +323 -0
- package/src/container/contract.ts +43 -0
- package/src/container/runtime.ts +29 -0
- package/src/events/bus.ts +174 -0
- package/src/events/concerns/dispatchable.ts +10 -0
- package/src/events/provider.ts +9 -0
- package/src/events/types.ts +9 -0
- package/src/foundation/application.ts +136 -0
- package/src/foundation/current-application.ts +20 -0
- package/src/index.ts +58 -0
- package/src/log/contract.ts +21 -0
- package/src/log/drivers/console.ts +54 -0
- package/src/log/drivers/null.ts +23 -0
- package/src/log/drivers/stack.ts +46 -0
- package/src/log/manager.ts +76 -0
- package/src/log/provider.ts +11 -0
- package/src/react.ts +2 -0
- package/src/renderers/adapters/react.tsx +25 -0
- package/src/renderers/adapters/solid.tsx +26 -0
- package/src/renderers/adapters/svelte.ts +73 -0
- package/src/renderers/adapters/vue.ts +22 -0
- package/src/renderers/contract.ts +12 -0
- package/src/runtimes/react.tsx +81 -0
- package/src/runtimes/solid.tsx +47 -0
- package/src/runtimes/svelte.ts +17 -0
- package/src/runtimes/vue.ts +34 -0
- package/src/solid.ts +2 -0
- package/src/store/adapters/contract.ts +11 -0
- package/src/store/adapters/indexed-db.ts +187 -0
- package/src/store/adapters/local-storage.ts +48 -0
- package/src/store/adapters/memory.ts +35 -0
- package/src/store/manager.ts +68 -0
- package/src/store/provider.ts +10 -0
- package/src/store/store.ts +1 -0
- package/src/support/arr.ts +372 -0
- package/src/support/collection.ts +889 -0
- package/src/support/facades/cache.ts +6 -0
- package/src/support/facades/config.ts +6 -0
- package/src/support/facades/event.ts +6 -0
- package/src/support/facades/facade.ts +146 -0
- package/src/support/facades/index.ts +5 -0
- package/src/support/facades/log.ts +6 -0
- package/src/support/facades/store.ts +6 -0
- package/src/support/fluent.ts +56 -0
- package/src/support/globals.ts +8 -0
- package/src/support/lazy-collection.ts +341 -0
- package/src/support/manager.ts +53 -0
- package/src/support/num.ts +50 -0
- package/src/support/pipeline.ts +29 -0
- package/src/support/service-provider.ts +19 -0
- package/src/support/str.ts +682 -0
- package/src/svelte.ts +2 -0
- package/src/types/peer-deps.d.ts +10 -0
- package/src/vue.ts +2 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
function splitWords(value: string): string[] {
|
|
2
|
+
const normalized = value
|
|
3
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
4
|
+
.replace(/[_\-.]+/g, " ")
|
|
5
|
+
.trim()
|
|
6
|
+
if (!normalized) return []
|
|
7
|
+
return normalized.split(/\s+/)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function normalizeForSlug(value: string): string {
|
|
11
|
+
return value
|
|
12
|
+
.normalize("NFKD")
|
|
13
|
+
.replace(/[̀-ͯ]/g, "")
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
16
|
+
.replace(/^-+|-+$/g, "")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function lower(value: string): string {
|
|
20
|
+
return value.toLowerCase()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function upper(value: string): string {
|
|
24
|
+
return value.toUpperCase()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function length(value: string): number {
|
|
28
|
+
return value.length
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function contains(haystack: string, needle: string | string[], ignoreCase = false): boolean {
|
|
32
|
+
const source = ignoreCase ? haystack.toLowerCase() : haystack
|
|
33
|
+
const needles = Array.isArray(needle) ? needle : [needle]
|
|
34
|
+
return needles.some((part) => source.includes(ignoreCase ? part.toLowerCase() : part))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function startsWith(haystack: string, needle: string | string[]): boolean {
|
|
38
|
+
const needles = Array.isArray(needle) ? needle : [needle]
|
|
39
|
+
return needles.some((part) => haystack.startsWith(part))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function endsWith(haystack: string, needle: string | string[]): boolean {
|
|
43
|
+
const needles = Array.isArray(needle) ? needle : [needle]
|
|
44
|
+
return needles.some((part) => haystack.endsWith(part))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function replace(search: string | RegExp, replaceWith: string, subject: string): string {
|
|
48
|
+
return subject.replace(search, replaceWith)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function snake(value: string, separator = "_"): string {
|
|
52
|
+
return splitWords(value)
|
|
53
|
+
.map((w) => w.toLowerCase())
|
|
54
|
+
.join(separator)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function kebab(value: string): string {
|
|
58
|
+
return snake(value, "-")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function studly(value: string): string {
|
|
62
|
+
return splitWords(value)
|
|
63
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
64
|
+
.join("")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function camel(value: string): string {
|
|
68
|
+
const s = studly(value)
|
|
69
|
+
return s ? s.charAt(0).toLowerCase() + s.slice(1) : ""
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function slug(value: string, separator = "-"): string {
|
|
73
|
+
const slugged = normalizeForSlug(value)
|
|
74
|
+
return separator === "-" ? slugged : slugged.replace(/-/g, separator)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function after(subject: string, search: string): string {
|
|
78
|
+
if (search === "") return subject
|
|
79
|
+
const idx = subject.indexOf(search)
|
|
80
|
+
return idx === -1 ? subject : subject.slice(idx + search.length)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function afterLast(subject: string, search: string): string {
|
|
84
|
+
if (search === "") return subject
|
|
85
|
+
const idx = subject.lastIndexOf(search)
|
|
86
|
+
return idx === -1 ? subject : subject.slice(idx + search.length)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const APA_MINOR = new Set([
|
|
90
|
+
"a",
|
|
91
|
+
"an",
|
|
92
|
+
"the",
|
|
93
|
+
"and",
|
|
94
|
+
"but",
|
|
95
|
+
"for",
|
|
96
|
+
"nor",
|
|
97
|
+
"or",
|
|
98
|
+
"so",
|
|
99
|
+
"yet",
|
|
100
|
+
"at",
|
|
101
|
+
"by",
|
|
102
|
+
"in",
|
|
103
|
+
"of",
|
|
104
|
+
"on",
|
|
105
|
+
"to",
|
|
106
|
+
"up",
|
|
107
|
+
"as",
|
|
108
|
+
"is",
|
|
109
|
+
"it",
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
export function apa(value: string): string {
|
|
113
|
+
const words = value.split(/\s+/)
|
|
114
|
+
return words
|
|
115
|
+
.map((word, i) => {
|
|
116
|
+
const lower = word.toLowerCase()
|
|
117
|
+
if (i === 0 || i === words.length - 1 || !APA_MINOR.has(lower)) {
|
|
118
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
|
119
|
+
}
|
|
120
|
+
return lower
|
|
121
|
+
})
|
|
122
|
+
.join(" ")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function before(subject: string, search: string): string {
|
|
126
|
+
if (search === "") return subject
|
|
127
|
+
const idx = subject.indexOf(search)
|
|
128
|
+
return idx === -1 ? subject : subject.slice(0, idx)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function beforeLast(subject: string, search: string): string {
|
|
132
|
+
if (search === "") return subject
|
|
133
|
+
const idx = subject.lastIndexOf(search)
|
|
134
|
+
return idx === -1 ? subject : subject.slice(0, idx)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function between(subject: string, from: string, to: string): string {
|
|
138
|
+
if (from === "" || to === "") return subject
|
|
139
|
+
const start = subject.indexOf(from)
|
|
140
|
+
if (start === -1) return subject
|
|
141
|
+
const end = subject.lastIndexOf(to)
|
|
142
|
+
if (end === -1 || end <= start) return subject
|
|
143
|
+
return subject.slice(start + from.length, end)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function betweenFirst(subject: string, from: string, to: string): string {
|
|
147
|
+
if (from === "" || to === "") return subject
|
|
148
|
+
const start = subject.indexOf(from)
|
|
149
|
+
if (start === -1) return subject
|
|
150
|
+
const end = subject.indexOf(to, start + from.length)
|
|
151
|
+
if (end === -1) return subject
|
|
152
|
+
return subject.slice(start + from.length, end)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function charAt(value: string, index: number): string | false {
|
|
156
|
+
if (index < 0 || index >= value.length) return false
|
|
157
|
+
return value[index] as string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function chopStart(value: string, needle: string | string[]): string {
|
|
161
|
+
const needles = Array.isArray(needle) ? needle : [needle]
|
|
162
|
+
for (const n of needles) {
|
|
163
|
+
if (value.startsWith(n)) {
|
|
164
|
+
return value.slice(n.length)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return value
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function chopEnd(value: string, needle: string | string[]): string {
|
|
171
|
+
const needles = Array.isArray(needle) ? needle : [needle]
|
|
172
|
+
for (const n of needles) {
|
|
173
|
+
if (value.endsWith(n)) {
|
|
174
|
+
return value.slice(0, value.length - n.length)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return value
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function containsAll(haystack: string, needles: string[], ignoreCase = false): boolean {
|
|
181
|
+
const source = ignoreCase ? haystack.toLowerCase() : haystack
|
|
182
|
+
return needles.every((n) => source.includes(ignoreCase ? n.toLowerCase() : n))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function deduplicate(value: string, char = " "): string {
|
|
186
|
+
const escaped = char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
187
|
+
return value.replace(new RegExp(`${escaped}+`, "g"), char)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function doesntContain(haystack: string, needle: string | string[], ignoreCase = false): boolean {
|
|
191
|
+
return !contains(haystack, needle, ignoreCase)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function doesntEndWith(haystack: string, needle: string | string[]): boolean {
|
|
195
|
+
return !endsWith(haystack, needle)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function doesntStartWith(haystack: string, needle: string | string[]): boolean {
|
|
199
|
+
return !startsWith(haystack, needle)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function excerpt(value: string, phrase: string, options: { radius?: number; omission?: string } = {}): string {
|
|
203
|
+
const radius = options.radius ?? 100
|
|
204
|
+
const omission = options.omission ?? "..."
|
|
205
|
+
|
|
206
|
+
const idx = value.indexOf(phrase)
|
|
207
|
+
if (idx === -1) return value
|
|
208
|
+
|
|
209
|
+
const start = Math.max(0, idx - radius)
|
|
210
|
+
const end = Math.min(value.length, idx + phrase.length + radius)
|
|
211
|
+
|
|
212
|
+
const prefix = start > 0 ? omission : ""
|
|
213
|
+
const suffix = end < value.length ? omission : ""
|
|
214
|
+
|
|
215
|
+
return prefix + value.slice(start, end) + suffix
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function finish(value: string, cap: string): string {
|
|
219
|
+
return value.endsWith(cap) ? value : value + cap
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function fromBase64(value: string): string {
|
|
223
|
+
return atob(value)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function headline(value: string): string {
|
|
227
|
+
return splitWords(value)
|
|
228
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
229
|
+
.join(" ")
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function initials(value: string, capitalize = false): string {
|
|
233
|
+
const result = value
|
|
234
|
+
.trim()
|
|
235
|
+
.split(/\s+/)
|
|
236
|
+
.map((w) => w.charAt(0))
|
|
237
|
+
.join("")
|
|
238
|
+
return capitalize ? result.toUpperCase() : result.toLowerCase()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function is(pattern: string, value: string, ignoreCase = false): boolean {
|
|
242
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")
|
|
243
|
+
const flags = ignoreCase ? "i" : ""
|
|
244
|
+
return new RegExp(`^${escaped}$`, flags).test(value)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function isAscii(value: string): boolean {
|
|
248
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ascii characters
|
|
249
|
+
return /^[\x00-\x7F]*$/.test(value)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function isJson(value: string): boolean {
|
|
253
|
+
try {
|
|
254
|
+
JSON.parse(value)
|
|
255
|
+
return true
|
|
256
|
+
} catch {
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function isUrl(value: string, protocols?: string[]): boolean {
|
|
262
|
+
try {
|
|
263
|
+
const url = new URL(value)
|
|
264
|
+
if (protocols && protocols.length > 0) {
|
|
265
|
+
const proto = url.protocol.replace(/:$/, "")
|
|
266
|
+
return protocols.includes(proto)
|
|
267
|
+
}
|
|
268
|
+
return true
|
|
269
|
+
} catch {
|
|
270
|
+
return false
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function isUlid(value: string): boolean {
|
|
275
|
+
return /^[0-9A-HJKMNP-TV-Z]{26}$/i.test(value)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function isUuid(value: string, version?: 1 | 3 | 4 | 5 | 6 | 7 | 8): boolean {
|
|
279
|
+
if (version === undefined) {
|
|
280
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)
|
|
281
|
+
}
|
|
282
|
+
const v = version.toString()
|
|
283
|
+
return new RegExp(`^[0-9a-f]{8}-[0-9a-f]{4}-${v}[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$`, "i").test(value)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function isMatch(pattern: string | RegExp, value: string): boolean {
|
|
287
|
+
return parsePattern(pattern).test(value)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function lcfirst(value: string): string {
|
|
291
|
+
return value ? value.charAt(0).toLowerCase() + value.slice(1) : value
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function limit(value: string, limitChars = 100, end = "...", preserveWords = false): string {
|
|
295
|
+
if (value.length <= limitChars) return value
|
|
296
|
+
if (!preserveWords) return value.slice(0, limitChars) + end
|
|
297
|
+
const truncated = value.slice(0, limitChars)
|
|
298
|
+
const lastSpace = truncated.lastIndexOf(" ")
|
|
299
|
+
return (lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated) + end
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function mask(value: string, char: string, index: number, maskLength?: number): string {
|
|
303
|
+
const len = value.length
|
|
304
|
+
const start = index < 0 ? Math.max(0, len + index) : Math.min(index, len)
|
|
305
|
+
const count = maskLength !== undefined ? Math.min(maskLength, len - start) : len - start
|
|
306
|
+
return value.slice(0, start) + char.repeat(count) + value.slice(start + count)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function parsePattern(pattern: string | RegExp): RegExp {
|
|
310
|
+
if (pattern instanceof RegExp) return pattern
|
|
311
|
+
// Support /pattern/flags delimiter syntax
|
|
312
|
+
const delimited = /^\/(.+)\/([gimsuy]*)$/.exec(pattern)
|
|
313
|
+
if (delimited) {
|
|
314
|
+
return new RegExp(delimited[1] as string, delimited[2] || "")
|
|
315
|
+
}
|
|
316
|
+
return new RegExp(pattern)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export function match(pattern: string | RegExp, value: string): string {
|
|
320
|
+
const re = parsePattern(pattern)
|
|
321
|
+
const m = value.match(re)
|
|
322
|
+
if (!m) return ""
|
|
323
|
+
return m[1] !== undefined ? m[1] : m[0]
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function matchAll(pattern: string | RegExp, value: string): string[] {
|
|
327
|
+
const base = parsePattern(pattern)
|
|
328
|
+
const flags = base.flags.includes("g") ? base.flags : `${base.flags}g`
|
|
329
|
+
const re = new RegExp(base.source, flags)
|
|
330
|
+
const results: string[] = []
|
|
331
|
+
let m: RegExpExecArray | null
|
|
332
|
+
while ((m = re.exec(value)) !== null) {
|
|
333
|
+
results.push(m[1] !== undefined ? m[1] : m[0])
|
|
334
|
+
}
|
|
335
|
+
return results
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function padBoth(value: string, padLength: number, pad = " "): string {
|
|
339
|
+
if (value.length >= padLength) return value
|
|
340
|
+
const total = padLength - value.length
|
|
341
|
+
const leftPad = Math.floor(total / 2)
|
|
342
|
+
const rightPad = total - leftPad
|
|
343
|
+
return (
|
|
344
|
+
pad.repeat(Math.ceil(leftPad / pad.length)).slice(0, leftPad) +
|
|
345
|
+
value +
|
|
346
|
+
pad.repeat(Math.ceil(rightPad / pad.length)).slice(0, rightPad)
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function padLeft(value: string, padLength: number, pad = " "): string {
|
|
351
|
+
return value.padStart(padLength, pad)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function padRight(value: string, padLength: number, pad = " "): string {
|
|
355
|
+
return value.padEnd(padLength, pad)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function position(haystack: string, needle: string, offset = 0): number | false {
|
|
359
|
+
const idx = haystack.indexOf(needle, offset)
|
|
360
|
+
return idx === -1 ? false : idx
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export function random(len = 16): string {
|
|
364
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
365
|
+
const result: string[] = []
|
|
366
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function") {
|
|
367
|
+
const bytes = new Uint8Array(len)
|
|
368
|
+
globalThis.crypto.getRandomValues(bytes)
|
|
369
|
+
for (const b of bytes) {
|
|
370
|
+
result.push(chars[b % chars.length] as string)
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
for (let i = 0; i < len; i++) {
|
|
374
|
+
result.push(chars[Math.floor(Math.random() * chars.length)] as string)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return result.join("")
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function remove(search: string | string[], subject: string, caseSensitive = true): string {
|
|
381
|
+
const searches = Array.isArray(search) ? search : [search]
|
|
382
|
+
let result = subject
|
|
383
|
+
for (const s of searches) {
|
|
384
|
+
if (caseSensitive) {
|
|
385
|
+
result = result.split(s).join("")
|
|
386
|
+
} else {
|
|
387
|
+
result = result.replace(new RegExp(s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi"), "")
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return result
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function repeat(value: string, times: number): string {
|
|
394
|
+
return value.repeat(times)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function replaceArray(search: string, replaceList: string[], subject: string): string {
|
|
398
|
+
let replaceIdx = 0
|
|
399
|
+
const searchLen = search.length
|
|
400
|
+
let result = ""
|
|
401
|
+
let remaining = subject
|
|
402
|
+
while (replaceIdx < replaceList.length) {
|
|
403
|
+
const pos = remaining.indexOf(search)
|
|
404
|
+
if (pos === -1) break
|
|
405
|
+
result += remaining.slice(0, pos) + (replaceList[replaceIdx] as string)
|
|
406
|
+
remaining = remaining.slice(pos + searchLen)
|
|
407
|
+
replaceIdx++
|
|
408
|
+
}
|
|
409
|
+
return result + remaining
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export function replaceFirst(search: string, replaceWith: string, subject: string): string {
|
|
413
|
+
const idx = subject.indexOf(search)
|
|
414
|
+
if (idx === -1) return subject
|
|
415
|
+
return subject.slice(0, idx) + replaceWith + subject.slice(idx + search.length)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function replaceLast(search: string, replaceWith: string, subject: string): string {
|
|
419
|
+
const idx = subject.lastIndexOf(search)
|
|
420
|
+
if (idx === -1) return subject
|
|
421
|
+
return subject.slice(0, idx) + replaceWith + subject.slice(idx + search.length)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export function replaceMatches(
|
|
425
|
+
pattern: string | RegExp,
|
|
426
|
+
replaceWith: string | ((match: string[]) => string),
|
|
427
|
+
subject: string,
|
|
428
|
+
): string {
|
|
429
|
+
const base = parsePattern(pattern)
|
|
430
|
+
const flags = base.flags.includes("g") ? base.flags : `${base.flags}g`
|
|
431
|
+
const re = new RegExp(base.source, flags)
|
|
432
|
+
if (typeof replaceWith === "string") {
|
|
433
|
+
return subject.replace(re, replaceWith)
|
|
434
|
+
}
|
|
435
|
+
return subject.replace(re, (...args: unknown[]) => {
|
|
436
|
+
const matchArr = args.slice(0, args.length - 2) as string[]
|
|
437
|
+
return replaceWith(matchArr)
|
|
438
|
+
})
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export function replaceStart(search: string, replaceWith: string, subject: string): string {
|
|
442
|
+
return subject.startsWith(search) ? replaceWith + subject.slice(search.length) : subject
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export function replaceEnd(search: string, replaceWith: string, subject: string): string {
|
|
446
|
+
return subject.endsWith(search) ? subject.slice(0, subject.length - search.length) + replaceWith : subject
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function reverse(value: string): string {
|
|
450
|
+
return [...value].reverse().join("")
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function squish(value: string): string {
|
|
454
|
+
return value.trim().replace(/\s+/g, " ")
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function start(value: string, prefix: string): string {
|
|
458
|
+
return value.startsWith(prefix) ? value : prefix + value
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function substr(value: string, startIndex: number, len?: number): string {
|
|
462
|
+
if (len === undefined) {
|
|
463
|
+
return value.slice(startIndex)
|
|
464
|
+
}
|
|
465
|
+
return value.slice(startIndex, startIndex < 0 ? startIndex + len : startIndex + len)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export function substrCount(value: string, needle: string): number {
|
|
469
|
+
if (needle === "") return 0
|
|
470
|
+
let count = 0
|
|
471
|
+
let idx = 0
|
|
472
|
+
while ((idx = value.indexOf(needle, idx)) !== -1) {
|
|
473
|
+
count++
|
|
474
|
+
idx += needle.length
|
|
475
|
+
}
|
|
476
|
+
return count
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export function substrReplace(value: string, replacer: string, startIndex: number, len?: number): string {
|
|
480
|
+
const arr = [...value]
|
|
481
|
+
const realStart = startIndex < 0 ? Math.max(0, arr.length + startIndex) : startIndex
|
|
482
|
+
if (len === undefined) {
|
|
483
|
+
arr.splice(realStart, arr.length - realStart, ...replacer)
|
|
484
|
+
} else {
|
|
485
|
+
arr.splice(realStart, len, ...replacer)
|
|
486
|
+
}
|
|
487
|
+
return arr.join("")
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export function swap(map: Record<string, string>, subject: string): string {
|
|
491
|
+
const keys = Object.keys(map).sort((a, b) => b.length - a.length)
|
|
492
|
+
let result = subject
|
|
493
|
+
for (const key of keys) {
|
|
494
|
+
result = result.split(key).join(map[key] as string)
|
|
495
|
+
}
|
|
496
|
+
return result
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export function take(value: string, limitChars: number): string {
|
|
500
|
+
if (limitChars >= 0) return value.slice(0, limitChars)
|
|
501
|
+
return value.slice(limitChars)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export function title(value: string): string {
|
|
505
|
+
return value.replace(/\S+/g, (w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export function toBase64(value: string): string {
|
|
509
|
+
return btoa(value)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export function trim(value: string, chars?: string): string {
|
|
513
|
+
if (!chars) return value.trim()
|
|
514
|
+
const escaped = chars.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
515
|
+
return value.replace(new RegExp(`^[${escaped}]+|[${escaped}]+$`, "g"), "")
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export function ltrim(value: string, chars?: string): string {
|
|
519
|
+
if (!chars) return value.trimStart()
|
|
520
|
+
const escaped = chars.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
521
|
+
return value.replace(new RegExp(`^[${escaped}]+`, "g"), "")
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export function rtrim(value: string, chars?: string): string {
|
|
525
|
+
if (!chars) return value.trimEnd()
|
|
526
|
+
const escaped = chars.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
527
|
+
return value.replace(new RegExp(`[${escaped}]+$`, "g"), "")
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export function ucfirst(value: string): string {
|
|
531
|
+
return value ? value.charAt(0).toUpperCase() + value.slice(1) : value
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function ucsplit(value: string): string[] {
|
|
535
|
+
return value.split(/(?=[A-Z])/).filter(Boolean)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export function ucwords(value: string): string {
|
|
539
|
+
return value.replace(/(?:^|\s)\S/g, (c) => c.toUpperCase())
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export function unwrap(value: string, before: string, after?: string): string {
|
|
543
|
+
const end = after ?? before
|
|
544
|
+
let result = value
|
|
545
|
+
if (result.startsWith(before)) result = result.slice(before.length)
|
|
546
|
+
if (result.endsWith(end)) result = result.slice(0, result.length - end.length)
|
|
547
|
+
return result
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export function wordCount(value: string): number {
|
|
551
|
+
return value.trim() === "" ? 0 : value.trim().split(/\s+/).length
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
export function wordWrap(value: string, characters = 75, breakWith = "\n", cutLongWords = false): string {
|
|
555
|
+
if (value.length <= characters) return value
|
|
556
|
+
const words = value.split(" ")
|
|
557
|
+
const lines: string[] = []
|
|
558
|
+
let current = ""
|
|
559
|
+
for (const word of words) {
|
|
560
|
+
if (cutLongWords && word.length > characters) {
|
|
561
|
+
if (current !== "") {
|
|
562
|
+
lines.push(current)
|
|
563
|
+
current = ""
|
|
564
|
+
}
|
|
565
|
+
let remaining = word
|
|
566
|
+
while (remaining.length > characters) {
|
|
567
|
+
lines.push(remaining.slice(0, characters))
|
|
568
|
+
remaining = remaining.slice(characters)
|
|
569
|
+
}
|
|
570
|
+
current = remaining
|
|
571
|
+
} else if (current === "") {
|
|
572
|
+
current = word
|
|
573
|
+
} else if (current.length + 1 + word.length <= characters) {
|
|
574
|
+
current += ` ${word}`
|
|
575
|
+
} else {
|
|
576
|
+
lines.push(current)
|
|
577
|
+
current = word
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (current !== "") lines.push(current)
|
|
581
|
+
return lines.join(breakWith)
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export function words(value: string, count: number, end = "..."): string {
|
|
585
|
+
const parts = value.trim().split(/\s+/)
|
|
586
|
+
if (parts.length <= count) return value
|
|
587
|
+
return parts.slice(0, count).join(" ") + end
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export function wrap(value: string, before: string, after?: string): string {
|
|
591
|
+
return before + value + (after ?? before)
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export const Str = {
|
|
595
|
+
after,
|
|
596
|
+
afterLast,
|
|
597
|
+
apa,
|
|
598
|
+
before,
|
|
599
|
+
beforeLast,
|
|
600
|
+
between,
|
|
601
|
+
betweenFirst,
|
|
602
|
+
camel,
|
|
603
|
+
charAt,
|
|
604
|
+
chopEnd,
|
|
605
|
+
chopStart,
|
|
606
|
+
contains,
|
|
607
|
+
containsAll,
|
|
608
|
+
deduplicate,
|
|
609
|
+
doesntContain,
|
|
610
|
+
doesntEndWith,
|
|
611
|
+
doesntStartWith,
|
|
612
|
+
endsWith,
|
|
613
|
+
excerpt,
|
|
614
|
+
finish,
|
|
615
|
+
fromBase64,
|
|
616
|
+
headline,
|
|
617
|
+
initials,
|
|
618
|
+
is,
|
|
619
|
+
isAscii,
|
|
620
|
+
isJson,
|
|
621
|
+
isMatch,
|
|
622
|
+
isUlid,
|
|
623
|
+
isUrl,
|
|
624
|
+
isUuid,
|
|
625
|
+
kebab,
|
|
626
|
+
lcfirst,
|
|
627
|
+
length,
|
|
628
|
+
limit,
|
|
629
|
+
lower,
|
|
630
|
+
ltrim,
|
|
631
|
+
mask,
|
|
632
|
+
match,
|
|
633
|
+
matchAll,
|
|
634
|
+
padBoth,
|
|
635
|
+
padLeft,
|
|
636
|
+
padRight,
|
|
637
|
+
position,
|
|
638
|
+
random,
|
|
639
|
+
remove,
|
|
640
|
+
repeat,
|
|
641
|
+
replace,
|
|
642
|
+
replaceArray,
|
|
643
|
+
replaceEnd,
|
|
644
|
+
replaceFirst,
|
|
645
|
+
replaceLast,
|
|
646
|
+
replaceMatches,
|
|
647
|
+
replaceStart,
|
|
648
|
+
reverse,
|
|
649
|
+
rtrim,
|
|
650
|
+
slug,
|
|
651
|
+
snake,
|
|
652
|
+
squish,
|
|
653
|
+
start,
|
|
654
|
+
startsWith,
|
|
655
|
+
studly,
|
|
656
|
+
substr,
|
|
657
|
+
substrCount,
|
|
658
|
+
substrReplace,
|
|
659
|
+
swap,
|
|
660
|
+
take,
|
|
661
|
+
title,
|
|
662
|
+
toBase64,
|
|
663
|
+
trim,
|
|
664
|
+
ucfirst,
|
|
665
|
+
ucsplit,
|
|
666
|
+
ucwords,
|
|
667
|
+
unwrap,
|
|
668
|
+
upper,
|
|
669
|
+
wordCount,
|
|
670
|
+
wordWrap,
|
|
671
|
+
words,
|
|
672
|
+
wrap,
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
export function registerGlobalStr(): void {
|
|
676
|
+
const globalScope = globalThis as { Str?: typeof Str }
|
|
677
|
+
if (typeof globalScope.Str === "undefined") {
|
|
678
|
+
globalScope.Str = Str
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
export const strTestingHelpers = { splitWords, normalizeForSlug }
|
package/src/svelte.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare module "svelte" {
|
|
2
|
+
export function setContext(key: unknown, value: unknown): void
|
|
3
|
+
export function getContext<T>(key: unknown): T
|
|
4
|
+
export function mount(component: unknown, options: unknown): unknown
|
|
5
|
+
export function unmount(instance: unknown): void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare module "solid-js/web/dist/server.js" {
|
|
9
|
+
export * from "solid-js/web"
|
|
10
|
+
}
|
package/src/vue.ts
ADDED
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"paths": {
|
|
9
|
+
"@/*": ["./src/*"]
|
|
10
|
+
},
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"noEmit": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|