diff --git a/.eslintignore b/.eslintignore index 72df373..e93e39b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,3 +18,7 @@ /.node_modules.ember-try/ /bower.json.ember-try /package.json.ember-try + +# don't bother linting types and type tests +types +type-tests diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fc4695d..a4a7be4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -76,3 +76,26 @@ jobs: run: yarn install --frozen-lockfile - name: Run Tests run: node_modules/.bin/ember try:one ${{ matrix.scenario }} --skip-cleanup + + types: + name: type tests + + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + ts-version: + - 4.4 + - 4.5 + - 4.6 + - next + + steps: + - uses: actions/checkout@v3 + - run: yarn install --frozen-lockfile --non-interactive + - run: yarn add -D typescript@${{ matrix.ts-version }} + - run: ./node_modules/.bin/tsc --project type-tests + - run: ./node_modules/.bin/tsc --project type-tests/with-registry + + continue-on-error: ${{ matrix.ts-version == 'next' }} diff --git a/.npmignore b/.npmignore index 773de27..cb73824 100644 --- a/.npmignore +++ b/.npmignore @@ -33,3 +33,6 @@ /.node_modules.ember-try/ /bower.json.ember-try /package.json.ember-try + +# non-published TypeScript infrastructure +type-tests diff --git a/README.md b/README.md index 4cd7e2a..5e6b7f0 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,68 @@ moduleForComponent('my-component', 'Integration | Component | my component', { Note: for Ember before 2.3.0, you'll need to use [ember-getowner-polyfill](https://github.com/rwjblue/ember-getowner-polyfill). +### TypeScript + +The library ships with full support for TypeScript usage with the service, helper, and test helpers. The API described above works as expected, with one additional nicety and one caveat. + +**Nicety:** the library provides you the ability to define statically your known feature flags by using a *registry* (as you may be familiar with from the registries for Ember's services, Ember Data models, etc.). If you define your keys (in kebab case!) in a registry like this: + +```ts +// types/index.d.ts, with other types defined for your app + + +declare module 'ember-feature-flags/services/features' { + export interface FeaturesRegistry { + 'feature-a': boolean; + 'feature-b': boolean; + } +} +``` + +Then in your app code, you will get type checking: TS will require you to use one of those keys (or a camel case variant of it), and reject unknown keys. + +```ts +import Component from '@glimmer/component'; +import { service } from '@ember/service'; +import type Features from 'ember-feature-flags/services/features'; + +export default class Example extends Component { + @service declare features: Features; + + get shouldDoSomething() { + return this.features.isEnabled('feature-a'); // ✅ + } + + get whoops() { + return this.features.isEnabled('not-a-real-feature'); // ❌ + } +} +``` + +This applies to all the values. If you do *not* add any keys to the `FeatureFlags` interface, the types will fall back to simply allowing any string and returning a boolean value. + +**Caveat:** The runtime uses Ember's `unknownProperty` proxy handling to allow direct access on the service itself with the `get` helper. This allows you to access the features directly in a template: + +```hbs +{{#if this.features.featureA}} + {{! ... }} +{{/if}} +``` + +For [Glint](https://github.com/typed-ember/glint), this is impossible to support in a way which would not *also* suggest that you could write `this.features.featureA` in your TypeScript code. Doing that will always return `undefined` until we are able to update the library to use native proxies instead of Ember's `unknownProperty()` method. The `feature-flag` helper does *not* have this restriction, so you should prefer that instead. If you still want to use the service directly instead of using the helper, you can use `get`: + +```hbs +{{#if (get this.features 'featureA')}} + {{! ... }} +{{/if}} +``` + +This will *not* provide autocomplete or type safety, but will work. + +#### Stability + +This library provides type definitions and follows the current draft of the [Semantic Versioning for TypeScript Types](https://www.semver-ts.org) specification. The public API is all published types. It currently supports TypeScript 4.4, 4.5, and 4.6. + ### Development #### Installation diff --git a/package.json b/package.json index e91120c..56e6b67 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ }, "devDependencies": { "@ember/optional-features": "^1.0.0", + "@types/ember__component": "^4.0.8", + "@types/ember__service": "^4.0.0", "babel-eslint": "^10.0.3", "broccoli-asset-rev": "^3.0.0", "ember-cli": "~3.13.1", @@ -38,9 +40,11 @@ "ember-try": "^1.2.1", "eslint-plugin-ember": "^7.1.0", "eslint-plugin-node": "^10.0.0", + "expect-type": "^0.13.0", "lerna-changelog": "^0.8.2", "loader.js": "^4.7.0", - "qunit-dom": "^0.9.0" + "qunit-dom": "^0.9.0", + "typescript": "^4.6.4" }, "keywords": [ "ember-addon", @@ -76,5 +80,12 @@ "documentation": ":memo: Documentation", "internal": ":house: Internal" } + }, + "typesVersions": { + "*": { + "*": [ + "types/*" + ] + } } } diff --git a/type-tests/index.ts b/type-tests/index.ts new file mode 100644 index 0000000..fb852e2 --- /dev/null +++ b/type-tests/index.ts @@ -0,0 +1,46 @@ +import { expectTypeOf } from "expect-type"; +import Helper from "@ember/component/helper"; +import type Features from "ember-feature-flags/services/features"; +import { + enableFeature, + disableFeature, +} from "ember-feature-flags/test-support"; +import FeatureFlag from "ember-feature-flags/helpers/feature-flag"; + +// side-effect import for `withFeature` +import "ember-feature-flags/test-support/helpers/with-feature"; + +expectTypeOf(enableFeature).toEqualTypeOf<(name: string) => void>(); +expectTypeOf(enableFeature("hello")).toEqualTypeOf(); +expectTypeOf(disableFeature).toEqualTypeOf<(name: string) => void>(); +expectTypeOf(disableFeature("hello")).toEqualTypeOf(); + +// globally available because of the side effect import +expectTypeOf(withFeature).toEqualTypeOf<(name: string) => void>(); +expectTypeOf(withFeature("hello")).toEqualTypeOf(); + +// The default for all the methods, if you don't add to the registry, is to +// simply use a string key lookup. See `./with-registry/index.ts` for testing +// when there *is* a registry. +declare let features: Features; +expectTypeOf(features.setup).toEqualTypeOf< + (config: Record) => void +>(); + +expectTypeOf(features.isEnabled).toEqualTypeOf<(key: string) => boolean>(); +expectTypeOf(features.get).toEqualTypeOf<(key: string) => boolean>(); +expectTypeOf(features.enable).toEqualTypeOf< + (key: string, value: boolean) => void +>(); +expectTypeOf(features.disable).toEqualTypeOf< + (key: string, value: boolean) => void +>(); +expectTypeOf(features.flags).toEqualTypeOf>(); + +expectTypeOf().toEqualTypeOf< + Helper<{ + Args: { + Positional: [string]; + }; + }> +>(); diff --git a/type-tests/tsconfig.json b/type-tests/tsconfig.json new file mode 100644 index 0000000..67d726a --- /dev/null +++ b/type-tests/tsconfig.json @@ -0,0 +1,73 @@ +// Pulled from ember-cli-typescript's blueprint +{ + "compilerOptions": { + "target": "ES2021", + "module": "ES2020", + "moduleResolution": "node", + + // Trying to check Ember apps and addons with `allowJs: true` is a recipe + // for many unresolveable type errors, because with *considerable* extra + // configuration it ends up including many files which are *not* valid and + // cannot be: they *appear* to be resolve-able to TS, but are in fact not in + // valid Node-resolveable locations and may not have TS-ready types. This + // will likely improve over time + "allowJs": false, + + // --- TS for SemVer Types compatibility + // Strictness settings -- you should *not* change these: Ember code is not + // guaranteed to type check with these set to looser values. + "strict": true, + "noUncheckedIndexedAccess": true, + + // Interop: these are viral and will require anyone downstream of your + // package to *also* set them to true. If you *must* enable them to consume + // an upstream package, you should document that for downstream consumers to + // be aware of. + // + // These *are* safe for apps to enable, since they do not *have* downstream + // consumers; but leaving them off is still preferred when possible, since + // it makes it easier to switch between apps and addons and have the same + // rules for what can be imported and how. + "allowSyntheticDefaultImports": false, + "esModuleInterop": false, + + // --- Lint-style rules + + // TypeScript also supplies some lint-style checks; nearly all of them are + // better handled by ESLint with the `@typescript-eslint`. This one is more + // like a safety check, though, so we leave it on. + "noPropertyAccessFromIndexSignature": true, + + // --- Compilation/integration settings + // Setting `noEmitOnError` here allows ember-cli-typescript to catch errors + // and inject them into Ember CLI's build error reporting, which provides + // nice feedback for when + "noEmitOnError": true, + + // We use Babel for emitting runtime code, because it's very important that + // we always and only use the same transpiler for non-stable features, in + // particular decorators. If you were to change this to `true`, it could + // lead to accidentally generating code with `tsc` instead of Babel, and + // could thereby result in broken code at runtime. + "noEmit": true, + + // Ember makes heavy use of decorators; TS does not support them at all + // without this flag. + "experimentalDecorators": true, + + // Support generation of source maps. Note: you must *also* enable source + // maps in your `ember-cli-babel` config and/or `babel.config.js`. + "declaration": true, + "declarationMap": true, + "inlineSourceMap": true, + "inlineSources": true, + + // --- custom paths shenanigans since we don't have `exports` yet --- + "baseUrl": "../types", + "paths": { + "ember-feature-flags": ["."], + "ember-feature-flags/*": ["./*"] + } + }, + "include": ["index.ts", "../types/*"] +} diff --git a/type-tests/with-registry/index.ts b/type-tests/with-registry/index.ts new file mode 100644 index 0000000..b43b054 --- /dev/null +++ b/type-tests/with-registry/index.ts @@ -0,0 +1,90 @@ +import { expectTypeOf } from "expect-type"; +import Features from "ember-feature-flags/services/features"; +import Helper from "@ember/component/helper"; +import { + enableFeature, + disableFeature, +} from "ember-feature-flags/test-support"; +import FeatureFlag from "ember-feature-flags/helpers/feature-flag"; + +// @ts-expect-error -- we have *not* done the side-effect import in this test, +// so there is no `withFeature` in the global scope! +withFeature("whatever", true); + +declare module "ember-feature-flags/services/features" { + export interface FeaturesRegistry { + simple: boolean; + "with-dash": boolean; + } +} + +expectTypeOf(enableFeature).parameters.toEqualTypeOf< + ["simple" | "withDash" | "with-dash"] +>(); +expectTypeOf(disableFeature).parameters.toEqualTypeOf< + ["simple" | "withDash" | "with-dash"] +>(); + +declare let features: Features; + +// @ts-expect-error -- no calling it without args at all +features.setup(); +// no keys is allowed (though: why would you do that?) +expectTypeOf(features.setup({})).toEqualTypeOf(); +// any key actually set up is allowed +expectTypeOf(features.setup({ "with-dash": true })).toEqualTypeOf(); +expectTypeOf(features.setup({ simple: false })).toEqualTypeOf(); +expectTypeOf( + features.setup({ + "with-dash": false, + simple: true, + }) +).toEqualTypeOf(); +// But random keys are *not* allowed +// @ts-expect-error +features.setup({ otherShenanigans: false }); + +// Both kebab and camel-case are supported +expectTypeOf(features.isEnabled).parameters.toEqualTypeOf< + ["withDash" | "with-dash" | "simple"] +>(); +// But random keys are disallowed if using the registry +// @ts-expect-error +features.isEnabled("whatever"); + +// Both kebab and camel-case are supported +expectTypeOf(features.get).parameters.toEqualTypeOf< + ["withDash" | "with-dash" | "simple"] +>(); +// But random keys are disallowed if using the registry +// @ts-expect-error +features.get("whatever"); + +expectTypeOf(features.enable) + .parameter(0) + .toEqualTypeOf<"withDash" | "with-dash" | "simple">(); +expectTypeOf(features.enable).parameter(1).toEqualTypeOf(); +// But random keys are disallowed if using the registry +// @ts-expect-error +features.enable("whatever", true); + +expectTypeOf(features.disable) + .parameter(0) + .toEqualTypeOf<"withDash" | "with-dash" | "simple">(); +expectTypeOf(features.disable).parameter(1).toEqualTypeOf(); +// But random keys are disallowed if using the registry +// @ts-expect-error +features.disable("whatever", true); + +expectTypeOf(features.flags).toEqualTypeOf< + Array<"simple" | "withDash" | "with-dash"> +>(); + +expectTypeOf().toEqualTypeOf< + Helper<{ + Args: { + Positional: ["simple" | "withDash" | "with-dash"]; + }; + }> +>(); +declare let ff: FeatureFlag; diff --git a/type-tests/with-registry/tsconfig.json b/type-tests/with-registry/tsconfig.json new file mode 100644 index 0000000..fd8251e --- /dev/null +++ b/type-tests/with-registry/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["./index.ts", "../../types/*"] +} diff --git a/types/helpers/feature-flag.d.ts b/types/helpers/feature-flag.d.ts new file mode 100644 index 0000000..df5312c --- /dev/null +++ b/types/helpers/feature-flag.d.ts @@ -0,0 +1,11 @@ +import Helper from "@ember/component/helper"; +import { Keys } from "ember-feature-flags/services/features"; + +interface FeatureFlagSignature { + Args: { + Positional: [Keys]; + }; +} + +/** A helper named `feature-flag` to check features in templates */ +export default class FeatureFlag extends Helper {} diff --git a/types/services/features.d.ts b/types/services/features.d.ts new file mode 100644 index 0000000..b7309c8 --- /dev/null +++ b/types/services/features.d.ts @@ -0,0 +1,83 @@ +import Service from "@ember/service"; + +/** + * If there are keys in the registry, require them to be the keys used in the + * config (though they can be left off and will default to false). + */ +type AllowedConfig = keyof FeaturesRegistry extends never + ? Record + : Partial>; + +/** + * Get a version of an object whose keys are camel-cased. + */ +type Camelized = { + [Key in CamelCased]: O[K]; +}; + +type Keys = keyof FeaturesRegistry extends never + ? string + : keyof FeaturesRegistry | keyof Camelized; + +type CamelCased = S extends string + ? S extends `${infer A}-${infer B}${infer C}` + ? `${Uncapitalize}${Capitalize}${CamelCased}` + : `${Uncapitalize}` + : never; + +/** + * A service named `features` available for injection into your routes, + * controllers, components, etc. + */ +// we are using an interface instead of a class since this is not intended to +// be subclassed and needs to change the behavior of `get` +export default interface Features extends Omit { + /** + * Enable or disable features in bulk. + * + * NOTE: `setup` methods reset previously setup flags and their state. + */ + setup(config: AllowedConfig): void; + + /** Enable a feature at runtime. */ + enable(key: K): void; + + /** Disable a feature at runtime. */ + disable(key: K): void; + + /** Check if a feature is enabled. */ + isEnabled(key: K): boolean; + + /** + * Features are also available as camel-cased properties of `features`, but + * behind a proxy, so you can only access them safely using `get`. + * + * ```ts + * import Controller from '@ember/controller'; + * import { service } from '@ember/service'; + * import type Features from 'ember-feature-flags/services/features'; + * + * export default class BillingPlansController extends Controller { + * @service declare features: Features; + * + * get plans() { + * if (this.features.get('newBillingPlans')) { + * // Return new plans + * } else { + * // Return old plans + * } + * } + * } + * ``` + */ + get(key: K): boolean; + + flags: Keys[]; +} + +/** + * Registry of settings. Provide the kebab-case (`"foo-bar"`) setting name here + * and both kebab– and camel-case will be available for type-powered + * autocomplete etc. + */ +export interface FeaturesRegistry {} diff --git a/types/test-support/helpers/with-feature.d.ts b/types/test-support/helpers/with-feature.d.ts new file mode 100644 index 0000000..8f54f5d --- /dev/null +++ b/types/test-support/helpers/with-feature.d.ts @@ -0,0 +1,15 @@ +declare global { + /** + * "Old"-style acceptance tests can utilize `withFeature` test helper to turn on + * a feature for the test. To use, import into your `test-helper.js`: `import + * 'ember-feature-flags/test-support/helpers/with-feature'` and add to your test + * `.jshintrc`, it will now be available in all of your tests. + * + * + */ + export function withFeature(name: string): void; +} + +// Shut off "exporting" mode so that the global enhancement works per the API +// as designed. +export {}; diff --git a/types/test-support/index.d.ts b/types/test-support/index.d.ts new file mode 100644 index 0000000..be04fd9 --- /dev/null +++ b/types/test-support/index.d.ts @@ -0,0 +1,19 @@ +import { Keys } from "ember-feature-flags/services/features"; + +/** + * Turns on or off a feature for the test in which it is called. Requires + * ember-cli-qunit >= 4.1.0 and the newer style of tests that use `setupTest`, + * `setupRenderingTest`, `setupApplicationTest`. + * + * @param name The feature to enable + */ +export function enableFeature(name: Keys): void; + +/** + * Turns on or off a feature for the test in which it is called. Requires + * ember-cli-qunit >= 4.1.0 and the newer style of tests that use `setupTest`, + * `setupRenderingTest`, `setupApplicationTest`. + * + * @param name The feature to disable + */ +export function disableFeature(name: Keys): void; diff --git a/yarn.lock b/yarn.lock index 41b0825..371b3a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -950,6 +950,161 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@types/ember-resolver@*": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@types/ember-resolver/-/ember-resolver-5.0.11.tgz#db931fb5c2d6bda4e29adea132fb48c7ed17aa62" + integrity sha512-2BL9d8kBdNUO9Je6KBF7Q34BSwbQG6vzCzTeSopt8FAmLDfaDU9xDDdyZobpfy9GR36mCSeG9b9wr4bgYh/MYw== + dependencies: + "@types/ember__object" "*" + +"@types/ember@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember/-/ember-4.0.0.tgz#0c29294fa0e5aa07ba6090f60243707dde8fc411" + integrity sha512-IR4o8OkFgoiRKVLRI8URvyNhEBSkjO5DXp2900/TptxOl0Retu8/tKtFaRTwkqteg2a0/6zXAA1rpFb3BbxNpA== + dependencies: + "@types/ember__application" "*" + "@types/ember__array" "*" + "@types/ember__component" "*" + "@types/ember__controller" "*" + "@types/ember__debug" "*" + "@types/ember__engine" "*" + "@types/ember__error" "*" + "@types/ember__object" "*" + "@types/ember__polyfills" "*" + "@types/ember__routing" "*" + "@types/ember__runloop" "*" + "@types/ember__service" "*" + "@types/ember__string" "*" + "@types/ember__template" "*" + "@types/ember__test" "*" + "@types/ember__utils" "*" + "@types/htmlbars-inline-precompile" "*" + "@types/rsvp" "*" + +"@types/ember__application@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__application/-/ember__application-4.0.0.tgz#a4d2fead37845550dad83bb1fd8afd52052563a7" + integrity sha512-1Atwevfyu1/vjiezPPdP4s96BxWGelEQlCJRU5ZQV9WlzVuMTuCDPumZ1lQdS4/EYycFZeod030FjE3CT9mZFA== + dependencies: + "@types/ember" "*" + "@types/ember-resolver" "*" + "@types/ember__application" "*" + "@types/ember__engine" "*" + "@types/ember__object" "*" + "@types/ember__routing" "*" + +"@types/ember__array@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/ember__array/-/ember__array-4.0.1.tgz#b62126ed080b29351a5633bd28e595a5cfee27ce" + integrity sha512-fwrYcYmFbsEnu77Xn9z3WSAp6tqpwn8Wksx8RzGg5pib6VmFD/dkT5jefwoKtlcImsxUNEoP1VgWKrdrpGaQcg== + dependencies: + "@types/ember" "*" + "@types/ember__array" "*" + "@types/ember__object" "*" + +"@types/ember__component@*", "@types/ember__component@^4.0.8": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@types/ember__component/-/ember__component-4.0.8.tgz#09a5f954f734fcbe6c988a173f4de4fa09084470" + integrity sha512-YVGn/kpWtpZAu6I2XtS9fsZV+78/sON5NyKzK5EOUyMiCwwpbUr5XL8dTSdkHehYrsfzJikcYvqpmwbNZSJxGQ== + dependencies: + "@types/ember" "*" + "@types/ember__component" "*" + "@types/ember__object" "*" + +"@types/ember__controller@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__controller/-/ember__controller-4.0.0.tgz#f8891840ebb84cb54eba82385e3b2dbe805d692a" + integrity sha512-rxJt8McWaaIZFsu2z+IB7TvgSjglAPb07Pj0F7OGvZQ3j9NV7kreqqics/cHQIEBG3GgVAewBE+xI5D6PNq/vg== + dependencies: + "@types/ember__object" "*" + +"@types/ember__debug@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/ember__debug/-/ember__debug-4.0.1.tgz#1e4a8a1045484295dddc7bd4356d0b3014b0d509" + integrity sha512-qrKk6Ujh6oev7TSB0eB7AEmQWKCt5t84k/K3hDvJXUiLU3YueN0kyt7aPoIAkVjC111A9FqDugl9n60+N5yeEw== + dependencies: + "@types/ember-resolver" "*" + "@types/ember__debug" "*" + "@types/ember__object" "*" + +"@types/ember__engine@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__engine/-/ember__engine-4.0.0.tgz#e39c06d98c7a085912508e8257c48a70196c1a87" + integrity sha512-AfJHIWaBeZ+TZWJbSoUz7LK+z8uNPjMqmucz8C5u+EV2NDiaq02oGPTB4SeKInLNBMga8c5xvz0gVefZJnTBnQ== + dependencies: + "@types/ember-resolver" "*" + "@types/ember__engine" "*" + "@types/ember__object" "*" + +"@types/ember__error@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__error/-/ember__error-4.0.0.tgz#c73037e65c1c3d7060b97f98135ba73c712972b1" + integrity sha512-1WVMR65/QTqPzMWafK2vKEwGafILxRxItbWJng6eEJyKDHRvvHFCl3XzJ4dQjdFcfOlozsn0mmEYCpjKoyzMqA== + +"@types/ember__object@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/ember__object/-/ember__object-4.0.2.tgz#070f1a36961f7df777e1fceed2551a648db0fd23" + integrity sha512-m3xjqjs7bGVT0+QXlgIoDMsp/oqePobnf4IiVoFdXLBpGCICiOAEi7HuUtCLi57WTvx0lYsS9hE1vgGyZn9qnw== + dependencies: + "@types/ember" "*" + "@types/ember__object" "*" + "@types/rsvp" "*" + +"@types/ember__polyfills@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__polyfills/-/ember__polyfills-4.0.0.tgz#d83ae94ff2890ad47798315426d9916f39ff4ae6" + integrity sha512-Yk85J18y1Ys6agoIBLdJWu6ZkWe68oaC9JPyW7BhOINVNKm89PXrR/yxdOJ1/vN1Hj7ZZQKq+4X6fz3sxebavA== + +"@types/ember__routing@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/ember__routing/-/ember__routing-4.0.5.tgz#f4e5c3d0e966859594d0303aa863053538802928" + integrity sha512-9f5wWoDn1snfjwtgX4uCvuvJSbG71vUzQcD1LDH6k5oWdhjk3iUsIalN4xD9jWzWF9JkasrTuOv+0wyNR4ahFw== + dependencies: + "@types/ember" "*" + "@types/ember__controller" "*" + "@types/ember__object" "*" + "@types/ember__routing" "*" + "@types/ember__service" "*" + +"@types/ember__runloop@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/ember__runloop/-/ember__runloop-4.0.1.tgz#7f6e45af7dbf1158655ef3ad852852b0bf87065f" + integrity sha512-3HrsavVrdgxUkYptQUv/e9RwJG02cV9WbnJxKSvwl9ZYpeX4JbuDVucjTWk5BAvJUVtbiQLPGzLEHZ6daoCbbg== + dependencies: + "@types/ember" "*" + "@types/ember__runloop" "*" + +"@types/ember__service@*", "@types/ember__service@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__service/-/ember__service-4.0.0.tgz#ae6164e3b5d927fe17513b49867b52dc0222490d" + integrity sha512-FbN2y6tRb6NIV+kmzQcxRAoB17vH7qHCfzcKlxsmt2EI7fboLTcdeKpZKPBEromRXg83fx67QX1b95WcwSGtaw== + dependencies: + "@types/ember__object" "*" + +"@types/ember__string@*": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@types/ember__string/-/ember__string-3.0.9.tgz#669188ccea5a61777a36bf88a05ba6875dc9b7d7" + integrity sha512-v9QwhhfTTgJH6PCviWlz3JgcraYdSWQoTg2XN5Z7bPgXMJYXczxB/N22L9FnuFgDYdN87yXdTJv6E9rw2YGEhw== + +"@types/ember__template@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__template/-/ember__template-4.0.0.tgz#3423b6ddc3a6cf0b13a1e0fd5f1a84eec664a095" + integrity sha512-51bAEQecMKpDYRXMmVVfU7excrtxDJixRU7huUsAm4acBCqL2+TmMgTqZEkOQSNy6qnKUc2ktSzX28a9//C6pA== + +"@types/ember__test@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__test/-/ember__test-4.0.0.tgz#1a7dcbe24fedfc34fa60547b03f130a14397c4b6" + integrity sha512-vI/qhZkexJLN25lp1UAfjJv4R6pPtrQlAmPDXkKd8PNjwRk3KANFVRzdghN7HWhXgQ+s91PbvxEnZ3eZiRPdcQ== + dependencies: + "@types/ember__application" "*" + +"@types/ember__utils@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ember__utils/-/ember__utils-4.0.0.tgz#a7e9e11334b5e203324e6155ff74c2b33ec21567" + integrity sha512-gwSFUm+6t6StkQxSllbn9lqRms/dXMCQDieRfaTGN8IRatnKjJoEPME3A0T6O9afsU6viBQyqlPyFxsOWknYkg== + dependencies: + "@types/ember" "*" + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -964,6 +1119,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/htmlbars-inline-precompile@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/htmlbars-inline-precompile/-/htmlbars-inline-precompile-3.0.0.tgz#4d3f19eeb2af9f4605620e13a566dae3952a4f68" + integrity sha512-n1YwM/Q937KmS9W4Ytran71nzhhcT2FDQI00eRGBNUyeErLZspBdDBewEe1F8tcRlUdsCVo2AZBLJsRjEceTRg== + "@types/minimatch@*", "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -974,6 +1134,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40" integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA== +"@types/rsvp@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/rsvp/-/rsvp-4.0.4.tgz#55e93e7054027f1ad4b4ebc1e60e59eb091e2d32" + integrity sha512-J3Ol++HCC7/hwZhanDvggFYU/GtxHxE/e7cGRWxR04BF7Tt3TqJZ84BkzQgDxmX0uu8IagiyfmfoUlBACh2Ilg== + "@types/symlink-or-copy@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.0.tgz#4151a81b4052c80bc2becbae09f3a9ec010a9c7a" @@ -4321,6 +4486,11 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" +expect-type@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-0.13.0.tgz#916646a7a73f3ee77039a634ee9035efe1876eb2" + integrity sha512-CclevazQfrqo8EvbLPmP7osnb1SZXkw47XPPvUUpeMz4HuGzDltE7CaIt3RLyT9UQrwVK/LDn+KVcC0hcgjgDg== + express@^4.10.7, express@^4.16.4: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -8580,6 +8750,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^4.6.4: + version "4.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" + integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"