etro 0.7.0 → 0.8.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 (106) hide show
  1. package/.github/workflows/nodejs.yml +1 -1
  2. package/CHANGELOG.md +45 -1
  3. package/CONTRIBUTING.md +23 -19
  4. package/README.md +81 -26
  5. package/dist/effect/base.d.ts +38 -0
  6. package/dist/effect/brightness.d.ts +16 -0
  7. package/dist/effect/channels.d.ts +23 -0
  8. package/dist/effect/chroma-key.d.ts +23 -0
  9. package/dist/effect/contrast.d.ts +15 -0
  10. package/dist/effect/elliptical-mask.d.ts +31 -0
  11. package/dist/effect/gaussian-blur.d.ts +60 -0
  12. package/dist/effect/grayscale.d.ts +7 -0
  13. package/dist/effect/index.d.ts +15 -0
  14. package/dist/effect/pixelate.d.ts +18 -0
  15. package/dist/effect/shader.d.ts +99 -0
  16. package/dist/effect/stack.d.ts +23 -0
  17. package/dist/effect/transform.d.ts +73 -0
  18. package/dist/etro-cjs.js +9337 -3331
  19. package/dist/etro-iife.js +9279 -3273
  20. package/dist/etro.d.ts +7 -0
  21. package/dist/event.d.ts +35 -0
  22. package/dist/index.d.ts +6 -0
  23. package/dist/layer/audio-source.d.ts +24 -0
  24. package/dist/layer/audio.d.ts +14 -0
  25. package/dist/layer/base.d.ts +69 -0
  26. package/dist/layer/image.d.ts +6 -0
  27. package/dist/layer/index.d.ts +11 -0
  28. package/dist/layer/text.d.ts +60 -0
  29. package/dist/layer/video.d.ts +11 -0
  30. package/dist/layer/visual-source.d.ts +32 -0
  31. package/dist/layer/visual.d.ts +58 -0
  32. package/dist/movie.d.ts +192 -0
  33. package/dist/object.d.ts +12 -0
  34. package/dist/util.d.ts +125 -0
  35. package/eslint.conf.js +0 -8
  36. package/eslint.example-conf.js +9 -0
  37. package/eslint.typescript-conf.js +5 -0
  38. package/examples/application/readme-screenshot.html +12 -9
  39. package/examples/application/video-player.html +7 -7
  40. package/examples/application/webcam.html +6 -6
  41. package/examples/introduction/audio.html +30 -18
  42. package/examples/introduction/effects.html +14 -10
  43. package/examples/introduction/export.html +32 -25
  44. package/examples/introduction/functions.html +6 -4
  45. package/examples/introduction/hello-world1.html +9 -5
  46. package/examples/introduction/hello-world2.html +5 -5
  47. package/examples/introduction/keyframes.html +35 -23
  48. package/examples/introduction/media.html +26 -18
  49. package/examples/introduction/text.html +9 -5
  50. package/karma.conf.js +1 -1
  51. package/package.json +29 -13
  52. package/rollup.config.js +15 -4
  53. package/scripts/gen-effect-samples.html +29 -25
  54. package/scripts/save-effect-samples.js +14 -15
  55. package/spec/assets/effect/gaussian-blur-horizontal.png +0 -0
  56. package/spec/assets/effect/gaussian-blur-vertical.png +0 -0
  57. package/spec/assets/effect/grayscale.png +0 -0
  58. package/spec/assets/effect/original.png +0 -0
  59. package/spec/assets/effect/pixelate.png +0 -0
  60. package/spec/assets/effect/transform/multiply.png +0 -0
  61. package/spec/assets/effect/transform/rotate.png +0 -0
  62. package/spec/assets/effect/transform/scale-fraction.png +0 -0
  63. package/spec/assets/effect/transform/scale.png +0 -0
  64. package/spec/assets/effect/transform/translate-fraction.png +0 -0
  65. package/spec/assets/effect/transform/translate.png +0 -0
  66. package/spec/effect.spec.js +126 -57
  67. package/spec/event.spec.js +14 -0
  68. package/spec/layer.spec.js +175 -18
  69. package/spec/movie.spec.js +191 -7
  70. package/spec/util.spec.js +14 -5
  71. package/src/effect/base.ts +96 -0
  72. package/src/effect/brightness.ts +43 -0
  73. package/src/effect/channels.ts +50 -0
  74. package/src/effect/chroma-key.ts +82 -0
  75. package/src/effect/contrast.ts +42 -0
  76. package/src/effect/elliptical-mask.ts +75 -0
  77. package/src/effect/gaussian-blur.ts +232 -0
  78. package/src/effect/grayscale.ts +34 -0
  79. package/src/effect/index.ts +22 -0
  80. package/src/effect/pixelate.ts +59 -0
  81. package/src/effect/shader.ts +561 -0
  82. package/src/effect/stack.ts +74 -0
  83. package/src/effect/transform.ts +194 -0
  84. package/src/etro.ts +26 -0
  85. package/src/event.ts +118 -0
  86. package/src/index.ts +8 -0
  87. package/src/layer/audio-source.ts +217 -0
  88. package/src/layer/audio.ts +35 -0
  89. package/src/layer/base.ts +156 -0
  90. package/src/layer/image.ts +8 -0
  91. package/src/layer/index.ts +13 -0
  92. package/src/layer/text.ts +138 -0
  93. package/src/layer/video.ts +15 -0
  94. package/src/layer/visual-source.ts +150 -0
  95. package/src/layer/visual.ts +198 -0
  96. package/src/movie.ts +709 -0
  97. package/src/object.ts +14 -0
  98. package/src/util.ts +473 -0
  99. package/tsconfig.json +8 -0
  100. package/screenshots/2019-08-17_0.png +0 -0
  101. package/src/effect.js +0 -1268
  102. package/src/event.js +0 -78
  103. package/src/index.js +0 -23
  104. package/src/layer.js +0 -897
  105. package/src/movie.js +0 -637
  106. package/src/util.js +0 -505
package/src/object.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { Movie } from './movie'
2
+
3
+ /** A movie, layer or effect */
4
+ export default interface EtroObject {
5
+ /** Used in etro internals */
6
+ type: string
7
+ /** Which properties to not watch for changes, for `Movie#autoRefresh` */
8
+ publicExcludes: string[]
9
+ /** Map of property name to function to run on result of `val` */
10
+ propertyFilters: Record<string, <T>(value: T) => T>
11
+ movie: Movie
12
+
13
+ getDefaultOptions(): object // eslint-disable-line @typescript-eslint/ban-types
14
+ }
package/src/util.ts ADDED
@@ -0,0 +1,473 @@
1
+ /**
2
+ * @module util
3
+ */
4
+
5
+ import EtroObject from './object'
6
+ import { publish } from './event'
7
+ import { Movie } from './movie'
8
+
9
+ /**
10
+ * Gets the first matching property descriptor in the prototype chain, or
11
+ * undefined.
12
+ * @param obj
13
+ * @param name
14
+ */
15
+ function getPropertyDescriptor (obj: unknown, name: string | number | symbol): PropertyDescriptor {
16
+ do {
17
+ const propDesc = Object.getOwnPropertyDescriptor(obj, name)
18
+ if (propDesc) {
19
+ return propDesc
20
+ }
21
+ obj = Object.getPrototypeOf(obj)
22
+ } while (obj)
23
+ return undefined
24
+ }
25
+
26
+ /**
27
+ * Merges `options` with `defaultOptions`, and then copies the properties with
28
+ * the keys in `defaultOptions` from the merged object to `destObj`.
29
+ *
30
+ * @return
31
+ */
32
+ // TODO: Make methods like getDefaultOptions private
33
+ export function applyOptions (options: object, destObj: EtroObject): void { // eslint-disable-line @typescript-eslint/ban-types
34
+ const defaultOptions = destObj.getDefaultOptions()
35
+
36
+ // Validate; make sure `keys` doesn't have any extraneous items
37
+ for (const option in options) {
38
+ // eslint-disable-next-line no-prototype-builtins
39
+ if (!defaultOptions.hasOwnProperty(option)) {
40
+ throw new Error("Invalid option: '" + option + "'")
41
+ }
42
+ }
43
+
44
+ // Merge options and defaultOptions
45
+ options = { ...defaultOptions, ...options }
46
+
47
+ // Copy options
48
+ for (const option in options) {
49
+ const propDesc = getPropertyDescriptor(destObj, option)
50
+ // Update the property as long as the property has not been set (unless if it has a setter)
51
+ if (!propDesc || propDesc.set) {
52
+ destObj[option] = options[option]
53
+ }
54
+ }
55
+ }
56
+
57
+ // This must be cleared at the start of each frame
58
+ const valCache = new WeakMap()
59
+ function cacheValue (element: EtroObject, path: string, value: unknown) {
60
+ // Initiate movie cache
61
+ if (!valCache.has(element.movie)) {
62
+ valCache.set(element.movie, new WeakMap())
63
+ }
64
+ const movieCache = valCache.get(element.movie)
65
+
66
+ // Iniitate element cache
67
+ if (!movieCache.has(element)) {
68
+ movieCache.set(element, {})
69
+ }
70
+ const elementCache = movieCache.get(element)
71
+
72
+ // Cache the value
73
+ elementCache[path] = value
74
+ return value
75
+ }
76
+ function hasCachedValue (element, path) {
77
+ return valCache.has(element.movie) &&
78
+ valCache.get(element.movie).has(element) &&
79
+ path in valCache.get(element.movie).get(element)
80
+ }
81
+ function getCachedValue (element, path) {
82
+ return valCache.get(element.movie).get(element)[path]
83
+ }
84
+ export function clearCachedValues (movie: Movie): void {
85
+ valCache.delete(movie)
86
+ }
87
+
88
+ /**
89
+ * A keyframe set.
90
+ *
91
+ * Usage:
92
+ * ```js
93
+ new etro.KeyFrame([time1, value1, interpolation1], [time2, value2])`
94
+ * ```
95
+ * TypeScript users need to specify the type of the value as a type parameter.
96
+ */
97
+ export class KeyFrame<T> {
98
+ value: unknown[][]
99
+ /** Keys to interpolate, or all keys if undefined */
100
+ interpolationKeys: string[]
101
+
102
+ constructor (...value: T[][]) {
103
+ this.value = value
104
+ this.interpolationKeys = []
105
+ }
106
+
107
+ withKeys (keys: string[]): KeyFrame<T> {
108
+ this.interpolationKeys = keys
109
+ return this
110
+ }
111
+
112
+ evaluate (time: number): T {
113
+ if (this.value.length === 0) {
114
+ throw new Error('Empty keyframe')
115
+ }
116
+ if (time === undefined) {
117
+ throw new Error('|time| is undefined or null')
118
+ }
119
+ const firstTime: number = this.value[0][0] as number
120
+ if (time < firstTime) {
121
+ throw new Error('No keyframe point before |time|')
122
+ }
123
+ // I think reduce are slow to do per-frame (or more)?
124
+ for (let i = 0; i < this.value.length; i++) {
125
+ const startTime = this.value[i][0] as number
126
+ const startValue = this.value[i][1] as T
127
+ type interpolateType = <U = number | object>(startValue: U, endValue: U, percentProgress: number, interpolationKeys: string[]) => U // eslint-disable-line @typescript-eslint/ban-types
128
+ const interpolate = this.value[i].length === 3 ? this.value[i][2] as interpolateType : linearInterp
129
+ if (i + 1 < this.value.length) {
130
+ const endTime = this.value[i + 1][0] as number
131
+ const endValue = this.value[i + 1][1] as T
132
+ if (startTime <= time && time < endTime) {
133
+ // No need for endValue if it is flat interpolation
134
+ // TODO: support custom interpolation for 'other' types?
135
+ if (!(typeof startValue === 'number' || typeof endValue === 'object')) {
136
+ return startValue
137
+ } else if (typeof startValue !== typeof endValue) {
138
+ throw new Error('Type mismatch in keyframe values')
139
+ } else {
140
+ // Interpolate
141
+ const percentProgress = (time - startTime) / (endTime - startTime)
142
+ return interpolate(
143
+ startValue as unknown as (number | object), // eslint-disable-line @typescript-eslint/ban-types
144
+ endValue as unknown as (number | object), // eslint-disable-line @typescript-eslint/ban-types
145
+ percentProgress, this.interpolationKeys
146
+ ) as unknown as T
147
+ }
148
+ }
149
+ } else {
150
+ // Repeat last value forever
151
+ return startValue
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ /** A dynamic property. Supports simple values, keyframes and functions */
158
+ export type Dynamic<T> = T | KeyFrame<T> | ((element: EtroObject, time: number) => T)
159
+
160
+ /**
161
+ * Computes a property.
162
+ *
163
+ * @param element - the etro object to which the property belongs to
164
+ * @param path - the dot-separated path to a property on `element`
165
+ * @param time - time to calculate keyframes for, if necessary
166
+ *
167
+ * Note that only values used in keyframes that are numbers or objects
168
+ * (including arrays) are interpolated. All other values are taken sequentially
169
+ * with no interpolation. JavaScript will convert parsed colors, if created
170
+ * correctly, to their string representations when assigned to a
171
+ * CanvasRenderingContext2D property.
172
+ */
173
+ // TODO: Is this function efficient?
174
+ // TODO: Update doc @params to allow for keyframes
175
+ export function val (element: EtroObject, path: string, time: number): any { // eslint-disable-line @typescript-eslint/no-explicit-any
176
+ if (hasCachedValue(element, path)) {
177
+ return getCachedValue(element, path)
178
+ }
179
+
180
+ // Get property of element at path
181
+ const pathParts = path.split('.')
182
+ let property = element[pathParts.shift()]
183
+ while (pathParts.length > 0) {
184
+ property = property[pathParts.shift()]
185
+ }
186
+ // Property filter function
187
+ const process = element.propertyFilters[path]
188
+
189
+ let value
190
+ if (property instanceof KeyFrame) {
191
+ value = property.evaluate(time)
192
+ } else if (typeof property === 'function') {
193
+ value = property(element, time) // TODO? add more args
194
+ } else {
195
+ // Simple value
196
+ value = property
197
+ }
198
+ return cacheValue(element, path, process ? process.call(element, value) : value)
199
+ }
200
+
201
+ /* export function floorInterp(x1, x2, t, objectKeys) {
202
+ // https://stackoverflow.com/a/25835337/3783155 (TODO: preserve getters/setters, etc?)
203
+ return !objectKeys ? x1 : objectKeys.reduce((a, x) => {
204
+ if (x1.hasOwnProperty(x)) a[x] = o[x]; // ignore x2
205
+ return a;
206
+ }, Object.create(Object.getPrototypeOf(x1)));
207
+ } */
208
+
209
+ export function linearInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
210
+ if (typeof x1 !== typeof x2) {
211
+ throw new Error('Type mismatch')
212
+ }
213
+ if (typeof x1 !== 'number' && typeof x1 !== 'object') {
214
+ // Flat interpolation (floor)
215
+ return x1
216
+ }
217
+ if (typeof x1 === 'object') { // to work with objects (including arrays)
218
+ // TODO: make this code DRY
219
+ if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
220
+ throw new Error('Prototype mismatch')
221
+ }
222
+ // Preserve prototype of objects
223
+ const int = Object.create(Object.getPrototypeOf(x1))
224
+ // Take the intersection of properties
225
+ const keys = Object.keys(x1) || objectKeys // TODO: reverse operands
226
+ for (let i = 0; i < keys.length; i++) {
227
+ const key = keys[i]
228
+ // eslint-disable-next-line no-prototype-builtins
229
+ if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
230
+ continue
231
+ }
232
+ int[key] = linearInterp(x1[key], x2[key], t)
233
+ }
234
+ return int
235
+ }
236
+ return (1 - t) * x1 + t * (x2 as number)
237
+ }
238
+
239
+ export function cosineInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
240
+ if (typeof x1 !== typeof x2) {
241
+ throw new Error('Type mismatch')
242
+ }
243
+ if (typeof x1 !== 'number' && typeof x1 !== 'object') {
244
+ // Flat interpolation (floor)
245
+ return x1
246
+ }
247
+ if (typeof x1 === 'object' && typeof x2 === 'object') { // to work with objects (including arrays)
248
+ if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
249
+ throw new Error('Prototype mismatch')
250
+ }
251
+ // Preserve prototype of objects
252
+ const int = Object.create(Object.getPrototypeOf(x1))
253
+ // Take the intersection of properties
254
+ const keys = Object.keys(x1) || objectKeys
255
+ for (let i = 0; i < keys.length; i++) {
256
+ const key = keys[i]
257
+ // eslint-disable-next-line no-prototype-builtins
258
+ if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
259
+ continue
260
+ }
261
+ int[key] = cosineInterp(x1[key], x2[key], t)
262
+ }
263
+ return int
264
+ }
265
+ const cos = Math.cos(Math.PI / 2 * t)
266
+ return cos * (x1 as number) + (1 - cos) * (x2 as number)
267
+ }
268
+
269
+ /**
270
+ * An RGBA color, for proper interpolation and shader effects
271
+ */
272
+ export class Color {
273
+ r: number
274
+ g: number
275
+ b: number
276
+ a: number
277
+
278
+ /**
279
+ * @param r
280
+ * @param g
281
+ * @param b
282
+ * @param a
283
+ */
284
+ constructor (r: number, g: number, b: number, a = 1.0) {
285
+ this.r = r
286
+ this.g = g
287
+ this.b = b
288
+ this.a = a
289
+ }
290
+
291
+ /**
292
+ * Converts to a CSS color
293
+ */
294
+ toString (): string {
295
+ return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`
296
+ }
297
+ }
298
+
299
+ const parseColorCanvas = document.createElement('canvas')
300
+ parseColorCanvas.width = parseColorCanvas.height = 1
301
+ const parseColorCtx = parseColorCanvas.getContext('2d')
302
+ /**
303
+ * Converts a CSS color string to a {@link Color} object representation.
304
+ * @param str
305
+ * @return the parsed color
306
+ */
307
+ export function parseColor (str: string): Color {
308
+ // TODO - find a better way to deal with the fact that invalid values of "col"
309
+ // are ignored.
310
+ parseColorCtx.clearRect(0, 0, 1, 1)
311
+ parseColorCtx.fillStyle = str
312
+ parseColorCtx.fillRect(0, 0, 1, 1)
313
+ const data = parseColorCtx.getImageData(0, 0, 1, 1).data
314
+ return new Color(data[0], data[1], data[2], data[3] / 255)
315
+ }
316
+
317
+ /**
318
+ * A font, for proper interpolation
319
+ */
320
+ export class Font {
321
+ size: number
322
+ sizeUnit: string
323
+ family: string
324
+ style: string
325
+ variant: string
326
+ weight: string
327
+ stretch: string
328
+ lineHeight: string
329
+
330
+ /**
331
+ * @param size
332
+ * @param family
333
+ * @param sizeUnit
334
+ */
335
+ constructor (size: number, sizeUnit: string, family: string, style = 'normal', variant = 'normal',
336
+ weight = 'normal', stretch = 'normal', lineHeight = 'normal') {
337
+ this.size = size
338
+ this.sizeUnit = sizeUnit
339
+ this.family = family
340
+ this.style = style
341
+ this.variant = variant
342
+ this.weight = weight
343
+ this.stretch = stretch
344
+ this.lineHeight = lineHeight
345
+ }
346
+
347
+ /**
348
+ * Converts to CSS font syntax
349
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/font
350
+ */
351
+ toString (): string {
352
+ let s = ''
353
+ if (this.style !== 'normal') s += this.style + ' '
354
+ if (this.variant !== 'normal') s += this.variant + ' '
355
+ if (this.weight !== 'normal') s += this.weight + ' '
356
+ if (this.stretch !== 'normal') s += this.stretch + ' '
357
+ s += `${this.size}${this.sizeUnit} `
358
+ if (this.lineHeight !== 'normal') s += this.lineHeight + ' '
359
+ s += this.family
360
+
361
+ return s
362
+ }
363
+ }
364
+
365
+ const parseFontEl = document.createElement('div')
366
+ /**
367
+ * Converts a CSS font string to a {@link Font} object
368
+ * representation.
369
+ * @param str
370
+ * @return the parsed font
371
+ */
372
+ export function parseFont (str: string): Font {
373
+ // Assign css string to html element
374
+ parseFontEl.setAttribute('style', `font: ${str}`)
375
+ const {
376
+ fontSize, fontFamily, fontStyle, fontVariant, fontWeight, lineHeight
377
+ } = parseFontEl.style
378
+ parseFontEl.removeAttribute('style')
379
+
380
+ const size = parseFloat(fontSize)
381
+ const sizeUnit = fontSize.substring(size.toString().length)
382
+ return new Font(size, sizeUnit, fontFamily, fontStyle, fontVariant, fontWeight, lineHeight)
383
+ }
384
+
385
+ /**
386
+ * @param mapper
387
+ * @param canvas
388
+ * @param ctx
389
+ * @param x
390
+ * @param y
391
+ * @param width
392
+ * @param height
393
+ * @param flush
394
+ * @deprecated Use {@link effect.Shader} instead
395
+ */
396
+ export function mapPixels (
397
+ mapper: (pixels: Uint8ClampedArray, i: number) => void,
398
+ canvas: HTMLCanvasElement,
399
+ ctx: CanvasRenderingContext2D,
400
+ x: number,
401
+ y: number,
402
+ width: number,
403
+ height: number,
404
+ flush = true
405
+ ): void {
406
+ x = x || 0
407
+ y = y || 0
408
+ width = width || canvas.width
409
+ height = height || canvas.height
410
+ const frame = ctx.getImageData(x, y, width, height)
411
+ for (let i = 0, l = frame.data.length; i < l; i += 4) {
412
+ mapper(frame.data, i)
413
+ }
414
+ if (flush) {
415
+ ctx.putImageData(frame, x, y)
416
+ }
417
+ }
418
+
419
+ /**
420
+ * <p>Emits "change" event when public properties updated, recursively.
421
+ * <p>Must be called before any watchable properties are set, and only once in
422
+ * the prototype chain.
423
+ *
424
+ * @param target - object to watch
425
+ */
426
+ export function watchPublic (target: EtroObject): EtroObject {
427
+ const getPath = (receiver, prop) =>
428
+ (receiver === proxy ? '' : (paths.get(receiver) + '.')) + prop
429
+ const callback = function (prop, val, receiver) {
430
+ // Public API property updated, emit 'modify' event.
431
+ publish(proxy, `${target.type}.change.modify`, { property: getPath(receiver, prop), newValue: val })
432
+ }
433
+ const check = prop => !(prop.startsWith('_') || target.publicExcludes.includes(prop))
434
+
435
+ // The path to each child property (each is a unique proxy)
436
+ const paths = new WeakMap()
437
+
438
+ const handler = {
439
+ set (obj, prop, val, receiver) {
440
+ // Recurse
441
+ if (typeof val === 'object' && val !== null && !paths.has(val) && check(prop)) {
442
+ val = new Proxy(val, handler)
443
+ paths.set(val, getPath(receiver, prop))
444
+ }
445
+
446
+ const was = prop in obj
447
+ // Set property or attribute
448
+ // Search prototype chain for the closest setter
449
+ let objProto = obj
450
+ while ((objProto = Object.getPrototypeOf(objProto))) {
451
+ const propDesc = Object.getOwnPropertyDescriptor(objProto, prop)
452
+ if (propDesc && propDesc.set) {
453
+ // Call setter, supplying proxy as this (fixes event bugs)
454
+ propDesc.set.call(receiver, val)
455
+ break
456
+ }
457
+ }
458
+ if (!objProto) {
459
+ // Couldn't find setter; set value on instance
460
+ obj[prop] = val
461
+ }
462
+ // Check if it already existed and if it's a valid property to watch, if
463
+ // on root object.
464
+ if (obj !== target || (was && check(prop))) {
465
+ callback(prop, val, receiver)
466
+ }
467
+ return true
468
+ }
469
+ }
470
+
471
+ const proxy = new Proxy(target, handler)
472
+ return proxy
473
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["es6", "DOM"],
4
+ "target": "es5",
5
+ "declaration": true
6
+ },
7
+ "include": ["./src/**/*"]
8
+ }
Binary file