@c9up/ream 0.1.31 → 0.1.33

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 (189) hide show
  1. package/README.md +4 -1
  2. package/dist/Application.d.ts +27 -0
  3. package/dist/Application.d.ts.map +1 -1
  4. package/dist/Application.js +29 -0
  5. package/dist/Application.js.map +1 -1
  6. package/dist/ErrorBoundary.d.ts.map +1 -1
  7. package/dist/ErrorBoundary.js +13 -1
  8. package/dist/ErrorBoundary.js.map +1 -1
  9. package/dist/HttpKernel.d.ts +8 -0
  10. package/dist/HttpKernel.d.ts.map +1 -1
  11. package/dist/HttpKernel.js +35 -12
  12. package/dist/HttpKernel.js.map +1 -1
  13. package/dist/Ignitor.d.ts +8 -0
  14. package/dist/Ignitor.d.ts.map +1 -1
  15. package/dist/Ignitor.js +39 -0
  16. package/dist/Ignitor.js.map +1 -1
  17. package/dist/bodyparser/BodyParserMiddleware.d.ts +11 -4
  18. package/dist/bodyparser/BodyParserMiddleware.d.ts.map +1 -1
  19. package/dist/bodyparser/BodyParserMiddleware.js +55 -43
  20. package/dist/bodyparser/BodyParserMiddleware.js.map +1 -1
  21. package/dist/bodyparser/qsParse.d.ts +52 -0
  22. package/dist/bodyparser/qsParse.d.ts.map +1 -0
  23. package/dist/bodyparser/qsParse.js +161 -0
  24. package/dist/bodyparser/qsParse.js.map +1 -0
  25. package/dist/bootstrap/hyper-server.d.ts.map +1 -1
  26. package/dist/bootstrap/hyper-server.js +2 -8
  27. package/dist/bootstrap/hyper-server.js.map +1 -1
  28. package/dist/container/Container.d.ts +41 -0
  29. package/dist/container/Container.d.ts.map +1 -1
  30. package/dist/container/Container.js +193 -26
  31. package/dist/container/Container.js.map +1 -1
  32. package/dist/container/ContainerResolver.d.ts +46 -0
  33. package/dist/container/ContainerResolver.d.ts.map +1 -0
  34. package/dist/container/ContainerResolver.js +60 -0
  35. package/dist/container/ContainerResolver.js.map +1 -0
  36. package/dist/container/ContextualBindingsBuilder.d.ts +20 -0
  37. package/dist/container/ContextualBindingsBuilder.d.ts.map +1 -0
  38. package/dist/container/ContextualBindingsBuilder.js +31 -0
  39. package/dist/container/ContextualBindingsBuilder.js.map +1 -0
  40. package/dist/container/types.d.ts +9 -6
  41. package/dist/container/types.d.ts.map +1 -1
  42. package/dist/decorators/Service.d.ts +1 -5
  43. package/dist/decorators/Service.d.ts.map +1 -1
  44. package/dist/decorators/Service.js +10 -1
  45. package/dist/decorators/Service.js.map +1 -1
  46. package/dist/events/Emitter.d.ts +66 -6
  47. package/dist/events/Emitter.d.ts.map +1 -1
  48. package/dist/events/Emitter.js +165 -28
  49. package/dist/events/Emitter.js.map +1 -1
  50. package/dist/graphql/GraphQLEngine.d.ts.map +1 -1
  51. package/dist/graphql/GraphQLEngine.js +5 -5
  52. package/dist/graphql/GraphQLEngine.js.map +1 -1
  53. package/dist/helpers/duration.d.ts +32 -0
  54. package/dist/helpers/duration.d.ts.map +1 -0
  55. package/dist/helpers/duration.js +74 -0
  56. package/dist/helpers/duration.js.map +1 -0
  57. package/dist/helpers/napi-loader.d.ts +10 -1
  58. package/dist/helpers/napi-loader.d.ts.map +1 -1
  59. package/dist/helpers/napi-loader.js +18 -3
  60. package/dist/helpers/napi-loader.js.map +1 -1
  61. package/dist/helpers/types.d.ts +1 -1
  62. package/dist/helpers/types.d.ts.map +1 -1
  63. package/dist/http/Exception.d.ts.map +1 -1
  64. package/dist/http/Exception.js +17 -5
  65. package/dist/http/Exception.js.map +1 -1
  66. package/dist/http/HttpContext.d.ts +16 -5
  67. package/dist/http/HttpContext.d.ts.map +1 -1
  68. package/dist/http/HttpContext.js +4 -2
  69. package/dist/http/HttpContext.js.map +1 -1
  70. package/dist/http/RedirectBuilder.d.ts +48 -2
  71. package/dist/http/RedirectBuilder.d.ts.map +1 -1
  72. package/dist/http/RedirectBuilder.js +95 -14
  73. package/dist/http/RedirectBuilder.js.map +1 -1
  74. package/dist/http/Request.d.ts +128 -8
  75. package/dist/http/Request.d.ts.map +1 -1
  76. package/dist/http/Request.js +259 -8
  77. package/dist/http/Request.js.map +1 -1
  78. package/dist/http/Response.d.ts +74 -3
  79. package/dist/http/Response.d.ts.map +1 -1
  80. package/dist/http/Response.js +177 -4
  81. package/dist/http/Response.js.map +1 -1
  82. package/dist/index.d.ts +9 -3
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +5 -1
  85. package/dist/index.js.map +1 -1
  86. package/dist/router/Router.d.ts +36 -7
  87. package/dist/router/Router.d.ts.map +1 -1
  88. package/dist/router/Router.js +66 -22
  89. package/dist/router/Router.js.map +1 -1
  90. package/dist/security/CookieSigner.d.ts +35 -4
  91. package/dist/security/CookieSigner.d.ts.map +1 -1
  92. package/dist/security/CookieSigner.js +99 -10
  93. package/dist/security/CookieSigner.js.map +1 -1
  94. package/dist/server/Server.d.ts +2 -3
  95. package/dist/server/Server.d.ts.map +1 -1
  96. package/dist/server/Server.js +30 -13
  97. package/dist/server/Server.js.map +1 -1
  98. package/dist/session/ReadOnlyValuesStore.d.ts +24 -0
  99. package/dist/session/ReadOnlyValuesStore.d.ts.map +1 -0
  100. package/dist/session/ReadOnlyValuesStore.js +45 -0
  101. package/dist/session/ReadOnlyValuesStore.js.map +1 -0
  102. package/dist/session/Session.d.ts +181 -9
  103. package/dist/session/Session.d.ts.map +1 -1
  104. package/dist/session/Session.js +284 -33
  105. package/dist/session/Session.js.map +1 -1
  106. package/dist/session/SessionMiddleware.d.ts.map +1 -1
  107. package/dist/session/SessionMiddleware.js +87 -33
  108. package/dist/session/SessionMiddleware.js.map +1 -1
  109. package/dist/session/drivers/DatabaseDriver.d.ts +66 -0
  110. package/dist/session/drivers/DatabaseDriver.d.ts.map +1 -0
  111. package/dist/session/drivers/DatabaseDriver.js +132 -0
  112. package/dist/session/drivers/DatabaseDriver.js.map +1 -0
  113. package/dist/session/drivers/FileDriver.d.ts +22 -0
  114. package/dist/session/drivers/FileDriver.d.ts.map +1 -0
  115. package/dist/session/drivers/FileDriver.js +66 -0
  116. package/dist/session/drivers/FileDriver.js.map +1 -0
  117. package/dist/session/drivers/MemoryDriver.d.ts +10 -2
  118. package/dist/session/drivers/MemoryDriver.d.ts.map +1 -1
  119. package/dist/session/drivers/MemoryDriver.js +70 -0
  120. package/dist/session/drivers/MemoryDriver.js.map +1 -1
  121. package/dist/session/drivers/RedisDriver.d.ts +10 -2
  122. package/dist/session/drivers/RedisDriver.d.ts.map +1 -1
  123. package/dist/session/drivers/RedisDriver.js +58 -0
  124. package/dist/session/drivers/RedisDriver.js.map +1 -1
  125. package/dist/session/templateTags.d.ts +42 -0
  126. package/dist/session/templateTags.d.ts.map +1 -0
  127. package/dist/session/templateTags.js +101 -0
  128. package/dist/session/templateTags.js.map +1 -0
  129. package/dist/testing/RequestBuilder.d.ts +38 -1
  130. package/dist/testing/RequestBuilder.d.ts.map +1 -1
  131. package/dist/testing/RequestBuilder.js +40 -1
  132. package/dist/testing/RequestBuilder.js.map +1 -1
  133. package/dist/testing/TestClient.d.ts +3 -1
  134. package/dist/testing/TestClient.d.ts.map +1 -1
  135. package/dist/testing/TestClient.js +3 -1
  136. package/dist/testing/TestClient.js.map +1 -1
  137. package/dist/types/helpers.d.ts +1 -1
  138. package/dist/types/helpers.d.ts.map +1 -1
  139. package/dist/utils/Macroable.d.ts +14 -5
  140. package/dist/utils/Macroable.d.ts.map +1 -1
  141. package/dist/utils/Macroable.js +20 -18
  142. package/dist/utils/Macroable.js.map +1 -1
  143. package/events.win32-x64-msvc.node +0 -0
  144. package/index.win32-x64-msvc.node +0 -0
  145. package/package.json +1 -1
  146. package/scheduler.win32-x64-msvc.node +0 -0
  147. package/src/Application.ts +39 -0
  148. package/src/ErrorBoundary.ts +13 -1
  149. package/src/HttpKernel.ts +45 -19
  150. package/src/Ignitor.ts +39 -0
  151. package/src/bodyparser/BodyParserMiddleware.ts +66 -44
  152. package/src/bodyparser/qsParse.ts +179 -0
  153. package/src/bootstrap/hyper-server.ts +2 -9
  154. package/src/container/Container.ts +265 -25
  155. package/src/container/ContainerResolver.ts +84 -0
  156. package/src/container/ContextualBindingsBuilder.ts +41 -0
  157. package/src/container/types.ts +11 -7
  158. package/src/decorators/Service.ts +16 -3
  159. package/src/events/Emitter.ts +200 -25
  160. package/src/graphql/GraphQLEngine.ts +7 -8
  161. package/src/helpers/duration.ts +71 -0
  162. package/src/helpers/napi-loader.ts +19 -3
  163. package/src/helpers/types.ts +4 -1
  164. package/src/http/Exception.ts +17 -5
  165. package/src/http/HttpContext.ts +20 -5
  166. package/src/http/RedirectBuilder.ts +126 -11
  167. package/src/http/Request.ts +283 -13
  168. package/src/http/Response.ts +213 -6
  169. package/src/index.ts +17 -3
  170. package/src/router/Router.ts +98 -33
  171. package/src/security/CookieSigner.ts +102 -10
  172. package/src/server/Server.ts +36 -16
  173. package/src/session/ReadOnlyValuesStore.ts +52 -0
  174. package/src/session/Session.ts +364 -37
  175. package/src/session/SessionMiddleware.ts +93 -30
  176. package/src/session/drivers/DatabaseDriver.ts +184 -0
  177. package/src/session/drivers/FileDriver.ts +82 -0
  178. package/src/session/drivers/MemoryDriver.ts +75 -2
  179. package/src/session/drivers/RedisDriver.ts +77 -2
  180. package/src/session/templateTags.ts +124 -0
  181. package/src/testing/RequestBuilder.ts +76 -0
  182. package/src/testing/TestClient.ts +10 -2
  183. package/src/types/helpers.ts +4 -1
  184. package/src/utils/Macroable.ts +23 -8
  185. package/dist/utils/inflect.d.ts +0 -17
  186. package/dist/utils/inflect.d.ts.map +0 -1
  187. package/dist/utils/inflect.js +0 -32
  188. package/dist/utils/inflect.js.map +0 -1
  189. package/src/utils/inflect.ts +0 -29
@@ -46,6 +46,35 @@ export interface AuthSubject {
46
46
  extraHeaders?: Record<string, string>
47
47
  }
48
48
 
49
+ /**
50
+ * What a guard answers when asked to authenticate a test client
51
+ * (AdonisJS `AuthClientResponse`).
52
+ */
53
+ export interface ClientAuthResponse {
54
+ headers?: Record<string, string>
55
+ session?: Record<string, unknown>
56
+ cookies?: Record<string, string>
57
+ }
58
+
59
+ /**
60
+ * The little `loginAs()` needs of a guard.
61
+ *
62
+ * Structurally typed: the test client must not import warden, and an app with
63
+ * its own guard satisfies this by exposing the same method.
64
+ */
65
+ export interface ClientAuthenticatable {
66
+ authenticateAsClient(...args: never[]): ClientAuthResponse | Promise<ClientAuthResponse>
67
+ }
68
+
69
+ /**
70
+ * Persists session values for a test request and returns the cookie that
71
+ * addresses them. Given the session driver, it writes an entry and hands back
72
+ * `{ <cookieName>: <sessionId> }`.
73
+ */
74
+ export type SessionSeeder = (
75
+ values: Record<string, unknown>,
76
+ ) => Record<string, string> | Promise<Record<string, string>>
77
+
49
78
  export interface AuthStrategy {
50
79
  /** Compute the headers Warden expects for this user (Bearer token / session cookie / ...). */
51
80
  headersFor(subject: AuthSubject): Record<string, string> | Promise<Record<string, string>>
@@ -156,7 +185,12 @@ export class RequestBuilder {
156
185
  #cookies: Record<string, string> = {}
157
186
  #query = new URLSearchParams()
158
187
  #authStrategy: AuthStrategy | null
188
+ #sessionSeeder: SessionSeeder | null
159
189
  #pendingAuth: AuthSubject | null = null
190
+ #pendingClientAuth: {
191
+ guard: ClientAuthenticatable
192
+ args: never[]
193
+ } | null = null
160
194
  #timeoutMs: number | undefined
161
195
  #maxRedirects = DEFAULT_MAX_REDIRECTS
162
196
  #sent: Promise<ApiResponse> | null = null
@@ -170,11 +204,13 @@ export class RequestBuilder {
170
204
  method: HttpMethod,
171
205
  path: string,
172
206
  authStrategy: AuthStrategy | null = null,
207
+ sessionSeeder: SessionSeeder | null = null,
173
208
  ) {
174
209
  this.#sender = sender
175
210
  this.#method = method
176
211
  this.#path = path
177
212
  this.#authStrategy = authStrategy
213
+ this.#sessionSeeder = sessionSeeder
178
214
  applyRequestExtensions(this)
179
215
  }
180
216
 
@@ -421,6 +457,23 @@ export class RequestBuilder {
421
457
  return this
422
458
  }
423
459
 
460
+ /**
461
+ * Send this request as an authenticated user (AdonisJS `loginAs`).
462
+ *
463
+ * await client.get('/dashboard').loginAs(sessionGuard, user)
464
+ * await client.get('/api/me').loginAs(jwtGuard, user)
465
+ * await client.get('/admin').loginAs(basicGuard, 'ada', 'secret')
466
+ *
467
+ * The GUARD answers what to send — a header, a session entry, a cookie — so
468
+ * the test never reproduces how that guard authenticates. A test that forges
469
+ * its own header proves only that the forgery works; this one travels the
470
+ * same verification path as production.
471
+ */
472
+ loginAs(guard: ClientAuthenticatable, ...args: never[]): this {
473
+ this.#pendingClientAuth = { guard, args }
474
+ return this
475
+ }
476
+
424
477
  /** Fire the request (once) and return the rich response. Idempotent. */
425
478
  send(): Promise<ApiResponse> {
426
479
  if (this.#sent === null) this.#sent = this.#execute()
@@ -560,6 +613,29 @@ export class RequestBuilder {
560
613
  }
561
614
  }
562
615
 
616
+ if (this.#pendingClientAuth !== null) {
617
+ const { guard, args } = this.#pendingClientAuth
618
+ const answer = await guard.authenticateAsClient(...args)
619
+ for (const [k, v] of Object.entries(answer.headers ?? {})) {
620
+ this.#headers[k.toLowerCase()] = v
621
+ }
622
+ Object.assign(this.#cookies, answer.cookies ?? {})
623
+ // A session guard answers with session VALUES, which only something
624
+ // holding the session driver can persist. Without a seeder the request
625
+ // would go out unauthenticated and the test would fail somewhere far from
626
+ // the cause — so say it here instead.
627
+ if (answer.session && Object.keys(answer.session).length > 0) {
628
+ if (!this.#sessionSeeder) {
629
+ throw new Error(
630
+ 'RequestBuilder: `loginAs()` was given a session-based guard, which needs a session to be written before the request. ' +
631
+ 'Pass `sessionSeeder` in the client options (it receives the values and returns the session cookie).',
632
+ )
633
+ }
634
+ const cookie = await this.#sessionSeeder(answer.session)
635
+ Object.assign(this.#cookies, cookie)
636
+ }
637
+ }
638
+
563
639
  // Multipart parts (.field()/.file()) take precedence over any body set by
564
640
  // json()/form()/body(); encode them with a fresh boundary at send time.
565
641
  if (this.#multipart.length > 0) {
@@ -23,6 +23,7 @@ import {
23
23
  type HttpMethod,
24
24
  type HttpSender,
25
25
  RequestBuilder,
26
+ type SessionSeeder,
26
27
  type TestResponse,
27
28
  } from './RequestBuilder.js'
28
29
 
@@ -47,15 +48,22 @@ export class TestClient {
47
48
  #headers: Dict = {}
48
49
  #bootFn: (port: number) => Promise<{ port: number; close: () => Promise<void> | void }>
49
50
  #auth: AuthStrategy | null
51
+ #sessionSeeder: SessionSeeder | null
50
52
  #routes: RouteManifest | null
51
53
 
52
54
  constructor(
53
55
  bootFn: (port: number) => Promise<{ port: number; close: () => Promise<void> | void }>,
54
- options: { auth?: AuthStrategy; routes?: RouteManifest } = {},
56
+ options: {
57
+ auth?: AuthStrategy
58
+ routes?: RouteManifest
59
+ /** Writes session values for `loginAs()` with a session-based guard. */
60
+ sessionSeeder?: SessionSeeder
61
+ } = {},
55
62
  ) {
56
63
  this.#bootFn = bootFn
57
64
  this.#auth = options.auth ?? null
58
65
  this.#routes = options.routes ?? null
66
+ this.#sessionSeeder = options.sessionSeeder ?? null
59
67
  }
60
68
 
61
69
  #server: { port: number; close: () => Promise<void> | void } | null = null
@@ -149,7 +157,7 @@ export class TestClient {
149
157
  init.body.toString('utf8'),
150
158
  init.timeoutMs,
151
159
  )
152
- return new RequestBuilder(sender, method, path, this.#auth)
160
+ return new RequestBuilder(sender, method, path, this.#auth, this.#sessionSeeder)
153
161
  }
154
162
 
155
163
  /**
@@ -28,7 +28,10 @@ export type UnwrapOpaque<T> = T extends Opaque<infer Base, string> ? Base : T
28
28
  export type AsyncOrSync<T> = T | Promise<T>
29
29
 
30
30
  /** Constructor type. */
31
- export type Constructor<T = unknown, Args extends unknown[] = unknown[]> = new (...args: Args) => T
31
+ // The default `Args` is `never[]`, not `unknown[]`: parameters are
32
+ // contravariant, so only a rest of `never` accepts EVERY concrete parameter
33
+ // list. Pass `Args` explicitly when you need to call the constructor.
34
+ export type Constructor<T = unknown, Args extends unknown[] = never[]> = new (...args: Args) => T
32
35
 
33
36
  /** Abstract constructor type. */
34
37
  export type AbstractConstructor<T = unknown> = abstract new (...args: unknown[]) => T
@@ -1,3 +1,9 @@
1
+ // biome-ignore-all lint/complexity/noThisInStatic: `this` inside a static method
2
+ // IS the calling class, and that is the whole mechanism here — `this.prototype`
3
+ // is what scopes a macro to the subclass that declared it. Using
4
+ // `Macroable.prototype` put every macro on all eight subclasses at once.
5
+ // Removing the `this` reintroduces that bug.
6
+
1
7
  /**
2
8
  * Macroable — lets a package extend a class at runtime with extra methods
3
9
  * (`macro`) and computed properties (`getter`), AdonisJS-style. A framework
@@ -22,9 +28,17 @@ export type MacroFn = (...args: unknown[]) => unknown
22
28
  export type GetterFn = () => unknown
23
29
 
24
30
  export class Macroable {
25
- /** Attach a method to every instance of this class (AdonisJS `macro`). */
26
- static macro(name: string, fn: MacroFn): void {
27
- Object.defineProperty(Macroable.prototype, name, {
31
+ /**
32
+ * Attach a method to every instance of THIS class (AdonisJS `macro`).
33
+ *
34
+ * `this.prototype`, not `Macroable.prototype`: eight classes extend this one,
35
+ * and defining on the base put every macro on all of them — a
36
+ * `Response.macro('json', …)` also landed on `Request`, `Router` and
37
+ * `HttpContext`, and two subclasses declaring the same name clobbered each
38
+ * other silently.
39
+ */
40
+ static macro(this: typeof Macroable, name: string, fn: MacroFn): void {
41
+ Object.defineProperty(this.prototype, name, {
28
42
  value: fn,
29
43
  writable: true,
30
44
  configurable: true,
@@ -32,16 +46,17 @@ export class Macroable {
32
46
  }
33
47
 
34
48
  /**
35
- * Attach a computed property to every instance (AdonisJS `getter`). With
36
- * `singleton`, the value is computed once per instance and cached.
49
+ * Attach a computed property to every instance of THIS class (AdonisJS
50
+ * `getter`). With `singleton`, the value is computed once per instance and
51
+ * cached.
37
52
  */
38
- static getter(name: string, fn: GetterFn, singleton = false): void {
53
+ static getter(this: typeof Macroable, name: string, fn: GetterFn, singleton = false): void {
39
54
  if (!singleton) {
40
- Object.defineProperty(Macroable.prototype, name, { get: fn, configurable: true })
55
+ Object.defineProperty(this.prototype, name, { get: fn, configurable: true })
41
56
  return
42
57
  }
43
58
  const cacheKey = Symbol(name)
44
- Object.defineProperty(Macroable.prototype, name, {
59
+ Object.defineProperty(this.prototype, name, {
45
60
  configurable: true,
46
61
  get(this: Record<symbol, unknown>) {
47
62
  if (!(cacheKey in this)) {
@@ -1,17 +0,0 @@
1
- /**
2
- * Minimal zero-dependency string inflection — the two operations resourceful
3
- * routing needs from AdonisJS's `@poppinss/utils/string`: `snakeCase` (route
4
- * name normalisation) and `singular` (nested-resource param naming, `photos`
5
- * → `photo_id`).
6
- *
7
- * Deviation from AdonisJS (named): `singular` covers regular English plurals
8
- * only (`-ies→-y`, `-(s|x|z|ch|sh)es→drop es`, trailing `-s→drop`). Irregular
9
- * plurals (`people→person`, `geese→goose`) are NOT handled — resource names in
10
- * REST APIs are overwhelmingly regular, and a full inflector (irregular tables,
11
- * uncountables) would be a dependency-sized rabbit hole for a niche case.
12
- */
13
- /** Convert `camelCase`/`PascalCase`/`kebab-case`/spaced text to `snake_case`. */
14
- export declare function snakeCase(value: string): string;
15
- /** Singularize a regular English plural (see module deviation note). */
16
- export declare function singular(word: string): string;
17
- //# sourceMappingURL=inflect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inflect.d.ts","sourceRoot":"","sources":["../../src/utils/inflect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM7C"}
@@ -1,32 +0,0 @@
1
- /**
2
- * Minimal zero-dependency string inflection — the two operations resourceful
3
- * routing needs from AdonisJS's `@poppinss/utils/string`: `snakeCase` (route
4
- * name normalisation) and `singular` (nested-resource param naming, `photos`
5
- * → `photo_id`).
6
- *
7
- * Deviation from AdonisJS (named): `singular` covers regular English plurals
8
- * only (`-ies→-y`, `-(s|x|z|ch|sh)es→drop es`, trailing `-s→drop`). Irregular
9
- * plurals (`people→person`, `geese→goose`) are NOT handled — resource names in
10
- * REST APIs are overwhelmingly regular, and a full inflector (irregular tables,
11
- * uncountables) would be a dependency-sized rabbit hole for a niche case.
12
- */
13
- /** Convert `camelCase`/`PascalCase`/`kebab-case`/spaced text to `snake_case`. */
14
- export function snakeCase(value) {
15
- return value
16
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
17
- .replace(/[-\s]+/g, '_')
18
- .toLowerCase();
19
- }
20
- /** Singularize a regular English plural (see module deviation note). */
21
- export function singular(word) {
22
- if (word.length > 3 && /ies$/i.test(word))
23
- return word.replace(/ies$/i, 'y');
24
- if (/(?:s|x|z|ch|sh)es$/i.test(word))
25
- return word.replace(/es$/i, '');
26
- if (/ss$/i.test(word))
27
- return word;
28
- if (/s$/i.test(word))
29
- return word.replace(/s$/i, '');
30
- return word;
31
- }
32
- //# sourceMappingURL=inflect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inflect.js","sourceRoot":"","sources":["../../src/utils/inflect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iFAAiF;AACjF,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,KAAK;SACT,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,WAAW,EAAE,CAAA;AAClB,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IAC5E,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACrE,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAClC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACpD,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -1,29 +0,0 @@
1
- /**
2
- * Minimal zero-dependency string inflection — the two operations resourceful
3
- * routing needs from AdonisJS's `@poppinss/utils/string`: `snakeCase` (route
4
- * name normalisation) and `singular` (nested-resource param naming, `photos`
5
- * → `photo_id`).
6
- *
7
- * Deviation from AdonisJS (named): `singular` covers regular English plurals
8
- * only (`-ies→-y`, `-(s|x|z|ch|sh)es→drop es`, trailing `-s→drop`). Irregular
9
- * plurals (`people→person`, `geese→goose`) are NOT handled — resource names in
10
- * REST APIs are overwhelmingly regular, and a full inflector (irregular tables,
11
- * uncountables) would be a dependency-sized rabbit hole for a niche case.
12
- */
13
-
14
- /** Convert `camelCase`/`PascalCase`/`kebab-case`/spaced text to `snake_case`. */
15
- export function snakeCase(value: string): string {
16
- return value
17
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
18
- .replace(/[-\s]+/g, '_')
19
- .toLowerCase()
20
- }
21
-
22
- /** Singularize a regular English plural (see module deviation note). */
23
- export function singular(word: string): string {
24
- if (word.length > 3 && /ies$/i.test(word)) return word.replace(/ies$/i, 'y')
25
- if (/(?:s|x|z|ch|sh)es$/i.test(word)) return word.replace(/es$/i, '')
26
- if (/ss$/i.test(word)) return word
27
- if (/s$/i.test(word)) return word.replace(/s$/i, '')
28
- return word
29
- }