digital-objects 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +25 -0
  3. package/LICENSE +21 -0
  4. package/README.md +476 -0
  5. package/dist/ai-database-adapter.d.ts +49 -0
  6. package/dist/ai-database-adapter.d.ts.map +1 -0
  7. package/dist/ai-database-adapter.js +89 -0
  8. package/dist/ai-database-adapter.js.map +1 -0
  9. package/dist/errors.d.ts +47 -0
  10. package/dist/errors.d.ts.map +1 -0
  11. package/dist/errors.js +72 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/http-schemas.d.ts +165 -0
  14. package/dist/http-schemas.d.ts.map +1 -0
  15. package/dist/http-schemas.js +55 -0
  16. package/dist/http-schemas.js.map +1 -0
  17. package/dist/index.d.ts +29 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +32 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/linguistic.d.ts +54 -0
  22. package/dist/linguistic.d.ts.map +1 -0
  23. package/dist/linguistic.js +226 -0
  24. package/dist/linguistic.js.map +1 -0
  25. package/dist/memory-provider.d.ts +46 -0
  26. package/dist/memory-provider.d.ts.map +1 -0
  27. package/dist/memory-provider.js +279 -0
  28. package/dist/memory-provider.js.map +1 -0
  29. package/dist/ns-client.d.ts +88 -0
  30. package/dist/ns-client.d.ts.map +1 -0
  31. package/dist/ns-client.js +253 -0
  32. package/dist/ns-client.js.map +1 -0
  33. package/dist/ns-exports.d.ts +23 -0
  34. package/dist/ns-exports.d.ts.map +1 -0
  35. package/dist/ns-exports.js +21 -0
  36. package/dist/ns-exports.js.map +1 -0
  37. package/dist/ns.d.ts +60 -0
  38. package/dist/ns.d.ts.map +1 -0
  39. package/dist/ns.js +818 -0
  40. package/dist/ns.js.map +1 -0
  41. package/dist/r2-persistence.d.ts +112 -0
  42. package/dist/r2-persistence.d.ts.map +1 -0
  43. package/dist/r2-persistence.js +252 -0
  44. package/dist/r2-persistence.js.map +1 -0
  45. package/dist/schema-validation.d.ts +80 -0
  46. package/dist/schema-validation.d.ts.map +1 -0
  47. package/dist/schema-validation.js +233 -0
  48. package/dist/schema-validation.js.map +1 -0
  49. package/dist/types.d.ts +184 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +26 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +55 -0
  54. package/src/ai-database-adapter.test.ts +610 -0
  55. package/src/ai-database-adapter.ts +189 -0
  56. package/src/benchmark.test.ts +109 -0
  57. package/src/errors.ts +91 -0
  58. package/src/http-schemas.ts +67 -0
  59. package/src/index.ts +87 -0
  60. package/src/linguistic.test.ts +1107 -0
  61. package/src/linguistic.ts +253 -0
  62. package/src/memory-provider.ts +470 -0
  63. package/src/ns-client.test.ts +1360 -0
  64. package/src/ns-client.ts +342 -0
  65. package/src/ns-exports.ts +23 -0
  66. package/src/ns.test.ts +1381 -0
  67. package/src/ns.ts +1215 -0
  68. package/src/provider.test.ts +675 -0
  69. package/src/r2-persistence.test.ts +263 -0
  70. package/src/r2-persistence.ts +367 -0
  71. package/src/schema-validation.test.ts +167 -0
  72. package/src/schema-validation.ts +330 -0
  73. package/src/types.ts +252 -0
  74. package/test/action-status.test.ts +42 -0
  75. package/test/batch-limits.test.ts +165 -0
  76. package/test/docs.test.ts +48 -0
  77. package/test/errors.test.ts +148 -0
  78. package/test/http-validation.test.ts +401 -0
  79. package/test/ns-client-errors.test.ts +208 -0
  80. package/test/ns-namespace.test.ts +307 -0
  81. package/test/performance.test.ts +168 -0
  82. package/test/schema-validation-error.test.ts +213 -0
  83. package/test/schema-validation.test.ts +440 -0
  84. package/test/search-escaping.test.ts +359 -0
  85. package/test/security.test.ts +322 -0
  86. package/tsconfig.json +10 -0
  87. package/wrangler.jsonc +16 -0
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Linguistic utilities for auto-deriving noun and verb forms
3
+ */
4
+
5
+ /**
6
+ * Derive noun forms from a PascalCase name
7
+ *
8
+ * @example
9
+ * deriveNoun('Post') => { singular: 'post', plural: 'posts', slug: 'post' }
10
+ * deriveNoun('BlogPost') => { singular: 'blog post', plural: 'blog posts', slug: 'blog-post' }
11
+ * deriveNoun('Person') => { singular: 'person', plural: 'persons', slug: 'person' }
12
+ */
13
+ export function deriveNoun(name: string): { singular: string; plural: string; slug: string } {
14
+ // Convert PascalCase to words
15
+ const words = name
16
+ .replace(/([A-Z])/g, ' $1')
17
+ .trim()
18
+ .toLowerCase()
19
+ const singular = words
20
+ const slug = words.replace(/\s+/g, '-')
21
+ const plural = pluralize(singular)
22
+
23
+ return { singular, plural, slug }
24
+ }
25
+
26
+ /**
27
+ * Pluralize a word
28
+ *
29
+ * Handles common English pluralization rules:
30
+ * - Words ending in 's', 'x', 'z', 'ch', 'sh' -> add 'es'
31
+ * - Words ending in consonant + 'y' -> replace 'y' with 'ies'
32
+ * - Words ending in 'f' or 'fe' -> replace with 'ves'
33
+ * - Special cases (person->people, child->children, etc.)
34
+ * - Default: add 's'
35
+ */
36
+ export function pluralize(word: string): string {
37
+ // Handle multi-word phrases (pluralize last word only)
38
+ const parts = word.split(' ')
39
+ if (parts.length > 1) {
40
+ const lastIdx = parts.length - 1
41
+ parts[lastIdx] = pluralize(parts[lastIdx]!)
42
+ return parts.join(' ')
43
+ }
44
+
45
+ const w = word.toLowerCase()
46
+
47
+ // Irregular plurals
48
+ const irregulars: Record<string, string> = {
49
+ person: 'people',
50
+ child: 'children',
51
+ man: 'men',
52
+ woman: 'women',
53
+ foot: 'feet',
54
+ tooth: 'teeth',
55
+ goose: 'geese',
56
+ mouse: 'mice',
57
+ ox: 'oxen',
58
+ index: 'indices',
59
+ vertex: 'vertices',
60
+ matrix: 'matrices',
61
+ }
62
+
63
+ if (irregulars[w]) return irregulars[w]
64
+
65
+ // Words ending in 's', 'x', 'z', 'ch', 'sh' -> add 'es'
66
+ if (/[sxz]$/.test(w) || /[sc]h$/.test(w)) {
67
+ return w + 'es'
68
+ }
69
+
70
+ // Words ending in consonant + 'y' -> replace 'y' with 'ies'
71
+ if (/[^aeiou]y$/.test(w)) {
72
+ return w.slice(0, -1) + 'ies'
73
+ }
74
+
75
+ // Words ending in 'f' -> replace with 'ves'
76
+ if (/f$/.test(w)) {
77
+ return w.slice(0, -1) + 'ves'
78
+ }
79
+
80
+ // Words ending in 'fe' -> replace with 'ves'
81
+ if (/fe$/.test(w)) {
82
+ return w.slice(0, -2) + 'ves'
83
+ }
84
+
85
+ // Default: add 's'
86
+ return w + 's'
87
+ }
88
+
89
+ /**
90
+ * Singularize a word (reverse of pluralize)
91
+ */
92
+ export function singularize(word: string): string {
93
+ // Handle multi-word phrases
94
+ const parts = word.split(' ')
95
+ if (parts.length > 1) {
96
+ const lastIdx = parts.length - 1
97
+ parts[lastIdx] = singularize(parts[lastIdx]!)
98
+ return parts.join(' ')
99
+ }
100
+
101
+ const w = word.toLowerCase()
102
+
103
+ // Irregular singulars (reverse of irregulars)
104
+ const irregulars: Record<string, string> = {
105
+ people: 'person',
106
+ children: 'child',
107
+ men: 'man',
108
+ women: 'woman',
109
+ feet: 'foot',
110
+ teeth: 'tooth',
111
+ geese: 'goose',
112
+ mice: 'mouse',
113
+ oxen: 'ox',
114
+ indices: 'index',
115
+ vertices: 'vertex',
116
+ matrices: 'matrix',
117
+ }
118
+
119
+ if (irregulars[w]) return irregulars[w]
120
+
121
+ // Words ending in 'ies' -> replace with 'y'
122
+ if (/ies$/.test(w)) {
123
+ return w.slice(0, -3) + 'y'
124
+ }
125
+
126
+ // Words ending in 'ves' -> replace with 'f' or 'fe'
127
+ if (/ves$/.test(w)) {
128
+ // Default to 'f' (most common, e.g., 'leaves' -> 'leaf')
129
+ const fSingular = w.slice(0, -3) + 'f'
130
+ return fSingular
131
+ }
132
+
133
+ // Words ending in 'es' (but not 'ies' or 'ves')
134
+ if (/[sxz]es$/.test(w) || /[sc]hes$/.test(w)) {
135
+ return w.slice(0, -2)
136
+ }
137
+
138
+ // Words ending in 's' (but not 'es')
139
+ if (/s$/.test(w) && !/ss$/.test(w)) {
140
+ return w.slice(0, -1)
141
+ }
142
+
143
+ return w
144
+ }
145
+
146
+ /**
147
+ * Derive verb conjugations from base form
148
+ *
149
+ * @example
150
+ * deriveVerb('create') => {
151
+ * action: 'create',
152
+ * act: 'creates',
153
+ * activity: 'creating',
154
+ * event: 'created',
155
+ * reverseBy: 'createdBy',
156
+ * reverseAt: 'createdAt'
157
+ * }
158
+ */
159
+ export function deriveVerb(name: string): {
160
+ action: string
161
+ act: string
162
+ activity: string
163
+ event: string
164
+ reverseBy: string
165
+ reverseAt: string
166
+ reverseIn: string
167
+ } {
168
+ const base = name.toLowerCase()
169
+
170
+ // Known irregular verbs
171
+ const irregulars: Record<string, { act: string; activity: string; event: string }> = {
172
+ write: { act: 'writes', activity: 'writing', event: 'written' },
173
+ read: { act: 'reads', activity: 'reading', event: 'read' },
174
+ run: { act: 'runs', activity: 'running', event: 'run' },
175
+ begin: { act: 'begins', activity: 'beginning', event: 'begun' },
176
+ do: { act: 'does', activity: 'doing', event: 'done' },
177
+ go: { act: 'goes', activity: 'going', event: 'gone' },
178
+ have: { act: 'has', activity: 'having', event: 'had' },
179
+ be: { act: 'is', activity: 'being', event: 'been' },
180
+ set: { act: 'sets', activity: 'setting', event: 'set' },
181
+ get: { act: 'gets', activity: 'getting', event: 'got' },
182
+ put: { act: 'puts', activity: 'putting', event: 'put' },
183
+ cut: { act: 'cuts', activity: 'cutting', event: 'cut' },
184
+ hit: { act: 'hits', activity: 'hitting', event: 'hit' },
185
+ }
186
+
187
+ if (irregulars[base]) {
188
+ const irr = irregulars[base]
189
+ return {
190
+ action: base,
191
+ act: irr.act,
192
+ activity: irr.activity,
193
+ event: irr.event,
194
+ reverseBy: `${irr.event}By`,
195
+ reverseAt: `${irr.event}At`,
196
+ reverseIn: `${irr.event}In`,
197
+ }
198
+ }
199
+
200
+ // Regular verb conjugations
201
+ let act: string
202
+ let activity: string
203
+ let event: string
204
+
205
+ // Third person singular (act)
206
+ if (
207
+ base.endsWith('s') ||
208
+ base.endsWith('x') ||
209
+ base.endsWith('z') ||
210
+ base.endsWith('ch') ||
211
+ base.endsWith('sh')
212
+ ) {
213
+ act = base + 'es'
214
+ } else if (base.endsWith('y') && !/[aeiou]y$/.test(base)) {
215
+ act = base.slice(0, -1) + 'ies'
216
+ } else {
217
+ act = base + 's'
218
+ }
219
+
220
+ // Present participle (activity) - gerund
221
+ if (base.endsWith('e') && !base.endsWith('ee')) {
222
+ activity = base.slice(0, -1) + 'ing'
223
+ } else if (base.endsWith('ie')) {
224
+ activity = base.slice(0, -2) + 'ying'
225
+ } else if (/[^aeiou][aeiou][bcdfghlmnprstvwz]$/.test(base) && base.length <= 6) {
226
+ // Double final consonant for short words (CVC pattern)
227
+ activity = base + base[base.length - 1] + 'ing'
228
+ } else {
229
+ activity = base + 'ing'
230
+ }
231
+
232
+ // Past participle (event)
233
+ if (base.endsWith('e')) {
234
+ event = base + 'd'
235
+ } else if (base.endsWith('y') && !/[aeiou]y$/.test(base)) {
236
+ event = base.slice(0, -1) + 'ied'
237
+ } else if (/[^aeiou][aeiou][bcdfghlmnprstvwz]$/.test(base) && base.length <= 6) {
238
+ // Double final consonant for short words
239
+ event = base + base[base.length - 1] + 'ed'
240
+ } else {
241
+ event = base + 'ed'
242
+ }
243
+
244
+ return {
245
+ action: base,
246
+ act,
247
+ activity,
248
+ event,
249
+ reverseBy: `${event}By`,
250
+ reverseAt: `${event}At`,
251
+ reverseIn: `${event}In`,
252
+ }
253
+ }