@holoyan/adonisjs-polymorphic 0.1.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 (75) hide show
  1. package/README.md +549 -0
  2. package/build/configure.d.ts +10 -0
  3. package/build/configure.d.ts.map +1 -0
  4. package/build/configure.js +27 -0
  5. package/build/configure.js.map +1 -0
  6. package/build/providers/plugin_provider.d.ts +15 -0
  7. package/build/providers/plugin_provider.d.ts.map +1 -0
  8. package/build/providers/plugin_provider.js +21 -0
  9. package/build/providers/plugin_provider.js.map +1 -0
  10. package/build/src/decorators.d.ts +41 -0
  11. package/build/src/decorators.d.ts.map +1 -0
  12. package/build/src/decorators.js +64 -0
  13. package/build/src/decorators.js.map +1 -0
  14. package/build/src/define_config.d.ts +20 -0
  15. package/build/src/define_config.d.ts.map +1 -0
  16. package/build/src/define_config.js +8 -0
  17. package/build/src/define_config.js.map +1 -0
  18. package/build/src/index.d.ts +16 -0
  19. package/build/src/index.d.ts.map +1 -0
  20. package/build/src/index.js +15 -0
  21. package/build/src/index.js.map +1 -0
  22. package/build/src/relations/morph_many/index.d.ts +39 -0
  23. package/build/src/relations/morph_many/index.d.ts.map +1 -0
  24. package/build/src/relations/morph_many/index.js +110 -0
  25. package/build/src/relations/morph_many/index.js.map +1 -0
  26. package/build/src/relations/morph_many/query_builder.d.ts +22 -0
  27. package/build/src/relations/morph_many/query_builder.d.ts.map +1 -0
  28. package/build/src/relations/morph_many/query_builder.js +80 -0
  29. package/build/src/relations/morph_many/query_builder.js.map +1 -0
  30. package/build/src/relations/morph_many/query_client.d.ts +40 -0
  31. package/build/src/relations/morph_many/query_client.d.ts.map +1 -0
  32. package/build/src/relations/morph_many/query_client.js +115 -0
  33. package/build/src/relations/morph_many/query_client.js.map +1 -0
  34. package/build/src/relations/morph_one/index.d.ts +68 -0
  35. package/build/src/relations/morph_one/index.d.ts.map +1 -0
  36. package/build/src/relations/morph_one/index.js +147 -0
  37. package/build/src/relations/morph_one/index.js.map +1 -0
  38. package/build/src/relations/morph_one/query_builder.d.ts +22 -0
  39. package/build/src/relations/morph_one/query_builder.d.ts.map +1 -0
  40. package/build/src/relations/morph_one/query_builder.js +79 -0
  41. package/build/src/relations/morph_one/query_builder.js.map +1 -0
  42. package/build/src/relations/morph_one/query_client.d.ts +41 -0
  43. package/build/src/relations/morph_one/query_client.d.ts.map +1 -0
  44. package/build/src/relations/morph_one/query_client.js +94 -0
  45. package/build/src/relations/morph_one/query_client.js.map +1 -0
  46. package/build/src/relations/morph_to/eager_loader.d.ts +29 -0
  47. package/build/src/relations/morph_to/eager_loader.d.ts.map +1 -0
  48. package/build/src/relations/morph_to/eager_loader.js +78 -0
  49. package/build/src/relations/morph_to/eager_loader.js.map +1 -0
  50. package/build/src/relations/morph_to/index.d.ts +76 -0
  51. package/build/src/relations/morph_to/index.d.ts.map +1 -0
  52. package/build/src/relations/morph_to/index.js +172 -0
  53. package/build/src/relations/morph_to/index.js.map +1 -0
  54. package/build/src/relations/morph_to/query_client.d.ts +27 -0
  55. package/build/src/relations/morph_to/query_client.d.ts.map +1 -0
  56. package/build/src/relations/morph_to/query_client.js +64 -0
  57. package/build/src/relations/morph_to/query_client.js.map +1 -0
  58. package/build/src/relations/morph_to/registry.d.ts +2 -0
  59. package/build/src/relations/morph_to/registry.d.ts.map +1 -0
  60. package/build/src/relations/morph_to/registry.js +13 -0
  61. package/build/src/relations/morph_to/registry.js.map +1 -0
  62. package/build/src/relations/shared/query_builder.d.ts +55 -0
  63. package/build/src/relations/shared/query_builder.d.ts.map +1 -0
  64. package/build/src/relations/shared/query_builder.js +70 -0
  65. package/build/src/relations/shared/query_builder.js.map +1 -0
  66. package/build/src/types.d.ts +84 -0
  67. package/build/src/types.d.ts.map +1 -0
  68. package/build/src/types.js +2 -0
  69. package/build/src/types.js.map +1 -0
  70. package/build/stubs/config/polymorphic.stub +19 -0
  71. package/build/stubs/main.d.ts +6 -0
  72. package/build/stubs/main.d.ts.map +1 -0
  73. package/build/stubs/main.js +6 -0
  74. package/build/stubs/main.js.map +1 -0
  75. package/package.json +81 -0
package/README.md ADDED
@@ -0,0 +1,549 @@
1
+ # @holoyan/adonisjs-polymorphic
2
+
3
+ Polymorphic relations for [AdonisJS Lucid ORM](https://lucid.adonisjs.com) — `morphOne`, `morphMany`, and `morphTo`.
4
+
5
+ | Package version | AdonisJS version |
6
+ |---|---|
7
+ | v0.x | v6 + v7 |
8
+
9
+ ---
10
+
11
+ ## How can you support me?
12
+
13
+ It's simple — just star this repository. That is enough to keep me motivated to maintain this package.
14
+
15
+ ---
16
+
17
+ ## Related packages
18
+
19
+ - [@holoyan/adonisjs-permissions](https://github.com/holoyan/adonisjs-permissions) — Role & permission system for AdonisJS. Supports multi-model ACL, resource-level permissions, scopes (multi-tenancy), and events.
20
+ - [@holoyan/morph-map-js](https://github.com/holoyan/morph-map-js) — The framework-agnostic morph map registry that powers the `@MorphMap` decorator used by this package.
21
+
22
+ ---
23
+
24
+ ## Table of Contents
25
+
26
+ - [Installation](#installation)
27
+ - [What are polymorphic relations?](#what-are-polymorphic-relations)
28
+ - [morphOne](#morphone)
29
+ - [morphMany](#morphmany)
30
+ - [morphTo](#morphto)
31
+ - [Global morph map with @MorphMap](#global-morph-map-with-morphmap)
32
+ - [Options reference](#options-reference)
33
+
34
+ ---
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ npm install @holoyan/adonisjs-polymorphic
40
+ ```
41
+
42
+ Register the service provider by running the configure command:
43
+
44
+ ```bash
45
+ node ace configure @holoyan/adonisjs-polymorphic
46
+ ```
47
+
48
+ This automatically adds the provider to your `adonisrc.ts`.
49
+
50
+ ---
51
+
52
+ ## What are polymorphic relations?
53
+
54
+ A polymorphic relation lets a single child model belong to more than one parent model using a shared pair of columns — a **type** column and an **id** column.
55
+
56
+ ```
57
+ images
58
+ id
59
+ url
60
+ imageable_type ← 'posts' | 'videos'
61
+ imageable_id ← id of the parent row
62
+ ```
63
+
64
+ This means a single `images` table can store thumbnails for both posts and videos without needing separate `post_images` and `video_images` tables.
65
+
66
+ ---
67
+
68
+ ## morphOne
69
+
70
+ A parent model **has one** polymorphic child.
71
+
72
+ ### Database migration
73
+
74
+ ```ts
75
+ await schema.createTable('images', (table) => {
76
+ table.increments('id')
77
+ table.string('url').notNullable()
78
+ table.string('imageable_type').notNullable()
79
+ table.integer('imageable_id').notNullable()
80
+ table.index(['imageable_type', 'imageable_id'])
81
+ })
82
+ ```
83
+
84
+ ### Model setup
85
+
86
+ ```ts
87
+ // app/models/image.ts
88
+ import { BaseModel, column } from '@adonisjs/lucid/orm'
89
+ import { morphTo } from '@holoyan/adonisjs-polymorphic'
90
+ import Post from '#models/post'
91
+ import Video from '#models/video'
92
+
93
+ export default class Image extends BaseModel {
94
+ @column({ isPrimary: true })
95
+ declare id: number
96
+
97
+ @column()
98
+ declare url: string
99
+
100
+ @column()
101
+ declare imageableType: string
102
+
103
+ @column()
104
+ declare imageableId: number
105
+
106
+ @morphTo({ name: 'imageable', morphMap: { posts: () => Post, videos: () => Video } })
107
+ declare imageable: Post | Video | null
108
+ }
109
+ ```
110
+
111
+ ```ts
112
+ // app/models/post.ts
113
+ import { BaseModel, column } from '@adonisjs/lucid/orm'
114
+ import { morphOne } from '@holoyan/adonisjs-polymorphic'
115
+ import Image from '#models/image'
116
+
117
+ export default class Post extends BaseModel {
118
+ @column({ isPrimary: true })
119
+ declare id: number
120
+
121
+ @column()
122
+ declare title: string
123
+
124
+ @morphOne(() => Image, { name: 'imageable' })
125
+ declare image: Image | null
126
+ }
127
+ ```
128
+
129
+ ```ts
130
+ // app/models/video.ts
131
+ import { BaseModel, column } from '@adonisjs/lucid/orm'
132
+ import { morphOne } from '@holoyan/adonisjs-polymorphic'
133
+ import Image from '#models/image'
134
+
135
+ export default class Video extends BaseModel {
136
+ @column({ isPrimary: true })
137
+ declare id: number
138
+
139
+ @column()
140
+ declare title: string
141
+
142
+ @morphOne(() => Image, { name: 'imageable' })
143
+ declare image: Image | null
144
+ }
145
+ ```
146
+
147
+ ### Querying
148
+
149
+ **Eager load (preload):**
150
+
151
+ ```ts
152
+ const post = await (Post.query() as any)
153
+ .preload('image')
154
+ .firstOrFail()
155
+
156
+ console.log(post.image) // Image | null
157
+ console.log(post.image?.url) // 'photo.jpg'
158
+ ```
159
+
160
+ **Preload multiple parents at once:**
161
+
162
+ ```ts
163
+ const posts = await (Post.query() as any)
164
+ .preload('image') as Post[]
165
+
166
+ // One SQL query — no N+1
167
+ // SELECT * FROM images WHERE imageable_type = 'posts' AND imageable_id IN (1, 2, 3)
168
+ ```
169
+
170
+ **Ad-hoc query:**
171
+
172
+ ```ts
173
+ const image = await (post as any)
174
+ .related('image')
175
+ .query()
176
+ .firstOrFail()
177
+ ```
178
+
179
+ ### Writing
180
+
181
+ **Create a related image:**
182
+
183
+ ```ts
184
+ // imageableType and imageableId are set automatically
185
+ const image = await (post as any)
186
+ .related('image')
187
+ .create({ url: 'photo.jpg' })
188
+ ```
189
+
190
+ **Save an existing image instance:**
191
+
192
+ ```ts
193
+ const image = new Image()
194
+ image.url = 'photo.jpg'
195
+
196
+ await (post as any).related('image').save(image)
197
+ ```
198
+
199
+ **Find or create:**
200
+
201
+ ```ts
202
+ const image = await (post as any)
203
+ .related('image')
204
+ .firstOrCreate({ url: 'photo.jpg' })
205
+ ```
206
+
207
+ **Update or create:**
208
+
209
+ ```ts
210
+ const image = await (post as any)
211
+ .related('image')
212
+ .updateOrCreate({ imageableId: post.id }, { url: 'new-photo.jpg' })
213
+ ```
214
+
215
+ ---
216
+
217
+ ## morphMany
218
+
219
+ A parent model **has many** polymorphic children. Works exactly like `morphOne` but returns an array.
220
+
221
+ ### Database migration
222
+
223
+ ```ts
224
+ await schema.createTable('comments', (table) => {
225
+ table.increments('id')
226
+ table.text('body').notNullable()
227
+ table.string('commentable_type').nullable()
228
+ table.integer('commentable_id').nullable()
229
+ table.index(['commentable_type', 'commentable_id'])
230
+ })
231
+ ```
232
+
233
+ ### Model setup
234
+
235
+ ```ts
236
+ // app/models/comment.ts
237
+ import { BaseModel, column } from '@adonisjs/lucid/orm'
238
+ import { morphTo } from '@holoyan/adonisjs-polymorphic'
239
+ import Post from '#models/post'
240
+ import Video from '#models/video'
241
+
242
+ export default class Comment extends BaseModel {
243
+ @column({ isPrimary: true })
244
+ declare id: number
245
+
246
+ @column()
247
+ declare body: string
248
+
249
+ @column()
250
+ declare commentableType: string
251
+
252
+ @column()
253
+ declare commentableId: number
254
+
255
+ @morphTo({ name: 'commentable', morphMap: { posts: () => Post, videos: () => Video } })
256
+ declare commentable: Post | Video | null
257
+ }
258
+ ```
259
+
260
+ ```ts
261
+ // app/models/post.ts
262
+ import { morphOne, morphMany } from '@holoyan/adonisjs-polymorphic'
263
+ import Image from '#models/image'
264
+ import Comment from '#models/comment'
265
+
266
+ export default class Post extends BaseModel {
267
+ // ...
268
+
269
+ @morphOne(() => Image, { name: 'imageable' })
270
+ declare image: Image | null
271
+
272
+ @morphMany(() => Comment, { name: 'commentable' })
273
+ declare comments: Comment[]
274
+ }
275
+ ```
276
+
277
+ ### Querying
278
+
279
+ **Eager load:**
280
+
281
+ ```ts
282
+ const post = await (Post.query() as any)
283
+ .preload('comments')
284
+ .firstOrFail()
285
+
286
+ console.log(post.comments) // Comment[]
287
+ console.log(post.comments.length) // 3
288
+ ```
289
+
290
+ **Comments are isolated by type — a post only gets its own comments, not a video's:**
291
+
292
+ ```ts
293
+ const post = await (Post.query() as any).preload('comments').firstOrFail()
294
+ const video = await (Video.query() as any).preload('comments').firstOrFail()
295
+
296
+ // Each only sees their own comments
297
+ ```
298
+
299
+ **Ad-hoc query with additional constraints:**
300
+
301
+ ```ts
302
+ const recentComments = await (post as any)
303
+ .related('comments')
304
+ .query()
305
+ .orderBy('created_at', 'desc')
306
+ .limit(5)
307
+ ```
308
+
309
+ ### Writing
310
+
311
+ **Create one:**
312
+
313
+ ```ts
314
+ const comment = await (post as any)
315
+ .related('comments')
316
+ .create({ body: 'Great post!' })
317
+
318
+ console.log(comment.commentableType) // 'posts'
319
+ console.log(comment.commentableId) // post.id
320
+ ```
321
+
322
+ **Create many:**
323
+
324
+ ```ts
325
+ await (post as any).related('comments').createMany([
326
+ { body: 'First comment' },
327
+ { body: 'Second comment' },
328
+ ])
329
+ ```
330
+
331
+ **Save an existing instance:**
332
+
333
+ ```ts
334
+ const comment = new Comment()
335
+ comment.body = 'Hello'
336
+
337
+ await (post as any).related('comments').save(comment)
338
+ ```
339
+
340
+ **Save many:**
341
+
342
+ ```ts
343
+ await (post as any).related('comments').saveMany([comment1, comment2])
344
+ ```
345
+
346
+ ---
347
+
348
+ ## morphTo
349
+
350
+ The child side of a polymorphic relation. A comment **belongs to** either a `Post` or a `Video`.
351
+
352
+ ### Querying
353
+
354
+ **Preload the parent:**
355
+
356
+ ```ts
357
+ const comment = await (Comment.query() as any)
358
+ .preload('commentable')
359
+ .firstOrFail()
360
+
361
+ if (comment.commentable instanceof Post) {
362
+ console.log('belongs to a post:', comment.commentable.title)
363
+ } else if (comment.commentable instanceof Video) {
364
+ console.log('belongs to a video:', comment.commentable.title)
365
+ }
366
+ ```
367
+
368
+ **Preload mixed parent types in one query:**
369
+
370
+ ```ts
371
+ // All comments in one query, parents resolved in two queries (posts + videos)
372
+ const comments = await (Comment.query() as any)
373
+ .preload('commentable') as Comment[]
374
+ ```
375
+
376
+ **Ad-hoc query:**
377
+
378
+ ```ts
379
+ const parent = await (comment as any)
380
+ .related('commentable')
381
+ .query()
382
+ .firstOrFail()
383
+ ```
384
+
385
+ ### Writing
386
+
387
+ **Associate with a parent:**
388
+
389
+ ```ts
390
+ const post = await Post.findOrFail(1)
391
+ await (comment as any).related('commentable').associate(post)
392
+
393
+ // comment.commentableType is now 'posts'
394
+ // comment.commentableId is now post.id
395
+ ```
396
+
397
+ **Dissociate from parent:**
398
+
399
+ ```ts
400
+ await (comment as any).related('commentable').dissociate()
401
+
402
+ // comment.commentableType is now null
403
+ // comment.commentableId is now null
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Global morph map with @MorphMap
409
+
410
+ When you have many `morphTo` relations, repeating `morphMap: { posts: () => Post, videos: () => Video }` on each one gets tedious. Use the `@MorphMap` decorator from `@holoyan/morph-map-js` (bundled as a dependency) to register each model once globally.
411
+
412
+ ### Setup
413
+
414
+ Decorate each parent model with its alias:
415
+
416
+ ```ts
417
+ // app/models/post.ts
418
+ import { MorphMap } from '@holoyan/morph-map-js'
419
+
420
+ @MorphMap('posts')
421
+ export default class Post extends BaseModel {
422
+ // ...
423
+ }
424
+ ```
425
+
426
+ ```ts
427
+ // app/models/video.ts
428
+ import { MorphMap } from '@holoyan/morph-map-js'
429
+
430
+ @MorphMap('videos')
431
+ export default class Video extends BaseModel {
432
+ // ...
433
+ }
434
+ ```
435
+
436
+ Now `morphTo` relations can omit the `morphMap` option entirely:
437
+
438
+ ```ts
439
+ // app/models/comment.ts
440
+ import { morphTo } from '@holoyan/adonisjs-polymorphic'
441
+
442
+ export default class Comment extends BaseModel {
443
+ @column()
444
+ declare commentableType: string
445
+
446
+ @column()
447
+ declare commentableId: number
448
+
449
+ // No morphMap needed — resolved from global registry at query time
450
+ @morphTo({ name: 'commentable' })
451
+ declare commentable: Post | Video | null
452
+ }
453
+ ```
454
+
455
+ Adding a new parent type (e.g. `Podcast`) only requires one change:
456
+
457
+ ```ts
458
+ @MorphMap('podcasts')
459
+ export default class Podcast extends BaseModel {}
460
+ ```
461
+
462
+ All existing `morphTo` relations pick it up automatically.
463
+
464
+ ### Alias vs table name
465
+
466
+ The `@MorphMap` alias is also used as the `morphValue` stored in the type column. This lets you decouple the alias from the table name:
467
+
468
+ ```ts
469
+ @MorphMap('post') // alias stored in type column
470
+ export default class Post extends BaseModel {
471
+ static table = 'posts' // actual DB table
472
+ }
473
+ ```
474
+
475
+ ```ts
476
+ @morphOne(() => Image, { name: 'imageable' })
477
+ // morphValue will be 'post' (from @MorphMap), not 'posts' (from table)
478
+ ```
479
+
480
+ ### Ensuring models are registered at boot time
481
+
482
+ The global registry is populated when a model file is **imported**. To guarantee the registry is fully populated before any request, seeder, or test query runs, register your parent models in `config/polymorphic.ts` (published automatically by `node ace configure`):
483
+
484
+ ```ts
485
+ // config/polymorphic.ts
486
+ import { defineConfig } from '@holoyan/adonisjs-polymorphic'
487
+
488
+ export default defineConfig({
489
+ morphModels: [
490
+ () => import('#models/post'),
491
+ () => import('#models/video'),
492
+ () => import('#models/podcast'), // add new parent models here
493
+ ],
494
+ })
495
+ ```
496
+
497
+ The service provider imports all listed models during `boot()` — before the app serves any request, before seeders run, before tests execute. This completely eliminates any load order concerns.
498
+
499
+ Every time you add a new model decorated with `@MorphMap`, add it to this list.
500
+
501
+ ### Explicit morphMap always wins
502
+
503
+ You can always override the registry on a per-relation basis:
504
+
505
+ ```ts
506
+ @morphTo({
507
+ name: 'commentable',
508
+ morphMap: { posts: () => Post }, // only posts, ignores registry
509
+ })
510
+ declare commentable: Post | null
511
+ ```
512
+
513
+ ### Priority order
514
+
515
+ | What's set | morphTo resolution | morphOne/morphMany morphValue |
516
+ |---|---|---|
517
+ | Explicit `morphMap` option | Used directly | — |
518
+ | `morphValue` option | — | Used directly |
519
+ | `@MorphMap` on model | Registry fallback | Registry alias |
520
+ | Nothing | Error at query time | `model.table` |
521
+
522
+ ---
523
+
524
+ ## Options reference
525
+
526
+ ### `@morphOne(relatedModel, options)`
527
+
528
+ | Option | Type | Default | Description |
529
+ |---|---|---|---|
530
+ | `name` | `string` | **required** | Prefix for the type/id columns on the related model. `'imageable'` → `imageableType` + `imageableId` |
531
+ | `localKey` | `string` | primary key | Attribute on the parent used to match against the id column |
532
+ | `morphValue` | `string` | `@MorphMap` alias or `model.table` | Value stored in the type column to identify this parent |
533
+ | `serializeAs` | `string \| null` | relation name | Key used during serialization. `null` excludes it |
534
+ | `onQuery` | `(query) => void` | — | Hook to add default constraints to every query on this relation |
535
+
536
+ ### `@morphMany(relatedModel, options)`
537
+
538
+ Same options as `@morphOne`.
539
+
540
+ ### `@morphTo(options)`
541
+
542
+ | Option | Type | Default | Description |
543
+ |---|---|---|---|
544
+ | `name` | `string` | relation name | Prefix used to derive the type/id attribute names on this model |
545
+ | `morphMap` | `Record<string, () => Model>` | global registry | Maps type strings to model factories. Optional when `@MorphMap` is used |
546
+ | `typeKey` | `string` | `${name}Type` | Explicit attribute name for the type column if it doesn't follow the naming convention |
547
+ | `idKey` | `string` | `${name}Id` | Explicit attribute name for the id column if it doesn't follow the naming convention |
548
+ | `serializeAs` | `string \| null` | relation name | Key used during serialization. `null` excludes it |
549
+ | `onQuery` | `(query) => void` | — | Hook to add default constraints |
@@ -0,0 +1,10 @@
1
+ import type Configure from '@adonisjs/core/commands/configure';
2
+ /**
3
+ * Configure hook — runs when the user executes:
4
+ * node ace configure @holoyan/adonisjs-polymorphic
5
+ *
6
+ * - Registers the service provider in adonisrc.ts
7
+ * - Publishes config/polymorphic.ts
8
+ */
9
+ export declare function configure(command: Configure): Promise<void>;
10
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAA;AAG9D;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAoBjD"}
@@ -0,0 +1,27 @@
1
+ import { stubsRoot } from './stubs/main.js';
2
+ /**
3
+ * Configure hook — runs when the user executes:
4
+ * node ace configure @holoyan/adonisjs-polymorphic
5
+ *
6
+ * - Registers the service provider in adonisrc.ts
7
+ * - Publishes config/polymorphic.ts
8
+ */
9
+ export async function configure(command) {
10
+ const codemods = await command.createCodemods();
11
+ await codemods.updateRcFile((rcFile) => {
12
+ rcFile.addProvider('@holoyan/adonisjs-polymorphic/provider');
13
+ });
14
+ await codemods.makeUsingStub(stubsRoot, 'config/polymorphic.stub', {});
15
+ command.logger.log('');
16
+ command.logger.log(' Install complete. Next steps:');
17
+ command.logger.log('');
18
+ command.logger.log(' 1. Decorate your parent models with @MorphMap:');
19
+ command.logger.log(' @MorphMap(\'posts\') export default class Post extends BaseModel {}');
20
+ command.logger.log('');
21
+ command.logger.log(' 2. Register them in config/polymorphic.ts:');
22
+ command.logger.log(' morphModels: [() => import(\'#models/post\')]');
23
+ command.logger.log('');
24
+ command.logger.log(' 3. Use @morphOne, @morphMany, @morphTo in your models.');
25
+ command.logger.log('');
26
+ }
27
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAA;IAE/C,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,WAAW,CAAC,wCAAwC,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,yBAAyB,EAAE,EAAE,CAAC,CAAA;IAEtE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAA;IACrD,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;IACtE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,4EAA4E,CAAC,CAAA;IAChG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAA;IAClE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAA;IAC1E,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAA;IAC9E,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;AACxB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ /**
3
+ * Service provider for @holoyan/adonisjs-polymorphic.
4
+ *
5
+ * During boot, imports every model listed in config/polymorphic.ts so that
6
+ * @MorphMap decorators run and the global registry is fully populated before
7
+ * any request, command, or test query executes.
8
+ */
9
+ export default class PolymorphicProvider {
10
+ protected app: ApplicationService;
11
+ constructor(app: ApplicationService);
12
+ register(): void;
13
+ boot(): Promise<void>;
14
+ }
15
+ //# sourceMappingURL=plugin_provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin_provider.d.ts","sourceRoot":"","sources":["../../providers/plugin_provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAG9D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAC1B,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IAEF,IAAI;CAOX"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Service provider for @holoyan/adonisjs-polymorphic.
3
+ *
4
+ * During boot, imports every model listed in config/polymorphic.ts so that
5
+ * @MorphMap decorators run and the global registry is fully populated before
6
+ * any request, command, or test query executes.
7
+ */
8
+ export default class PolymorphicProvider {
9
+ app;
10
+ constructor(app) {
11
+ this.app = app;
12
+ }
13
+ register() { }
14
+ async boot() {
15
+ const config = this.app.config.get('polymorphic', {});
16
+ if (config.morphModels?.length) {
17
+ await Promise.all(config.morphModels.map((factory) => factory()));
18
+ }
19
+ }
20
+ }
21
+ //# sourceMappingURL=plugin_provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin_provider.js","sourceRoot":"","sources":["../../providers/plugin_provider.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAChB;IAAtB,YAAsB,GAAuB;QAAvB,QAAG,GAAH,GAAG,CAAoB;IAAG,CAAC;IAEjD,QAAQ,KAAI,CAAC;IAEb,KAAK,CAAC,IAAI;QACR,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAoB,aAAa,EAAE,EAAE,CAAC,CAAA;QAExE,IAAI,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;YAC/B,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;QACnE,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,41 @@
1
+ import type { MorphOneOptions, MorphManyOptions, MorphToOptions } from './types.js';
2
+ /**
3
+ * Decorator to define a morphOne (has-one polymorphic) relation on a parent model.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * class Post extends BaseModel {
8
+ * @morphOne(() => Image, { name: 'imageable' })
9
+ * declare image: Image | null
10
+ * }
11
+ * ```
12
+ */
13
+ export declare function morphOne(relatedModelFactory: () => any, options: MorphOneOptions): (target: any, relationName: string) => void;
14
+ /**
15
+ * Decorator to define a morphMany (has-many polymorphic) relation on a parent model.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * class Post extends BaseModel {
20
+ * @morphMany(() => Comment, { name: 'commentable' })
21
+ * declare comments: Comment[]
22
+ * }
23
+ * ```
24
+ */
25
+ export declare function morphMany(relatedModelFactory: () => any, options: MorphManyOptions): (target: any, relationName: string) => void;
26
+ /**
27
+ * Decorator to define a morphTo (belongs-to polymorphic) relation on a child model.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * class Comment extends BaseModel {
32
+ * @column() declare commentableType: string
33
+ * @column() declare commentableId: number
34
+ *
35
+ * @morphTo({ name: 'commentable', morphMap: { posts: () => Post, videos: () => Video } })
36
+ * declare commentable: Post | Video | null
37
+ * }
38
+ * ```
39
+ */
40
+ export declare function morphTo(options: MorphToOptions): (target: any, relationName: string) => void;
41
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../src/decorators.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEnF;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,EAAE,OAAO,EAAE,eAAe,IAC5C,QAAQ,GAAG,EAAE,cAAc,MAAM,UAMrE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,mBAAmB,EAAE,MAAM,GAAG,EAAE,OAAO,EAAE,gBAAgB,IAC7C,QAAQ,GAAG,EAAE,cAAc,MAAM,UAMtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,IACX,QAAQ,GAAG,EAAE,cAAc,MAAM,UAMpE"}