• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ecamp / hal-json-vuex / 5566299053

pending completion
5566299053

push

github

web-flow
Merge pull request #285 from usu/chore/node-20

chore: add support for node 20

139 of 164 branches covered (84.76%)

Branch coverage included in aggregate %.

300 of 319 relevant lines covered (94.04%)

1268.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/src/LoadingResource.ts
1
import LoadingCollection from './LoadingCollection'
9✔
2
import ResourceInterface from './interfaces/ResourceInterface'
3
import CollectionInterface from './interfaces/CollectionInterface'
4
import { InternalConfig } from './interfaces/Config'
5

6
/**
7
 * Creates a placeholder for an entity which has not yet finished loading from the API.
8
 * Such a LoadingResource can safely be used in Vue components, since it will render as an empty
9
 * string and Vue's reactivity system will replace it with the real data once that is available.
10
 *
11
 * Accessing nested functions in a LoadingResource yields another LoadingResource:
12
 * new LoadingResource(...).author().organization() // gives another LoadingResource
13
 *
14
 * Using a LoadingResource or a property of a LoadingResource in a view renders to empty strings:
15
 * let user = new LoadingResource(...)
16
 * 'The "' + user + '" is called "' + user.name + '"' // gives 'The "" is called ""'
17
 */
18
class LoadingResource implements ResourceInterface {
19
  public _meta: {
20
    self: string | null,
21
    selfUrl: string | null,
22
    load: Promise<ResourceInterface>
23
    loading: boolean
24
  }
25

26
  private loadResource: Promise<ResourceInterface>
27

28
  /**
29
   * @param loadResource a Promise that resolves to a Resource when the entity has finished
30
   *                     loading from the API
31
   * @param self optional URI of the entity being loaded, if available. If passed, the
32
   *                     returned LoadingResource will return it in calls to .self and ._meta.self
33
   * @param config       configuration of this instance of hal-json-vuex
34
   */
35
  constructor (loadResource: Promise<ResourceInterface>, self: string | null = null, config: InternalConfig | null = null) {
180✔
36
    this._meta = {
816✔
37
      self: self,
38
      selfUrl: self ? config?.apiRoot + self : null,
2,337!
39
      load: loadResource,
40
      loading: true
41
    }
42

43
    this.loadResource = loadResource
816✔
44

45
    const handler = {
816✔
46
      get: function (target: LoadingResource, prop: string | number | symbol) {
47
        // This is necessary so that Vue's reactivity system understands to treat this LoadingResource
48
        // like a normal object.
49
        if (prop === Symbol.toPrimitive) {
684!
50
          return () => ''
×
51
        }
52

53
        // This is necessary so that Vue's reactivity system understands to treat this LoadingResource
54
        // like a normal object.
55
        if (['then', Symbol.toStringTag, 'state', 'getters', '$options', '_isVue', '__file', 'render', 'constructor'].includes(prop as string)) {
684!
56
          return undefined
×
57
        }
58

59
        // proxy to properties that actually exist on LoadingResource (_meta, $reload, etc.)
60
        if (Reflect.has(target, prop)) {
684✔
61
          return Reflect.get(target, prop)
528✔
62
        }
63

64
        // Proxy to all other unknown properties: return a function that yields another LoadingResource
65
        const loadProperty = loadResource.then(resource => resource[prop])
156✔
66

67
        const result = templateParams => new LoadingResource(loadProperty.then(property => {
156✔
68
          try {
90✔
69
            return property(templateParams)._meta.load
90✔
70
          } catch (e) {
71
            throw new Error(`Property '${prop.toString()}' on resource '${self}' was used like a relation, but no relation with this name was returned by the API (actual return value: ${JSON.stringify(property)})`)
12✔
72
          }
73
        }
74
        ))
75

76
        result.toString = () => ''
156✔
77
        return result
156✔
78
      }
79
    }
80
    return new Proxy(this, handler)
816✔
81
  }
82

83
  get items (): Array<ResourceInterface> {
84
    return LoadingCollection.create(this.loadResource.then(resource => (resource as CollectionInterface).items))
21✔
85
  }
86

87
  get allItems (): Array<ResourceInterface> {
88
    return LoadingCollection.create(this.loadResource.then(resource => (resource as CollectionInterface).allItems))
×
89
  }
90

91
  public $reload (): Promise<ResourceInterface> {
92
    // Skip reloading entities that are already loading
93
    return this._meta.load
3✔
94
  }
95

96
  public $loadItems (): Promise<CollectionInterface> {
97
    return this._meta.load.then(resource => (resource as CollectionInterface).$loadItems())
6✔
98
  }
99

100
  public $post (data: unknown): Promise<ResourceInterface | null> {
101
    return this._meta.load.then(resource => resource.$post(data))
3✔
102
  }
103

104
  public $patch (data: unknown): Promise<ResourceInterface> {
105
    return this._meta.load.then(resource => resource.$patch(data))
3✔
106
  }
107

108
  public $del (): Promise<string | void> {
109
    return this._meta.load.then(resource => resource.$del())
3✔
110
  }
111

112
  public $href (relation: string, templateParams = {}): Promise<string | undefined> {
×
113
    return this._meta.load.then(resource => resource.$href(relation, templateParams))
×
114
  }
115

116
  public toJSON (): string {
117
    return '{}'
18✔
118
  }
119
}
120

121
export default LoadingResource
9✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc